Skip to content

Commit 3d1115d

Browse files
authored
refactor: rule configuration and error management (#1185)
* refactor: avoid running rule once configuration failed * refactor: remove deep-exit in lint/linter.go
1 parent 7cbd3d1 commit 3d1115d

37 files changed

+228
-408
lines changed

config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ func GetLintingRules(config *lint.Config, extraRules []lint.Rule) ([]lint.Rule,
149149
continue // skip disabled rules
150150
}
151151

152+
if r, ok := r.(lint.ConfigurableRule); ok {
153+
if err := r.Configure(ruleConfig.Arguments); err != nil {
154+
return nil, fmt.Errorf("cannot configure rule: %s", name)
155+
}
156+
}
157+
152158
lintingRules = append(lintingRules, r)
153159
}
154160

lint/linter.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import (
1010
"regexp"
1111
"strconv"
1212
"strings"
13-
"sync"
1413

1514
goversion "github.com/hashicorp/go-version"
1615
"golang.org/x/mod/modfile"
16+
"golang.org/x/sync/errgroup"
1717
)
1818

1919
// ReadFile defines an abstraction for reading files.
@@ -101,20 +101,23 @@ func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-cha
101101
perPkgVersions[n] = v
102102
}
103103

104-
var wg sync.WaitGroup
105-
wg.Add(len(packages))
104+
var wg errgroup.Group
106105
for n := range packages {
107-
go func(pkg []string, gover *goversion.Version) {
106+
wg.Go(func() error {
107+
pkg := packages[n]
108+
gover := perPkgVersions[n]
108109
if err := l.lintPackage(pkg, gover, ruleSet, config, failures); err != nil {
109-
fmt.Fprintln(os.Stderr, "error during linting: "+err.Error())
110-
os.Exit(1)
110+
return fmt.Errorf("error during linting: %w", err)
111111
}
112-
wg.Done()
113-
}(packages[n], perPkgVersions[n])
112+
return nil
113+
})
114114
}
115115

116116
go func() {
117-
wg.Wait()
117+
err := wg.Wait()
118+
if err != nil {
119+
failures <- NewInternalFailure(err.Error())
120+
}
118121
close(failures)
119122
}()
120123

lint/rule.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ type Rule interface {
1717
Apply(*File, Arguments) []Failure
1818
}
1919

