@@ -12,20 +12,24 @@ import (
12
12
)
13
13
14
14
// StringFormatRule lints strings and/or comments according to a set of regular expressions given as Arguments
15
- type StringFormatRule struct {}
15
+ type StringFormatRule struct {
16
+ rules []stringFormatSubrule
17
+ }
16
18
17
19
// Apply applies the rule to the given file.
18
- func (* StringFormatRule ) Apply (file * lint.File , arguments lint.Arguments ) []lint.Failure {
20
+ func (r * StringFormatRule ) Apply (file * lint.File , _ lint.Arguments ) []lint.Failure {
19
21
var failures []lint.Failure
20
22
21
23
onFailure := func (failure lint.Failure ) {
22
24
failures = append (failures , failure )
23
25
}
24
26
25
- w := & lintStringFormatRule {onFailure : onFailure }
26
- err := w .parseArguments (arguments )
27
- if err != nil {
28
- return newInternalFailureError (err )
27
+ for i := range r .rules {
28
+ r .rules [i ].onFailure = onFailure
29
+ }
30
+
31
+ w := & lintStringFormatRule {
32
+ rules : r .rules ,
29
33
}
30
34
31
35
ast .Walk (w , file .AST )
@@ -38,32 +42,31 @@ func (*StringFormatRule) Name() string {
38
42
return "string-format"
39
43
}
40
44
41
- // ParseArgumentsTest is a public wrapper around w.parseArguments used for testing. Returns the error message provided to panic, or nil if no error was encountered
42
- func ( * StringFormatRule ) ParseArgumentsTest ( arguments lint. Arguments ) * string {
43
- w := lintStringFormatRule {}
44
- c := make ( chan any )
45
- // Parse the arguments in a goroutine, defer a recover() call, return the error encountered (or nil if there was no error)
46
- go func () {
47
- defer func () {
48
- err := w . parseArguments ( arguments )
49
- c <- err
50
- }()
51
- }()
52
- err := <- c
53
- if err != nil {
54
- e := fmt . Sprintf ( "%s" , err )
55
- return & e
45
+ // Configure validates the rule configuration, and configures the rule accordingly.
46
+ //
47
+ // Configuration implements the [lint.ConfigurableRule] interface.
48
+ func ( r * StringFormatRule ) Configure ( arguments lint. Arguments ) error {
49
+ for i , argument := range arguments {
50
+ scopes , regex , negated , errorMessage , err := r . parseArgument ( argument , i )
51
+ if err != nil {
52
+ return err
53
+ }
54
+ r . rules = append ( r . rules , stringFormatSubrule {
55
+ scopes : scopes ,
56
+ regexp : regex ,
57
+ negated : negated ,
58
+ errorMessage : errorMessage ,
59
+ })
56
60
}
57
61
return nil
58
62
}
59
63
60
64
type lintStringFormatRule struct {
61
- onFailure func (lint.Failure )
62
- rules []stringFormatSubrule
65
+ rules []stringFormatSubrule
63
66
}
64
67
65
68
type stringFormatSubrule struct {
66
- parent * lintStringFormatRule
69
+ onFailure func (lint. Failure )
67
70
scopes stringFormatSubruleScopes
68
71
regexp * regexp.Regexp
69
72
negated bool
@@ -84,45 +87,28 @@ const identRegex = "[_A-Za-z][_A-Za-z0-9]*"
84
87
var parseStringFormatScope = regexp .MustCompile (
85
88
fmt .Sprintf ("^(%s(?:\\ .%s)?)(?:\\ [([0-9]+)\\ ](?:\\ .(%s))?)?$" , identRegex , identRegex , identRegex ))
86
89
87
- func (w * lintStringFormatRule ) parseArguments (arguments lint.Arguments ) error {
88
- for i , argument := range arguments {
89
- scopes , regex , negated , errorMessage , err := w .parseArgument (argument , i )
90
- if err != nil {
91
- return err
92
- }
93
- w .rules = append (w .rules , stringFormatSubrule {
94
- parent : w ,
95
- scopes : scopes ,
96
- regexp : regex ,
97
- negated : negated ,
98
- errorMessage : errorMessage ,
99
- })
100
- }
101
- return nil
102
- }
103
-
104
- func (w * lintStringFormatRule ) parseArgument (argument any , ruleNum int ) (scopes stringFormatSubruleScopes , regex * regexp.Regexp , negated bool , errorMessage string , err error ) {
90
+ func (r * StringFormatRule ) parseArgument (argument any , ruleNum int ) (scopes stringFormatSubruleScopes , regex * regexp.Regexp , negated bool , errorMessage string , err error ) {
105
91
g , ok := argument .([]any ) // Cast to generic slice first
106
92
if ! ok {
107
- return stringFormatSubruleScopes {}, regex , false , "" , w .configError ("argument is not a slice" , ruleNum , 0 )
93
+ return stringFormatSubruleScopes {}, regex , false , "" , r .configError ("argument is not a slice" , ruleNum , 0 )
108
94
}
109
95
if len (g ) < 2 {
110
- return stringFormatSubruleScopes {}, regex , false , "" , w .configError ("less than two slices found in argument, scope and regex are required" , ruleNum , len (g )- 1 )
96
+ return stringFormatSubruleScopes {}, regex , false , "" , r .configError ("less than two slices found in argument, scope and regex are required" , ruleNum , len (g )- 1 )
111
97
}
112
98
rule := make ([]string , len (g ))
113
99
for i , obj := range g {
114
100
val , ok := obj .(string )
115
101
if ! ok {
116
- return stringFormatSubruleScopes {}, regex , false , "" , w .configError ("unexpected value, string was expected" , ruleNum , i )
102
+ return stringFormatSubruleScopes {}, regex , false , "" , r .configError ("unexpected value, string was expected" , ruleNum , i )
117
103
}
118
104
rule [i ] = val
119
105
}
120
106
121
107
// Validate scope and regex length
122
108
if rule [0 ] == "" {
123
- return stringFormatSubruleScopes {}, regex , false , "" , w .configError ("empty scope provided" , ruleNum , 0 )
109
+ return stringFormatSubruleScopes {}, regex , false , "" , r .configError ("empty scope provided" , ruleNum , 0 )
124
110
} else if len (rule [1 ]) < 2 {
125
- return stringFormatSubruleScopes {}, regex , false , "" , w .configError ("regex is too small (regexes should begin and end with '/')" , ruleNum , 1 )
111
+ return stringFormatSubruleScopes {}, regex , false , "" , r .configError ("regex is too small (regexes should begin and end with '/')" , ruleNum , 1 )
126
112
}
127
113
128
114
// Parse rule scopes
@@ -133,25 +119,25 @@ func (w *lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes
133
119
rawScope = strings .TrimSpace (rawScope )
134
120
135
121
if len (rawScope ) == 0 {
136
- return stringFormatSubruleScopes {}, regex , false , "" , w .parseScopeError ("empty scope in rule scopes:" , ruleNum , 0 , scopeNum )
122
+ return stringFormatSubruleScopes {}, regex , false , "" , r .parseScopeError ("empty scope in rule scopes:" , ruleNum , 0 , scopeNum )
137
123
}
138
124
139
125
scope := stringFormatSubruleScope {}
140
126
matches := parseStringFormatScope .FindStringSubmatch (rawScope )
141
127
if matches == nil {
142
128
// The rule's scope didn't match the parsing regex at all, probably a configuration error
143
- return stringFormatSubruleScopes {}, regex , false , "" , w .parseScopeError ("unable to parse rule scope" , ruleNum , 0 , scopeNum )
129
+ return stringFormatSubruleScopes {}, regex , false , "" , r .parseScopeError ("unable to parse rule scope" , ruleNum , 0 , scopeNum )
144
130
} else if len (matches ) != 4 {
145
131
// The rule's scope matched the parsing regex, but an unexpected number of submatches was returned, probably a bug
146
132
return stringFormatSubruleScopes {}, regex , false , "" ,
147
- w .parseScopeError (fmt .Sprintf ("unexpected number of submatches when parsing scope: %d, expected 4" , len (matches )), ruleNum , 0 , scopeNum )
133
+ r .parseScopeError (fmt .Sprintf ("unexpected number of submatches when parsing scope: %d, expected 4" , len (matches )), ruleNum , 0 , scopeNum )
148
134
}
149
135
scope .funcName = matches [1 ]
150
136
if len (matches [2 ]) > 0 {
151
137
var err error
152
138
scope .argument , err = strconv .Atoi (matches [2 ])
153
139
if err != nil {
154
- return stringFormatSubruleScopes {}, regex , false , "" , w .parseScopeError ("unable to parse argument number in rule scope" , ruleNum , 0 , scopeNum )
140
+ return stringFormatSubruleScopes {}, regex , false , "" , r .parseScopeError ("unable to parse argument number in rule scope" , ruleNum , 0 , scopeNum )
155
141
}
156
142
}
157
143
if len (matches [3 ]) > 0 {
@@ -169,7 +155,7 @@ func (w *lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes
169
155
}
170
156
regex , errr := regexp .Compile (rule [1 ][offset : len (rule [1 ])- 1 ])
171
157
if errr != nil {
172
- return stringFormatSubruleScopes {}, regex , false , "" , w .parseError (fmt .Sprintf ("unable to compile %s as regexp" , rule [1 ]), ruleNum , 1 )
158
+ return stringFormatSubruleScopes {}, regex , false , "" , r .parseError (fmt .Sprintf ("unable to compile %s as regexp" , rule [1 ]), ruleNum , 1 )
173
159
}
174
160
175
161
// Use custom error message if provided
@@ -180,17 +166,17 @@ func (w *lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes
180
166
}
181
167
182
168
// Report an invalid config, this is specifically the user's fault
183
- func (* lintStringFormatRule ) configError (msg string , ruleNum , option int ) error {
169
+ func (* StringFormatRule ) configError (msg string , ruleNum , option int ) error {
184
170
return fmt .Errorf ("invalid configuration for string-format: %s [argument %d, option %d]" , msg , ruleNum , option )
185
171
}
186
172
187
173
// Report a general config parsing failure, this may be the user's fault, but it isn't known for certain
188
- func (* lintStringFormatRule ) parseError (msg string , ruleNum , option int ) error {
174
+ func (* StringFormatRule ) parseError (msg string , ruleNum , option int ) error {
189
175
return fmt .Errorf ("failed to parse configuration for string-format: %s [argument %d, option %d]" , msg , ruleNum , option )
190
176
}
191
177
192
178
// Report a general scope config parsing failure, this may be the user's fault, but it isn't known for certain
193
- func (* lintStringFormatRule ) parseScopeError (msg string , ruleNum , option , scopeNum int ) error {
179
+ func (* StringFormatRule ) parseScopeError (msg string , ruleNum , option , scopeNum int ) error {
194
180
return fmt .Errorf ("failed to parse configuration for string-format: %s [argument %d, option %d, scope index %d]" , msg , ruleNum , option , scopeNum )
195
181
}
196
182
@@ -314,7 +300,7 @@ func (r *stringFormatSubrule) generateFailure(node ast.Node) {
314
300
failure = fmt .Sprintf ("string literal doesn't match user defined regex /%s/" , r .regexp .String ())
315
301
}
316
302
317
- r .parent . onFailure (lint.Failure {
303
+ r .onFailure (lint.Failure {
318
304
Confidence : 1 ,
319
305
Failure : failure ,
320
306
Node : node ,
0 commit comments