Skip to content

Commit 0b91a42

Browse files
committed
Fix #331: fix errcheck "ignore" config directive.
Make tests for "ignore" and "exclude" directives. Mark all hidden command-line options as deprecated.
1 parent 21c2590 commit 0b91a42

35 files changed

+183
-12476
lines changed

.golangci.example.yml

+9
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ linters-settings:
6969
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
7070
# default is false: such cases aren't reported by default.
7171
check-blank: false
72+
73+
# [deprecated] comma-separated list of pairs of the form pkg:regex
74+
# the regex is used to ignore names within pkg. (default "fmt:.*").
75+
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
76+
ignore: fmt,io/ioutil:^ReadF.*
77+
78+
# path to a file containing a list of functions to exclude from checking
79+
# see https://github.com/kisielk/errcheck#excluding-functions for details
80+
exclude: /path/to/file.txt
7281
govet:
7382
# report about shadowed variables
7483
check-shadowing: true

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,15 @@ linters-settings:
585585
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
586586
# default is false: such cases aren't reported by default.
587587
check-blank: false
588+
589+
# [deprecated] comma-separated list of pairs of the form pkg:regex
590+
# the regex is used to ignore names within pkg. (default "fmt:.*").
591+
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
592+
ignore: fmt,io/ioutil:^ReadF.*
593+
594+
# path to a file containing a list of functions to exclude from checking
595+
# see https://github.com/kisielk/errcheck#excluding-functions for details
596+
exclude: /path/to/file.txt
588597
govet:
589598
# report about shadowed variables
590599
check-shadowing: true

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ require (
1919
github.com/golang/mock v1.1.1
2020
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2
2121
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a
22-
github.com/golangci/errcheck v0.0.0-20181003203344-1765131d5be5
22+
github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6
2323
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613
2424
github.com/golangci/go-tools v0.0.0-20180902103155-93eecd106a0b
2525
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5
4545
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
4646
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
4747
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
48-
github.com/golangci/errcheck v0.0.0-20181003203344-1765131d5be5 h1:q7aCFiVt6gMxZseillOwgsyitdcxbqQz5oxA2rHIeMY=
49-
github.com/golangci/errcheck v0.0.0-20181003203344-1765131d5be5/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
48+
github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6 h1:i2jIkQFb8RG45DuQs+ElyROY848cSJIoIkBM+7XXypA=
49+
github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
5050
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:WadunOE/TeHR8U7f0TXiJACHeU3cuFOXuKafw4rozqU=
5151
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
5252
github.com/golangci/go-tools v0.0.0-20180902103155-93eecd106a0b h1:FSrt9JBK7JINu5UobyIF6epfpjL66H+67KZoTbE0zwk=

pkg/commands/run.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,19 @@ func wh(text string) string {
4141
return color.GreenString(text)
4242
}
4343

44-
func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager) {
44+
func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager, isFinalInit bool) {
4545
hideFlag := func(name string) {
4646
if err := fs.MarkHidden(name); err != nil {
4747
panic(err)
4848
}
49+
50+
// we run initFlagSet multiple times, but we wouldn't like to see deprecation message multiple times
51+
if isFinalInit {
52+
const deprecateMessage = "flag will be removed soon, please, use .golangci.yml config"
53+
if err := fs.MarkDeprecated(name, deprecateMessage); err != nil {
54+
panic(err)
55+
}
56+
}
4957
}
5058

5159
// Output config
@@ -85,9 +93,11 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager) {
8593
fs.BoolVar(&lsc.Errcheck.CheckAssignToBlank, "errcheck.check-blank", false,
8694
"Errcheck: check for errors assigned to blank identifier: _ = errFunc()")
8795
hideFlag("errcheck.check-blank")
88-
fs.StringVar(&lsc.Errcheck.Exclude, "errcheck.exclude", "", "errcheck.exclude")
96+
fs.StringVar(&lsc.Errcheck.Exclude, "errcheck.exclude", "",
97+
"Path to a file containing a list of functions to exclude from checking")
8998
hideFlag("errcheck.exclude")
90-
fs.Var(&lsc.Errcheck.Ignore, "errcheck.ignore", "errcheck.ignore")
99+
fs.StringVar(&lsc.Errcheck.Ignore, "errcheck.ignore", "fmt:.*",
100+
`Comma-separated list of pairs of the form pkg:regex. The regex is used to ignore names within pkg`)
91101
hideFlag("errcheck.ignore")
92102

93103
fs.BoolVar(&lsc.Govet.CheckShadowing, "govet.check-shadowing", false,
@@ -171,7 +181,7 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager) {
171181
func (e *Executor) initRunConfiguration(cmd *cobra.Command) {
172182
fs := cmd.Flags()
173183
fs.SortFlags = false // sort them as they are defined here
174-
initFlagSet(fs, e.cfg, e.DBManager)
184+
initFlagSet(fs, e.cfg, e.DBManager, true)
175185
}
176186

177187
func (e Executor) getConfigForCommandLine() (*config.Config, error) {
@@ -184,7 +194,7 @@ func (e Executor) getConfigForCommandLine() (*config.Config, error) {
184194
// `changed` variable inside string slice vars will be shared.
185195
// Use another config variable here, not e.cfg, to not
186196
// affect main parsing by this parsing of only config option.
187-
initFlagSet(fs, &cfg, e.DBManager)
197+
initFlagSet(fs, &cfg, e.DBManager, false)
188198

189199
// Parse max options, even force version option: don't want
190200
// to get access to Executor here: it's error-prone to use

pkg/config/config.go

+4-53
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package config
22

33
import (
44
"errors"
5-
"fmt"
6-
"regexp"
75
"strings"
86
"time"
97

@@ -174,10 +172,10 @@ type LintersSettings struct {
174172
}
175173

176174
type ErrcheckSettings struct {
177-
CheckTypeAssertions bool `mapstructure:"check-type-assertions"`
178-
CheckAssignToBlank bool `mapstructure:"check-blank"`
179-
Ignore IgnoreFlag `mapstructure:"ignore"`
180-
Exclude string `mapstructure:"exclude"`
175+
CheckTypeAssertions bool `mapstructure:"check-type-assertions"`
176+
CheckAssignToBlank bool `mapstructure:"check-blank"`
177+
Ignore string `mapstructure:"ignore"`
178+
Exclude string `mapstructure:"exclude"`
181179
}
182180

183181
type LllSettings struct {
@@ -295,9 +293,6 @@ var defaultLintersSettings = LintersSettings{
295293
RangeLoops: true,
296294
ForLoops: false,
297295
},
298-
Errcheck: ErrcheckSettings{
299-
Ignore: IgnoreFlag{},
300-
},
301296
Gocritic: GocriticSettings{
302297
SettingsPerCheck: map[string]GocriticCheckSettings{},
303298
},
@@ -347,47 +342,3 @@ func NewDefault() *Config {
347342
LintersSettings: defaultLintersSettings,
348343
}
349344
}
350-
351-
// IgnoreFlags was taken from errcheck in order to keep the API identical.
352-
// https://github.com/kisielk/errcheck/blob/1787c4bee836470bf45018cfbc783650db3c6501/main.go#L25-L60
353-
type IgnoreFlag map[string]*regexp.Regexp
354-
355-
func (f IgnoreFlag) String() string {
356-
pairs := make([]string, 0, len(f))
357-
for pkg, re := range f {
358-
prefix := ""
359-
if pkg != "" {
360-
prefix = pkg + ":"
361-
}
362-
pairs = append(pairs, prefix+re.String())
363-
}
364-
return fmt.Sprintf("%q", strings.Join(pairs, ","))
365-
}
366-
367-
func (f IgnoreFlag) Set(s string) error {
368-
if s == "" {
369-
return nil
370-
}
371-
for _, pair := range strings.Split(s, ",") {
372-
colonIndex := strings.Index(pair, ":")
373-
var pkg, re string
374-
if colonIndex == -1 {
375-
pkg = ""
376-
re = pair
377-
} else {
378-
pkg = pair[:colonIndex]
379-
re = pair[colonIndex+1:]
380-
}
381-
regex, err := regexp.Compile(re)
382-
if err != nil {
383-
return err
384-
}
385-
f[pkg] = regex
386-
}
387-
return nil
388-
}
389-
390-
// Type returns the type of the flag follow the pflag format.
391-
func (IgnoreFlag) Type() string {
392-
return "stringToRegexp"
393-
}

pkg/golinters/errcheck.go

+39-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"context"
66
"fmt"
77
"os"
8+
"regexp"
9+
"strings"
810

911
errcheckAPI "github.com/golangci/errcheck/golangci"
1012
"github.com/pkg/errors"
@@ -57,19 +59,55 @@ func (e Errcheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Is
5759
return res, nil
5860
}
5961

62+
// parseIgnoreConfig was taken from errcheck in order to keep the API identical.
63+
// https://github.com/kisielk/errcheck/blob/1787c4bee836470bf45018cfbc783650db3c6501/main.go#L25-L60
64+
func parseIgnoreConfig(s string) (map[string]*regexp.Regexp, error) {
65+
if s == "" {
66+
return nil, nil
67+
}
68+
69+
cfg := map[string]*regexp.Regexp{}
70+
71+
for _, pair := range strings.Split(s, ",") {
72+
colonIndex := strings.Index(pair, ":")
73+
var pkg, re string
74+
if colonIndex == -1 {
75+
pkg = ""
76+
re = pair
77+
} else {
78+
pkg = pair[:colonIndex]
79+
re = pair[colonIndex+1:]
80+
}
81+
regex, err := regexp.Compile(re)
82+
if err != nil {
83+
return nil, err
84+
}
85+
cfg[pkg] = regex
86+
}
87+
88+
return cfg, nil
89+
}
90+
6091
func genConfig(errCfg *config.ErrcheckSettings) (*errcheckAPI.Config, error) {
92+
ignoreConfig, err := parseIgnoreConfig(errCfg.Ignore)
93+
if err != nil {
94+
return nil, errors.Wrap(err, "failed to parse 'ignore' directive")
95+
}
96+
6197
c := &errcheckAPI.Config{
62-
Ignore: errCfg.Ignore,
98+
Ignore: ignoreConfig,
6399
Blank: errCfg.CheckAssignToBlank,
64100
Asserts: errCfg.CheckTypeAssertions,
65101
}
102+
66103
if errCfg.Exclude != "" {
67104
exclude, err := readExcludeFile(errCfg.Exclude)
68105
if err != nil {
69106
return nil, err
70107
}
71108
c.Exclude = exclude
72109
}
110+
73111
return c, nil
74112
}
75113

test/linters_test.go

+17-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finis
8585

8686
return cfgPath, func() {
8787
assert.NoError(t, f.Close())
88-
assert.NoError(t, os.Remove(cfgPath))
88+
if os.Getenv("GL_KEEP_TEMP_FILES") != "1" {
89+
assert.NoError(t, os.Remove(cfgPath))
90+
}
8991
}
9092
}
9193

@@ -106,6 +108,8 @@ func testOneSource(t *testing.T, sourcePath string) {
106108
p, finish := saveConfig(t, rc.config)
107109
defer finish()
108110
cfgPath = p
111+
} else if rc.configPath != "" {
112+
cfgPath = rc.configPath
109113
}
110114

111115
for _, addArg := range []string{"", "-Etypecheck"} {
@@ -129,8 +133,9 @@ func testOneSource(t *testing.T, sourcePath string) {
129133
}
130134

131135
type runContext struct {
132-
args []string
133-
config map[string]interface{}
136+
args []string
137+
config map[string]interface{}
138+
configPath string
134139
}
135140

136141
func buildConfigFromShortRepr(t *testing.T, repr string, config map[string]interface{}) {
@@ -188,6 +193,15 @@ func extractRunContextFromComments(t *testing.T, sourcePath string) *runContext
188193
buildConfigFromShortRepr(t, repr, rc.config)
189194
continue
190195
}
196+
197+
if strings.HasPrefix(line, "config_path: ") {
198+
configPath := strings.TrimPrefix(line, "config_path: ")
199+
assert.NotEmpty(t, configPath)
200+
rc.configPath = configPath
201+
continue
202+
}
203+
204+
assert.Fail(t, "invalid prefix of comment line %s", line)
191205
}
192206

193207
return rc

test/testdata/depguard.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//args: -Edepguard --depguard.include-go-root --depguard.packages='compress/*,log'
1+
//args: -Edepguard
2+
//config: linters-settings.depguard.include-go-root=true
3+
//config: linters-settings.depguard.packages=compress/*,log
24
package testdata
35

46
import (

test/testdata/dupl.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
//args: -Edupl --dupl.threshold=20
1+
//args: -Edupl
2+
//config: linters-settings.dupl.threshold=20
23
package testdata
34

45
type DuplLogger struct{}
@@ -10,7 +11,7 @@ func (DuplLogger) level() int {
1011
func (DuplLogger) Debug(args ...interface{}) {}
1112
func (DuplLogger) Info(args ...interface{}) {}
1213

13-
func (logger *DuplLogger) First(args ...interface{}) { // ERROR "13-22 lines are duplicate of `testdata/dupl.go:24-33`"
14+
func (logger *DuplLogger) First(args ...interface{}) { // ERROR "14-23 lines are duplicate of `testdata/dupl.go:25-34`"
1415
if logger.level() >= 0 {
1516
logger.Debug(args...)
1617
logger.Debug(args...)
@@ -21,7 +22,7 @@ func (logger *DuplLogger) First(args ...interface{}) { // ERROR "13-22 lines are
2122
}
2223
}
2324

24-
func (logger *DuplLogger) Second(args ...interface{}) { // ERROR "24-33 lines are duplicate of `testdata/dupl.go:13-22`"
25+
func (logger *DuplLogger) Second(args ...interface{}) { // ERROR "25-34 lines are duplicate of `testdata/dupl.go:14-23`"
2526
if logger.level() >= 1 {
2627
logger.Info(args...)
2728
logger.Info(args...)

test/testdata/errcheck/exclude.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io/ioutil.ReadFile
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
linters-settings:
2+
errcheck:
3+
check-blank: true
4+
ignore: os:.*,io/ioutil:^ReadF.*

test/testdata/errcheck_exclude.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//args: -Eerrcheck
2+
//config: linters-settings.errcheck.check-blank=true
3+
//config: linters-settings.errcheck.exclude=testdata/errcheck/exclude.txt
4+
package testdata
5+
6+
import (
7+
"io/ioutil"
8+
)
9+
10+
func TestErrcheckExclude() []byte {
11+
ret, _ := ioutil.ReadFile("f.txt")
12+
return ret
13+
}
14+
15+
func TestErrcheckNoExclude() []byte {
16+
ret, _ := ioutil.ReadAll(nil) // ERROR "Error return value of `ioutil.ReadAll` is not checked"
17+
return ret
18+
}

test/testdata/errcheck_ignore.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//args: -Eerrcheck
2+
//config_path: testdata/errcheck/ignore_config.yml
3+
package testdata
4+
5+
import (
6+
"fmt"
7+
"io/ioutil"
8+
"os"
9+
)
10+
11+
func TestErrcheckIgnoreOs() {
12+
_, _ = os.Open("f.txt")
13+
}
14+
15+
func TestErrcheckNoIgnoreFmt(s string) int {
16+
n, _ := fmt.Println(s) // ERROR "Error return value of `fmt.Println` is not checked"
17+
return n
18+
}
19+
20+
func TestErrcheckIgnoreIoutil() []byte {
21+
ret, _ := ioutil.ReadFile("f.txt")
22+
return ret
23+
}
24+
25+
func TestErrcheckNoIgnoreIoutil() []byte {
26+
ret, _ := ioutil.ReadAll(nil) // ERROR "Error return value of `ioutil.ReadAll` is not checked"
27+
return ret
28+
}

0 commit comments

Comments
 (0)