20+
// ConfigurableRule defines an abstract configurable rule interface.
21+
type ConfigurableRule interface {
22+
Configure(Arguments) error
23+
}
24+
2025
// ToFailurePosition returns the failure position.
2126
func ToFailurePosition(start, end token.Pos, file *File) FailurePosition {
2227
return FailurePosition{

rule/add_constant.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"regexp"
88
"strconv"
99
"strings"
10-
"sync"
1110

1211
"github.com/mgechev/revive/lint"
1312
)
@@ -37,18 +36,10 @@ type AddConstantRule struct {
3736
allowList allowList
3837
ignoreFunctions []*regexp.Regexp
3938
strLitLimit int
40-
41-
configureOnce sync.Once
4239
}
4340

4441
// Apply applies the rule to given file.
45-
func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
46-
var configureErr error
47-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
48-
if configureErr != nil {
49-
return newInternalFailureError(configureErr)
50-
}
51-
42+
func (r *AddConstantRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
5243
var failures []lint.Failure
5344

5445
onFailure := func(failure lint.Failure) {
@@ -206,7 +197,10 @@ func (w *lintAddConstantRule) isStructTag(n *ast.BasicLit) bool {
206197
return ok
207198
}
208199

209-
func (r *AddConstantRule) configure(arguments lint.Arguments) error {
200+
// Configure validates the rule configuration, and configures the rule accordingly.
201+
//
202+
// Configuration implements the [lint.ConfigurableRule] interface.
203+
func (r *AddConstantRule) Configure(arguments lint.Arguments) error {
210204
r.strLitLimit = defaultStrLitLimit
211205
r.allowList = newAllowList()
212206
if len(arguments) == 0 {

rule/argument_limit.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import (
44
"errors"
55
"fmt"
66
"go/ast"
7-
"sync"
87

98
"github.com/mgechev/revive/lint"
109
)
1110

1211
// ArgumentsLimitRule lints the number of arguments a function can receive.
1312
type ArgumentsLimitRule struct {
1413
max int
15-
16-
configureOnce sync.Once
1714
}
1815

1916
const defaultArgumentsLimit = 8
2017

21-
func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) error {
18+
// Configure validates the rule configuration, and configures the rule accordingly.
19+
//
20+
// Configuration implements the [lint.ConfigurableRule] interface.
21+
func (r *ArgumentsLimitRule) Configure(arguments lint.Arguments) error {
2222
if len(arguments) < 1 {
2323
r.max = defaultArgumentsLimit
2424
return nil
@@ -33,14 +33,7 @@ func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) error {
3333
}
3434

3535
// Apply applies the rule to given file.
36-
func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
37-
var configureErr error
38-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
39-
40-
if configureErr != nil {
41-
return newInternalFailureError(configureErr)
42-
}
43-
36+
func (r *ArgumentsLimitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
4437
var failures []lint.Failure
4538

4639
for _, decl := range file.AST.Decls {

rule/banned_characters.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import (
44
"fmt"
55
"go/ast"
66
"strings"
7-
"sync"
87

98
"github.com/mgechev/revive/lint"
109
)
1110

1211
// BannedCharsRule checks if a file contains banned characters.
1312
type BannedCharsRule struct {
1413
bannedCharList []string
15-
16-
configureOnce sync.Once
1714
}
1815

1916
const bannedCharsRuleName = "banned-characters"
2017

21-
func (r *BannedCharsRule) configure(arguments lint.Arguments) error {
18+
// Configure validates the rule configuration, and configures the rule accordingly.
19+
//
20+
// Configuration implements the [lint.ConfigurableRule] interface.
21+
func (r *BannedCharsRule) Configure(arguments lint.Arguments) error {
2222
if len(arguments) > 0 {
2323
err := checkNumberOfArguments(1, arguments, bannedCharsRuleName)
2424
if err != nil {
@@ -35,14 +35,7 @@ func (r *BannedCharsRule) configure(arguments lint.Arguments) error {
3535
}
3636

3737
// Apply applied the rule to the given file.
38-
func (r *BannedCharsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
39-
var configureErr error
40-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
41-
42-
if configureErr != nil {
43-
return newInternalFailureError(configureErr)
44-
}
45-
38+
func (r *BannedCharsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
4639
var failures []lint.Failure
4740
onFailure := func(failure lint.Failure) {
4841
failures = append(failures, failure)

rule/cognitive_complexity.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"go/ast"
66
"go/token"
7-
"sync"
87

98
"github.com/mgechev/revive/lint"
109
"golang.org/x/tools/go/ast/astutil"
@@ -13,13 +12,14 @@ import (
1312
// CognitiveComplexityRule sets restriction for maximum cognitive complexity.
1413
type CognitiveComplexityRule struct {
1514
maxComplexity int
16-
17-
configureOnce sync.Once
1815
}
1916

2017
const defaultMaxCognitiveComplexity = 7
2118

22-
func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) error {
19+
// Configure validates the rule configuration, and configures the rule accordingly.
20+
//
21+
// Configuration implements the [lint.ConfigurableRule] interface.
22+
func (r *CognitiveComplexityRule) Configure(arguments lint.Arguments) error {
2323
if len(arguments) < 1 {
2424
r.maxComplexity = defaultMaxCognitiveComplexity
2525
return nil
@@ -35,14 +35,7 @@ func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) error {
3535
}
3636

3737
// Apply applies the rule to given file.
38-
func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
39-
var configureErr error
40-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
41-
42-
if configureErr != nil {
43-
return newInternalFailureError(configureErr)
44-
}
45-
38+
func (r *CognitiveComplexityRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
4639
var failures []lint.Failure
4740

4841
linter := cognitiveComplexityLinter{

rule/comment_spacings.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package rule
33
import (
44
"fmt"
55
"strings"
6-
"sync"
76

87
"github.com/mgechev/revive/lint"
98
)
@@ -12,11 +11,12 @@ import (
1211
// the comment symbol( // ) and the start of the comment text
1312
type CommentSpacingsRule struct {
1413
allowList []string
15-
16-
configureOnce sync.Once
1714
}
1815

19-
func (r *CommentSpacingsRule) configure(arguments lint.Arguments) error {
16+
// Configure validates the rule configuration, and configures the rule accordingly.
17+
//
18+
// Configuration implements the [lint.ConfigurableRule] interface.
19+
func (r *CommentSpacingsRule) Configure(arguments lint.Arguments) error {
2020
r.allowList = []string{}
2121
for _, arg := range arguments {
2222
allow, ok := arg.(string) // Alt. non panicking version
@@ -29,14 +29,7 @@ func (r *CommentSpacingsRule) configure(arguments lint.Arguments) error {
2929
}
3030

3131
// Apply the rule.
32-
func (r *CommentSpacingsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
33-
var configureErr error
34-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
35-
36-
if configureErr != nil {
37-
return newInternalFailureError(configureErr)
38-
}
39-
32+
func (r *CommentSpacingsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
4033
var failures []lint.Failure
4134

4235
for _, cg := range file.AST.Comments {

rule/comments_density.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import (
44
"fmt"
55
"go/ast"
66
"strings"
7-
"sync"
87

98
"github.com/mgechev/revive/lint"
109
)
1110

1211
// CommentsDensityRule enforces a minimum comment / code relation.
1312
type CommentsDensityRule struct {
1413
minimumCommentsDensity int64
15-
16-
configureOnce sync.Once
1714
}
1815

1916
const defaultMinimumCommentsPercentage = 0
2017

21-
func (r *CommentsDensityRule) configure(arguments lint.Arguments) error {
18+
// Configure validates the rule configuration, and configures the rule accordingly.
19+
//
20+
// Configuration implements the [lint.ConfigurableRule] interface.
21+
func (r *CommentsDensityRule) Configure(arguments lint.Arguments) error {
2222
if len(arguments) < 1 {
2323
r.minimumCommentsDensity = defaultMinimumCommentsPercentage
2424
return nil
@@ -33,14 +33,7 @@ func (r *CommentsDensityRule) configure(arguments lint.Arguments) error {
3333
}
3434

3535
// Apply applies the rule to given file.
36-
func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
37-
var configureErr error
38-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
39-
40-
if configureErr != nil {
41-
return newInternalFailureError(configureErr)
42-
}
43-
36+
func (r *CommentsDensityRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
4437
commentsLines := countDocLines(file.AST.Comments)
4538
statementsCount := countStatements(file.AST)
4639
density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100

rule/context_as_argument.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,17 @@ import (
44
"fmt"
55
"go/ast"
66
"strings"
7-
"sync"
87

98
"github.com/mgechev/revive/lint"
109
)
1110

1211
// ContextAsArgumentRule suggests that `context.Context` should be the first argument of a function.
1312
type ContextAsArgumentRule struct {
1413
allowTypes map[string]struct{}
15-
16-
configureOnce sync.Once
1714
}
1815

1916
// Apply applies the rule to given file.
20-
func (r *ContextAsArgumentRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
21-
var configureErr error
22-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
23-
24-
if configureErr != nil {
25-
return newInternalFailureError(configureErr)
26-
}
27-
17+
func (r *ContextAsArgumentRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
2818
var failures []lint.Failure
2919
for _, decl := range file.AST.Decls {
3020
fn, ok := decl.(*ast.FuncDecl)
@@ -64,7 +54,10 @@ func (*ContextAsArgumentRule) Name() string {
6454
return "context-as-argument"
6555
}
6656

67-
func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) error {
57+
// Configure validates the rule configuration, and configures the rule accordingly.
58+
//
59+
// Configuration implements the [lint.ConfigurableRule] interface.
60+
func (r *ContextAsArgumentRule) Configure(arguments lint.Arguments) error {
6861
types, err := r.getAllowTypesFromArguments(arguments)
6962
if err != nil {
7063
return err

rule/cyclomatic.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"go/ast"
66
"go/token"
7-
"sync"
87

98
"github.com/mgechev/revive/lint"
109
)
@@ -14,13 +13,14 @@ import (
1413
// CyclomaticRule sets restriction for maximum cyclomatic complexity.
1514
type CyclomaticRule struct {
1615
maxComplexity int
17-
18-
configureOnce sync.Once
1916
}
2017

2118
const defaultMaxCyclomaticComplexity = 10
2219

23-
func (r *CyclomaticRule) configure(arguments lint.Arguments) error {
20+
// Configure validates the rule configuration, and configures the rule accordingly.
21+
//
22+
// Configuration implements the [lint.ConfigurableRule] interface.
23+
func (r *CyclomaticRule) Configure(arguments lint.Arguments) error {
2424
if len(arguments) < 1 {
2525
r.maxComplexity = defaultMaxCyclomaticComplexity
2626
return nil
@@ -35,14 +35,7 @@ func (r *CyclomaticRule) configure(arguments lint.Arguments) error {
3535
}
3636

3737
// Apply applies the rule to given file.
38-
func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
39-
var configureErr error
40-
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
41-
42-
if configureErr != nil {
43-
return newInternalFailureError(configureErr)
44-
}
45-
38+
func (r *CyclomaticRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
4639
var failures []lint.Failure
4740
for _, decl := range file.AST.Decls {
4841
fn, ok := decl.(*ast.FuncDecl)

0 commit comments

Comments
 (0)