1
0
mirror of https://github.com/mgechev/revive.git synced 2024-12-12 10:44:59 +02:00

refactor: replace panic with error in rules (#1126)

Co-authored-by: chavacava <salvadorcavadini+github@gmail.com>
Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com>
This commit is contained in:
Marcin Federowicz 2024-12-11 19:35:58 +01:00 committed by GitHub
parent 10d9697cc2
commit 9b15f3fcb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 514 additions and 239 deletions

View File

@ -106,7 +106,7 @@ func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-cha
for n := range packages { for n := range packages {
go func(pkg []string, gover *goversion.Version) { go func(pkg []string, gover *goversion.Version) {
if err := l.lintPackage(pkg, gover, ruleSet, config, failures); err != nil { if err := l.lintPackage(pkg, gover, ruleSet, config, failures); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, "error during linting: "+err.Error())
os.Exit(1) os.Exit(1)
} }
wg.Done() wg.Done()

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"regexp" "regexp"
@ -45,7 +46,7 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin
var configureErr error var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) }) r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil { if configureErr != nil {
return []lint.Failure{lint.NewInternalFailure(configureErr.Error())} return newInternalFailureError(configureErr)
} }
var failures []lint.Failure var failures []lint.Failure
@ -232,35 +233,35 @@ func (r *AddConstantRule) configure(arguments lint.Arguments) error {
} }
list, ok := v.(string) list, ok := v.(string)
if !ok { if !ok {
fmt.Errorf("invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v) return fmt.Errorf("invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v)
} }
r.allowList.add(kind, list) r.allowList.add(kind, list)
case "maxLitCount": case "maxLitCount":
sl, ok := v.(string) sl, ok := v.(string)
if !ok { if !ok {
fmt.Errorf("invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v) return fmt.Errorf("invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v)
} }
limit, err := strconv.Atoi(sl) limit, err := strconv.Atoi(sl)
if err != nil { if err != nil {
fmt.Errorf("invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v) return fmt.Errorf("invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)
} }
r.strLitLimit = limit r.strLitLimit = limit
case "ignoreFuncs": case "ignoreFuncs":
excludes, ok := v.(string) excludes, ok := v.(string)
if !ok { if !ok {
fmt.Errorf("invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v) return fmt.Errorf("invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v)
} }
for _, exclude := range strings.Split(excludes, ",") { for _, exclude := range strings.Split(excludes, ",") {
exclude = strings.Trim(exclude, " ") exclude = strings.Trim(exclude, " ")
if exclude == "" { if exclude == "" {
fmt.Errorf("invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.") return errors.New("invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty")
} }
exp, err := regexp.Compile(exclude) exp, err := regexp.Compile(exclude)
if err != nil { if err != nil {
fmt.Errorf("invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err) return fmt.Errorf("invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %w", exclude, err)
} }
r.ignoreFunctions = append(r.ignoreFunctions, exp) r.ignoreFunctions = append(r.ignoreFunctions, exp)

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"sync" "sync"
@ -17,22 +18,28 @@ type ArgumentsLimitRule struct {
const defaultArgumentsLimit = 8 const defaultArgumentsLimit = 8
func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) { func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultArgumentsLimit r.max = defaultArgumentsLimit
return return nil
} }
maxArguments, ok := arguments[0].(int64) // Alt. non panicking version maxArguments, ok := arguments[0].(int64) // Alt. non panicking version
if !ok { if !ok {
panic(`invalid value passed as argument number to the "argument-limit" rule`) return errors.New(`invalid value passed as argument number to the "argument-limit" rule`)
} }
r.max = int(maxArguments) r.max = int(maxArguments)
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -18,16 +18,30 @@ type BannedCharsRule struct {
const bannedCharsRuleName = "banned-characters" const bannedCharsRuleName = "banned-characters"
func (r *BannedCharsRule) configure(arguments lint.Arguments) { func (r *BannedCharsRule) configure(arguments lint.Arguments) error {
if len(arguments) > 0 { if len(arguments) > 0 {
checkNumberOfArguments(1, arguments, bannedCharsRuleName) err := checkNumberOfArguments(1, arguments, bannedCharsRuleName)
r.bannedCharList = r.getBannedCharsList(arguments) if err != nil {
return err
}
list, err := r.getBannedCharsList(arguments)
if err != nil {
return err
}
r.bannedCharList = list
} }
return nil
} }
// Apply applied the rule to the given file. // Apply applied the rule to the given file.
func (r *BannedCharsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *BannedCharsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
onFailure := func(failure lint.Failure) { onFailure := func(failure lint.Failure) {
@ -49,17 +63,17 @@ func (*BannedCharsRule) Name() string {
} }
// getBannedCharsList converts arguments into the banned characters list // getBannedCharsList converts arguments into the banned characters list
func (r *BannedCharsRule) getBannedCharsList(args lint.Arguments) []string { func (r *BannedCharsRule) getBannedCharsList(args lint.Arguments) ([]string, error) {
var bannedChars []string var bannedChars []string
for _, char := range args { for _, char := range args {
charStr, ok := char.(string) charStr, ok := char.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), char)) return nil, fmt.Errorf("invalid argument for the %s rule: expecting a string, got %T", r.Name(), char)
} }
bannedChars = append(bannedChars, charStr) bannedChars = append(bannedChars, charStr)
} }
return bannedChars return bannedChars, nil
} }
type lintBannedCharsRule struct { type lintBannedCharsRule struct {

View File

@ -19,23 +19,29 @@ type CognitiveComplexityRule struct {
const defaultMaxCognitiveComplexity = 7 const defaultMaxCognitiveComplexity = 7
func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) { func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.maxComplexity = defaultMaxCognitiveComplexity r.maxComplexity = defaultMaxCognitiveComplexity
return return nil
} }
complexity, ok := arguments[0].(int64) complexity, ok := arguments[0].(int64)
if !ok { if !ok {
panic(fmt.Sprintf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0])) return fmt.Errorf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0])
} }
r.maxComplexity = int(complexity) r.maxComplexity = int(complexity)
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -16,20 +16,26 @@ type CommentSpacingsRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *CommentSpacingsRule) configure(arguments lint.Arguments) { func (r *CommentSpacingsRule) configure(arguments lint.Arguments) error {
r.allowList = []string{} r.allowList = []string{}
for _, arg := range arguments { for _, arg := range arguments {
allow, ok := arg.(string) // Alt. non panicking version allow, ok := arg.(string) // Alt. non panicking version
if !ok { if !ok {
panic(fmt.Sprintf("invalid argument %v for %s; expected string but got %T", arg, r.Name(), arg)) return fmt.Errorf("invalid argument %v for %s; expected string but got %T", arg, r.Name(), arg)
} }
r.allowList = append(r.allowList, `//`+allow) r.allowList = append(r.allowList, `//`+allow)
} }
return nil
} }
// Apply the rule. // Apply the rule.
func (r *CommentSpacingsRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *CommentSpacingsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -18,23 +18,29 @@ type CommentsDensityRule struct {
const defaultMinimumCommentsPercentage = 0 const defaultMinimumCommentsPercentage = 0
func (r *CommentsDensityRule) configure(arguments lint.Arguments) { func (r *CommentsDensityRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.minimumCommentsDensity = defaultMinimumCommentsPercentage r.minimumCommentsDensity = defaultMinimumCommentsPercentage
return return nil
} }
var ok bool var ok bool
r.minimumCommentsDensity, ok = arguments[0].(int64) r.minimumCommentsDensity, ok = arguments[0].(int64)
if !ok { if !ok {
panic(fmt.Sprintf("invalid argument for %q rule: argument should be an int, got %T", r.Name(), arguments[0])) return fmt.Errorf("invalid argument for %q rule: argument should be an int, got %T", r.Name(), arguments[0])
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
commentsLines := countDocLines(file.AST.Comments) commentsLines := countDocLines(file.AST.Comments)
statementsCount := countStatements(file.AST) statementsCount := countStatements(file.AST)
density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100 density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100

View File

@ -17,8 +17,13 @@ type ContextAsArgumentRule struct {
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *ContextAsArgumentRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *ContextAsArgumentRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
for _, decl := range file.AST.Decls { for _, decl := range file.AST.Decls {
@ -59,27 +64,32 @@ func (*ContextAsArgumentRule) Name() string {
return "context-as-argument" return "context-as-argument"
} }
func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) { func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) error {
r.allowTypes = r.getAllowTypesFromArguments(arguments) types, err := r.getAllowTypesFromArguments(arguments)
if err != nil {
return err
}
r.allowTypes = types
return nil
} }
func (r *ContextAsArgumentRule) getAllowTypesFromArguments(args lint.Arguments) map[string]struct{} { func (r *ContextAsArgumentRule) getAllowTypesFromArguments(args lint.Arguments) (map[string]struct{}, error) {
allowTypesBefore := []string{} allowTypesBefore := []string{}
if len(args) >= 1 { if len(args) >= 1 {
argKV, ok := args[0].(map[string]any) argKV, ok := args[0].(map[string]any)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Expecting a k,v map, got %T", args[0])) return nil, fmt.Errorf("invalid argument to the context-as-argument rule. Expecting a k,v map, got %T", args[0])
} }
for k, v := range argKV { for k, v := range argKV {
switch k { switch k {
case "allowTypesBefore": case "allowTypesBefore":
typesBefore, ok := v.(string) typesBefore, ok := v.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got %T", v)) return nil, fmt.Errorf("invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got %T", v)
} }
allowTypesBefore = append(allowTypesBefore, strings.Split(typesBefore, ",")...) allowTypesBefore = append(allowTypesBefore, strings.Split(typesBefore, ",")...)
default: default:
panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Unrecognized key %s", k)) return nil, fmt.Errorf("invalid argument to the context-as-argument rule. Unrecognized key %s", k)
} }
} }
} }
@ -90,5 +100,5 @@ func (r *ContextAsArgumentRule) getAllowTypesFromArguments(args lint.Arguments)
} }
result["context.Context"] = struct{}{} // context.Context is always allowed before another context.Context result["context.Context"] = struct{}{} // context.Context is always allowed before another context.Context
return result return result, nil
} }

View File

@ -20,22 +20,28 @@ type CyclomaticRule struct {
const defaultMaxCyclomaticComplexity = 10 const defaultMaxCyclomaticComplexity = 10
func (r *CyclomaticRule) configure(arguments lint.Arguments) { func (r *CyclomaticRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.maxComplexity = defaultMaxCyclomaticComplexity r.maxComplexity = defaultMaxCyclomaticComplexity
return return nil
} }
complexity, ok := arguments[0].(int64) // Alt. non panicking version complexity, ok := arguments[0].(int64) // Alt. non panicking version
if !ok { if !ok {
panic(fmt.Sprintf("invalid argument for cyclomatic complexity; expected int but got %T", arguments[0])) return fmt.Errorf("invalid argument for cyclomatic complexity; expected int but got %T", arguments[0])
} }
r.maxComplexity = int(complexity) r.maxComplexity = int(complexity)
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
for _, decl := range file.AST.Decls { for _, decl := range file.AST.Decls {

View File

@ -15,13 +15,23 @@ type DeferRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *DeferRule) configure(arguments lint.Arguments) { func (r *DeferRule) configure(arguments lint.Arguments) error {
r.allow = r.allowFromArgs(arguments) list, err := r.allowFromArgs(arguments)
if err != nil {
return err
}
r.allow = list
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *DeferRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *DeferRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
onFailure := func(failure lint.Failure) { onFailure := func(failure lint.Failure) {
@ -39,7 +49,7 @@ func (*DeferRule) Name() string {
return "defer" return "defer"
} }
func (*DeferRule) allowFromArgs(args lint.Arguments) map[string]bool { func (*DeferRule) allowFromArgs(args lint.Arguments) (map[string]bool, error) {
if len(args) < 1 { if len(args) < 1 {
allow := map[string]bool{ allow := map[string]bool{
"loop": true, "loop": true,
@ -50,24 +60,24 @@ func (*DeferRule) allowFromArgs(args lint.Arguments) map[string]bool {
"immediate-recover": true, "immediate-recover": true,
} }
return allow return allow, nil
} }
aa, ok := args[0].([]any) aa, ok := args[0].([]any)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument '%v' for 'defer' rule. Expecting []string, got %T", args[0], args[0])) return nil, fmt.Errorf("invalid argument '%v' for 'defer' rule. Expecting []string, got %T", args[0], args[0])
} }
allow := make(map[string]bool, len(aa)) allow := make(map[string]bool, len(aa))
for _, subcase := range aa { for _, subcase := range aa {
sc, ok := subcase.(string) sc, ok := subcase.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument '%v' for 'defer' rule. Expecting string, got %T", subcase, subcase)) return nil, fmt.Errorf("invalid argument '%v' for 'defer' rule. Expecting string, got %T", subcase, subcase)
} }
allow[sc] = true allow[sc] = true
} }
return allow return allow, nil
} }
type lintDeferRule struct { type lintDeferRule struct {

View File

@ -17,7 +17,12 @@ type DotImportsRule struct {
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *DotImportsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *DotImportsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
@ -41,30 +46,31 @@ func (*DotImportsRule) Name() string {
return "dot-imports" return "dot-imports"
} }
func (r *DotImportsRule) configure(arguments lint.Arguments) { func (r *DotImportsRule) configure(arguments lint.Arguments) error {
r.allowedPackages = allowPackages{} r.allowedPackages = allowPackages{}
if len(arguments) == 0 { if len(arguments) == 0 {
return return nil
} }
args, ok := arguments[0].(map[string]any) args, ok := arguments[0].(map[string]any)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the dot-imports rule. Expecting a k,v map, got %T", arguments[0])) return fmt.Errorf("invalid argument to the dot-imports rule. Expecting a k,v map, got %T", arguments[0])
} }
if allowedPkgArg, ok := args["allowedPackages"]; ok { if allowedPkgArg, ok := args["allowedPackages"]; ok {
pkgs, ok := allowedPkgArg.([]any) pkgs, ok := allowedPkgArg.([]any)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the dot-imports rule, []string expected. Got '%v' (%T)", allowedPkgArg, allowedPkgArg)) return fmt.Errorf("invalid argument to the dot-imports rule, []string expected. Got '%v' (%T)", allowedPkgArg, allowedPkgArg)
} }
for _, p := range pkgs { for _, p := range pkgs {
pkg, ok := p.(string) pkg, ok := p.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the dot-imports rule, string expected. Got '%v' (%T)", p, p)) return fmt.Errorf("invalid argument to the dot-imports rule, string expected. Got '%v' (%T)", p, p)
} }
r.allowedPackages.add(pkg) r.allowedPackages.add(pkg)
} }
} }
return nil
} }
type lintImports struct { type lintImports struct {

View File

@ -44,35 +44,40 @@ type EnforceMapStyleRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *EnforceMapStyleRule) configure(arguments lint.Arguments) { func (r *EnforceMapStyleRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.enforceMapStyle = enforceMapStyleTypeAny r.enforceMapStyle = enforceMapStyleTypeAny
return return nil
} }
enforceMapStyle, ok := arguments[0].(string) enforceMapStyle, ok := arguments[0].(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument '%v' for 'enforce-map-style' rule. Expecting string, got %T", arguments[0], arguments[0])) return fmt.Errorf("invalid argument '%v' for 'enforce-map-style' rule. Expecting string, got %T", arguments[0], arguments[0])
} }
var err error var err error
r.enforceMapStyle, err = mapStyleFromString(enforceMapStyle) r.enforceMapStyle, err = mapStyleFromString(enforceMapStyle)
if err != nil { if err != nil {
panic(fmt.Sprintf("Invalid argument to the enforce-map-style rule: %v", err)) return fmt.Errorf("invalid argument to the enforce-map-style rule: %w", err)
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *EnforceMapStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *EnforceMapStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
if r.enforceMapStyle == enforceMapStyleTypeAny { if r.enforceMapStyle == enforceMapStyleTypeAny {
// this linter is not configured // this linter is not configured
return nil return nil
} }
var failures []lint.Failure var failures []lint.Failure
astFile := file.AST astFile := file.AST
ast.Inspect(astFile, func(n ast.Node) bool { ast.Inspect(astFile, func(n ast.Node) bool {
switch v := n.(type) { switch v := n.(type) {

View File

@ -16,14 +16,14 @@ const (
enforceRepeatedArgTypeStyleTypeFull enforceRepeatedArgTypeStyleType = "full" enforceRepeatedArgTypeStyleTypeFull enforceRepeatedArgTypeStyleType = "full"
) )
func repeatedArgTypeStyleFromString(s string) enforceRepeatedArgTypeStyleType { func repeatedArgTypeStyleFromString(s string) (enforceRepeatedArgTypeStyleType, error) {
switch s { switch s {
case string(enforceRepeatedArgTypeStyleTypeAny), "": case string(enforceRepeatedArgTypeStyleTypeAny), "":
return enforceRepeatedArgTypeStyleTypeAny return enforceRepeatedArgTypeStyleTypeAny, nil
case string(enforceRepeatedArgTypeStyleTypeShort): case string(enforceRepeatedArgTypeStyleTypeShort):
return enforceRepeatedArgTypeStyleTypeShort return enforceRepeatedArgTypeStyleTypeShort, nil
case string(enforceRepeatedArgTypeStyleTypeFull): case string(enforceRepeatedArgTypeStyleTypeFull):
return enforceRepeatedArgTypeStyleTypeFull return enforceRepeatedArgTypeStyleTypeFull, nil
default: default:
err := fmt.Errorf( err := fmt.Errorf(
"invalid repeated arg type style: %s (expecting one of %v)", "invalid repeated arg type style: %s (expecting one of %v)",
@ -35,7 +35,7 @@ func repeatedArgTypeStyleFromString(s string) enforceRepeatedArgTypeStyleType {
}, },
) )
panic(fmt.Sprintf("Invalid argument to the enforce-repeated-arg-type-style rule: %v", err)) return "", fmt.Errorf("invalid argument to the enforce-repeated-arg-type-style rule: %w", err)
} }
} }
@ -47,45 +47,67 @@ type EnforceRepeatedArgTypeStyleRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *EnforceRepeatedArgTypeStyleRule) configure(arguments lint.Arguments) { func (r *EnforceRepeatedArgTypeStyleRule) configure(arguments lint.Arguments) error {
r.funcArgStyle = enforceRepeatedArgTypeStyleTypeAny r.funcArgStyle = enforceRepeatedArgTypeStyleTypeAny
r.funcRetValStyle = enforceRepeatedArgTypeStyleTypeAny r.funcRetValStyle = enforceRepeatedArgTypeStyleTypeAny
if len(arguments) == 0 { if len(arguments) == 0 {
return return nil
} }
switch funcArgStyle := arguments[0].(type) { switch funcArgStyle := arguments[0].(type) {
case string: case string:
r.funcArgStyle = repeatedArgTypeStyleFromString(funcArgStyle) argstyle, err := repeatedArgTypeStyleFromString(funcArgStyle)
r.funcRetValStyle = repeatedArgTypeStyleFromString(funcArgStyle) if err != nil {
return err
}
r.funcArgStyle = argstyle
valstyle, err := repeatedArgTypeStyleFromString(funcArgStyle)
if err != nil {
return err
}
r.funcRetValStyle = valstyle
case map[string]any: // expecting map[string]string case map[string]any: // expecting map[string]string
for k, v := range funcArgStyle { for k, v := range funcArgStyle {
switch k { switch k {
case "funcArgStyle": case "funcArgStyle":
val, ok := v.(string) val, ok := v.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid map value type for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v)) return fmt.Errorf("invalid map value type for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v)
} }
r.funcArgStyle = repeatedArgTypeStyleFromString(val) valstyle, err := repeatedArgTypeStyleFromString(val)
if err != nil {
return err
}
r.funcArgStyle = valstyle
case "funcRetValStyle": case "funcRetValStyle":
val, ok := v.(string) val, ok := v.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid map value '%v' for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v, v)) return fmt.Errorf("invalid map value '%v' for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v, v)
} }
r.funcRetValStyle = repeatedArgTypeStyleFromString(val) argstyle, err := repeatedArgTypeStyleFromString(val)
if err != nil {
return err
}
r.funcRetValStyle = argstyle
default: default:
panic(fmt.Sprintf("Invalid map key for 'enforce-repeated-arg-type-style' rule. Expecting 'funcArgStyle' or 'funcRetValStyle', got %v", k)) return fmt.Errorf("invalid map key for 'enforce-repeated-arg-type-style' rule. Expecting 'funcArgStyle' or 'funcRetValStyle', got %v", k)
} }
} }
default: default:
panic(fmt.Sprintf("Invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])) return fmt.Errorf("invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])
} }
return nil
} }
// Apply applies the rule to a given file. // Apply applies the rule to a given file.
func (r *EnforceRepeatedArgTypeStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *EnforceRepeatedArgTypeStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeAny && r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeAny { if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeAny && r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeAny {
// This linter is not configured, return no failures. // This linter is not configured, return no failures.

View File

@ -48,27 +48,33 @@ type EnforceSliceStyleRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *EnforceSliceStyleRule) configure(arguments lint.Arguments) { func (r *EnforceSliceStyleRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.enforceSliceStyle = enforceSliceStyleTypeAny r.enforceSliceStyle = enforceSliceStyleTypeAny
return return nil
} }
enforceSliceStyle, ok := arguments[0].(string) enforceSliceStyle, ok := arguments[0].(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument '%v' for 'enforce-slice-style' rule. Expecting string, got %T", arguments[0], arguments[0])) return fmt.Errorf("invalid argument '%v' for 'enforce-slice-style' rule. Expecting string, got %T", arguments[0], arguments[0])
} }
var err error var err error
r.enforceSliceStyle, err = sliceStyleFromString(enforceSliceStyle) r.enforceSliceStyle, err = sliceStyleFromString(enforceSliceStyle)
if err != nil { if err != nil {
panic(fmt.Sprintf("Invalid argument to the enforce-slice-style rule: %v", err)) return fmt.Errorf("invalid argument to the enforce-slice-style rule: %w", err)
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
if r.enforceSliceStyle == enforceSliceStyleTypeAny { if r.enforceSliceStyle == enforceSliceStyleTypeAny {
// this linter is not configured // this linter is not configured

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"strconv" "strconv"
@ -19,7 +20,7 @@ type ErrorStringsRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *ErrorStringsRule) configure(arguments lint.Arguments) { func (r *ErrorStringsRule) configure(arguments lint.Arguments) error {
r.errorFunctions = map[string]map[string]struct{}{ r.errorFunctions = map[string]map[string]struct{}{
"fmt": { "fmt": {
"Errorf": {}, "Errorf": {},
@ -46,15 +47,21 @@ func (r *ErrorStringsRule) configure(arguments lint.Arguments) {
} }
} }
if len(invalidCustomFunctions) != 0 { if len(invalidCustomFunctions) != 0 {
panic("found invalid custom function: " + strings.Join(invalidCustomFunctions, ",")) return fmt.Errorf("found invalid custom function: " + strings.Join(invalidCustomFunctions, ","))
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *ErrorStringsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *ErrorStringsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var failures []lint.Failure var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
r.configureOnce.Do(func() { r.configure(arguments) }) if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure
fileAst := file.AST fileAst := file.AST
walker := lintErrorStrings{ walker := lintErrorStrings{

View File

@ -70,7 +70,7 @@ type ExportedRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *ExportedRule) configure(arguments lint.Arguments) { func (r *ExportedRule) configure(arguments lint.Arguments) error {
r.disabledChecks = disabledChecks{PrivateReceivers: true, PublicInterfaces: true} r.disabledChecks = disabledChecks{PrivateReceivers: true, PublicInterfaces: true}
r.stuttersMsg = "stutters" r.stuttersMsg = "stutters"
for _, flag := range arguments { for _, flag := range arguments {
@ -96,17 +96,24 @@ func (r *ExportedRule) configure(arguments lint.Arguments) {
case "disableChecksOnVariables": case "disableChecksOnVariables":
r.disabledChecks.Var = true r.disabledChecks.Var = true
default: default:
panic(fmt.Sprintf("Unknown configuration flag %s for %s rule", flag, r.Name())) return fmt.Errorf("unknown configuration flag %s for %s rule", flag, r.Name())
} }
default: default:
panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), flag)) return fmt.Errorf("invalid argument for the %s rule: expecting a string, got %T", r.Name(), flag)
} }
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *ExportedRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *ExportedRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
if file.IsTest() { if file.IsTest() {

View File

@ -20,21 +20,27 @@ var (
singleRegexp = regexp.MustCompile("^//") singleRegexp = regexp.MustCompile("^//")
) )
func (r *FileHeaderRule) configure(arguments lint.Arguments) { func (r *FileHeaderRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
return return nil
} }
var ok bool var ok bool
r.header, ok = arguments[0].(string) r.header, ok = arguments[0].(string)
if !ok { if !ok {
panic(fmt.Sprintf("invalid argument for \"file-header\" rule: argument should be a string, got %T", arguments[0])) return fmt.Errorf(`invalid argument for "file-header" rule: argument should be a string, got %T`, arguments[0])
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
if r.header == "" { if r.header == "" {
return nil return nil
@ -69,7 +75,7 @@ func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint
regex, err := regexp.Compile(r.header) regex, err := regexp.Compile(r.header)
if err != nil { if err != nil {
panic(err.Error()) return newInternalFailureError(err)
} }
if !regex.MatchString(comment) { if !regex.MatchString(comment) {

View File

@ -26,7 +26,12 @@ type FileLengthLimitRule struct {
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *FileLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *FileLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
if r.max <= 0 { if r.max <= 0 {
// when max is negative or 0 the rule is disabled // when max is negative or 0 the rule is disabled
@ -44,7 +49,7 @@ func (r *FileLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) [
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
panic(err.Error()) return newInternalFailureError(err)
} }
lines := all lines := all
@ -75,37 +80,38 @@ func (r *FileLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) [
} }
} }
func (r *FileLengthLimitRule) configure(arguments lint.Arguments) { func (r *FileLengthLimitRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
return // use default return nil // use default
} }
argKV, ok := arguments[0].(map[string]any) argKV, ok := arguments[0].(map[string]any)
if !ok { if !ok {
panic(fmt.Sprintf(`invalid argument to the "file-length-limit" rule. Expecting a k,v map, got %T`, arguments[0])) return fmt.Errorf(`invalid argument to the "file-length-limit" rule. Expecting a k,v map, got %T`, arguments[0])
} }
for k, v := range argKV { for k, v := range argKV {
switch k { switch k {
case "max": case "max":
maxLines, ok := v.(int64) maxLines, ok := v.(int64)
if !ok || maxLines < 0 { if !ok || maxLines < 0 {
panic(fmt.Sprintf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, arguments[0])) return fmt.Errorf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, arguments[0])
} }
r.max = int(maxLines) r.max = int(maxLines)
case "skipComments": case "skipComments":
skipComments, ok := v.(bool) skipComments, ok := v.(bool)
if !ok { if !ok {
panic(fmt.Sprintf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, arguments[1])) return fmt.Errorf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, arguments[1])
} }
r.skipComments = skipComments r.skipComments = skipComments
case "skipBlankLines": case "skipBlankLines":
skipBlankLines, ok := v.(bool) skipBlankLines, ok := v.(bool)
if !ok { if !ok {
panic(fmt.Sprintf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, arguments[2])) return fmt.Errorf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, arguments[2])
} }
r.skipBlankLines = skipBlankLines r.skipBlankLines = skipBlankLines
} }
} }
return nil
} }
// Name returns the rule name. // Name returns the rule name.

View File

@ -19,7 +19,12 @@ type FilenameFormatRule struct {
// Apply applies the rule to the given file. // Apply applies the rule to the given file.
func (r *FilenameFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *FilenameFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
filename := filepath.Base(file.Name) filename := filepath.Base(file.Name)
if r.format.MatchString(filename) { if r.format.MatchString(filename) {
@ -55,27 +60,29 @@ func (*FilenameFormatRule) Name() string {
var defaultFormat = regexp.MustCompile(`^[_A-Za-z0-9][_A-Za-z0-9-]*\.go$`) var defaultFormat = regexp.MustCompile(`^[_A-Za-z0-9][_A-Za-z0-9-]*\.go$`)
func (r *FilenameFormatRule) configure(arguments lint.Arguments) { func (r *FilenameFormatRule) configure(arguments lint.Arguments) error {
argsCount := len(arguments) argsCount := len(arguments)
if argsCount == 0 { if argsCount == 0 {
r.format = defaultFormat r.format = defaultFormat
return return nil
} }
if argsCount > 1 { if argsCount > 1 {
panic(fmt.Sprintf("rule %q expects only one argument, got %d %v", r.Name(), argsCount, arguments)) return fmt.Errorf("rule %q expects only one argument, got %d %v", r.Name(), argsCount, arguments)
} }
arg := arguments[0] arg := arguments[0]
str, ok := arg.(string) str, ok := arg.(string)
if !ok { if !ok {
panic(fmt.Sprintf("rule %q expects a string argument, got %v of type %T", r.Name(), arg, arg)) return fmt.Errorf("rule %q expects a string argument, got %v of type %T", r.Name(), arg, arg)
} }
format, err := regexp.Compile(str) format, err := regexp.Compile(str)
if err != nil { if err != nil {
panic(fmt.Sprintf("rule %q expects a valid regexp argument, got %v for %s", r.Name(), err, arg)) return fmt.Errorf("rule %q expects a valid regexp argument, got error for %s: %w", r.Name(), str, err)
} }
r.format = format r.format = format
return nil
} }

View File

@ -17,15 +17,24 @@ type FunctionLength struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *FunctionLength) configure(arguments lint.Arguments) { func (r *FunctionLength) configure(arguments lint.Arguments) error {
maxStmt, maxLines := r.parseArguments(arguments) maxStmt, maxLines, err := r.parseArguments(arguments)
if err != nil {
return err
}
r.maxStmt = int(maxStmt) r.maxStmt = int(maxStmt)
r.maxLines = int(maxLines) r.maxLines = int(maxLines)
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *FunctionLength) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *FunctionLength) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
for _, decl := range file.AST.Decls { for _, decl := range file.AST.Decls {
@ -74,33 +83,33 @@ func (*FunctionLength) Name() string {
const defaultFuncStmtsLimit = 50 const defaultFuncStmtsLimit = 50
const defaultFuncLinesLimit = 75 const defaultFuncLinesLimit = 75
func (*FunctionLength) parseArguments(arguments lint.Arguments) (maxStmt, maxLines int64) { func (*FunctionLength) parseArguments(arguments lint.Arguments) (maxStmt, maxLines int64, err error) {
if len(arguments) == 0 { if len(arguments) == 0 {
return defaultFuncStmtsLimit, defaultFuncLinesLimit return defaultFuncStmtsLimit, defaultFuncLinesLimit, nil
} }
const minArguments = 2 const minArguments = 2
if len(arguments) != minArguments { if len(arguments) != minArguments {
panic(fmt.Sprintf(`invalid configuration for "function-length" rule, expected %d arguments but got %d`, minArguments, len(arguments))) return 0, 0, fmt.Errorf(`invalid configuration for "function-length" rule, expected %d arguments but got %d`, minArguments, len(arguments))
} }
maxStmt, maxStmtOk := arguments[0].(int64) maxStmt, maxStmtOk := arguments[0].(int64)
if !maxStmtOk { if !maxStmtOk {
panic(fmt.Sprintf(`invalid configuration value for max statements in "function-length" rule; need int64 but got %T`, arguments[0])) return 0, 0, fmt.Errorf(`invalid configuration value for max statements in "function-length" rule; need int64 but got %T`, arguments[0])
} }
if maxStmt < 0 { if maxStmt < 0 {
panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxStmt)) return 0, 0, fmt.Errorf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxStmt)
} }
maxLines, maxLinesOk := arguments[1].(int64) maxLines, maxLinesOk := arguments[1].(int64)
if !maxLinesOk { if !maxLinesOk {
panic(fmt.Sprintf(`invalid configuration value for max lines in "function-length" rule; need int64 but got %T`, arguments[1])) return 0, 0, fmt.Errorf(`invalid configuration value for max lines in "function-length" rule; need int64 but got %T`, arguments[1])
} }
if maxLines < 0 { if maxLines < 0 {
panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxLines)) return 0, 0, fmt.Errorf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxLines)
} }
return maxStmt, maxLines return maxStmt, maxLines, nil
} }
func (*FunctionLength) countLines(b *ast.BlockStmt, file *lint.File) int { func (*FunctionLength) countLines(b *ast.BlockStmt, file *lint.File) int {

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"sync" "sync"
@ -17,7 +18,12 @@ type FunctionResultsLimitRule struct {
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
for _, decl := range file.AST.Decls { for _, decl := range file.AST.Decls {
@ -53,19 +59,20 @@ func (*FunctionResultsLimitRule) Name() string {
const defaultResultsLimit = 3 const defaultResultsLimit = 3
func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) { func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultResultsLimit r.max = defaultResultsLimit
return return nil
} }
maxResults, ok := arguments[0].(int64) // Alt. non panicking version maxResults, ok := arguments[0].(int64) // Alt. non panicking version
if !ok { if !ok {
panic(fmt.Sprintf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0])) return fmt.Errorf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0])
} }
if maxResults < 0 { if maxResults < 0 {
panic(`the value passed as return results number to the "function-result-limit" rule cannot be negative`) return errors.New(`the value passed as return results number to the "function-result-limit" rule cannot be negative`)
} }
r.max = int(maxResults) r.max = int(maxResults)
return nil
} }

View File

@ -20,38 +20,54 @@ const defaultImportAliasNamingAllowRule = "^[a-z][a-z0-9]{0,}$"
var defaultImportAliasNamingAllowRegexp = regexp.MustCompile(defaultImportAliasNamingAllowRule) var defaultImportAliasNamingAllowRegexp = regexp.MustCompile(defaultImportAliasNamingAllowRule)
func (r *ImportAliasNamingRule) configure(arguments lint.Arguments) { func (r *ImportAliasNamingRule) configure(arguments lint.Arguments) error {
if len(arguments) == 0 { if len(arguments) == 0 {
r.allowRegexp = defaultImportAliasNamingAllowRegexp r.allowRegexp = defaultImportAliasNamingAllowRegexp
return return nil
} }
switch namingRule := arguments[0].(type) { switch namingRule := arguments[0].(type) {
case string: case string:
r.setAllowRule(namingRule) err := r.setAllowRule(namingRule)
if err != nil {
return err
}
case map[string]any: // expecting map[string]string case map[string]any: // expecting map[string]string
for k, v := range namingRule { for k, v := range namingRule {
switch k { switch k {
case "allowRegex": case "allowRegex":
r.setAllowRule(v) err := r.setAllowRule(v)
if err != nil {
return err
}
case "denyRegex": case "denyRegex":
r.setDenyRule(v) err := r.setDenyRule(v)
if err != nil {
return err
}
default: default:
panic(fmt.Sprintf("Invalid map key for 'import-alias-naming' rule. Expecting 'allowRegex' or 'denyRegex', got %v", k)) return fmt.Errorf("invalid map key for 'import-alias-naming' rule. Expecting 'allowRegex' or 'denyRegex', got %v", k)
} }
} }
default: default:
panic(fmt.Sprintf("Invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])) return fmt.Errorf("invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])
} }
if r.allowRegexp == nil && r.denyRegexp == nil { if r.allowRegexp == nil && r.denyRegexp == nil {
r.allowRegexp = defaultImportAliasNamingAllowRegexp r.allowRegexp = defaultImportAliasNamingAllowRegexp
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *ImportAliasNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *ImportAliasNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
@ -93,28 +109,30 @@ func (*ImportAliasNamingRule) Name() string {
return "import-alias-naming" return "import-alias-naming"
} }
func (r *ImportAliasNamingRule) setAllowRule(value any) { func (r *ImportAliasNamingRule) setAllowRule(value any) error {
namingRule, ok := value.(string) namingRule, ok := value.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument '%v' for import-alias-naming allowRegexp rule. Expecting string, got %T", value, value)) return fmt.Errorf("invalid argument '%v' for import-alias-naming allowRegexp rule. Expecting string, got %T", value, value)
} }
namingRuleRegexp, err := regexp.Compile(namingRule) namingRuleRegexp, err := regexp.Compile(namingRule)
if err != nil { if err != nil {
panic(fmt.Sprintf("Invalid argument to the import-alias-naming allowRegexp rule. Expecting %q to be a valid regular expression, got: %v", namingRule, err)) return fmt.Errorf("invalid argument to the import-alias-naming allowRegexp rule. Expecting %q to be a valid regular expression, got: %w", namingRule, err)
} }
r.allowRegexp = namingRuleRegexp r.allowRegexp = namingRuleRegexp
return nil
} }
func (r *ImportAliasNamingRule) setDenyRule(value any) { func (r *ImportAliasNamingRule) setDenyRule(value any) error {
namingRule, ok := value.(string) namingRule, ok := value.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument '%v' for import-alias-naming denyRegexp rule. Expecting string, got %T", value, value)) return fmt.Errorf("invalid argument '%v' for import-alias-naming denyRegexp rule. Expecting string, got %T", value, value)
} }
namingRuleRegexp, err := regexp.Compile(namingRule) namingRuleRegexp, err := regexp.Compile(namingRule)
if err != nil { if err != nil {
panic(fmt.Sprintf("Invalid argument to the import-alias-naming denyRegexp rule. Expecting %q to be a valid regular expression, got: %v", namingRule, err)) return fmt.Errorf("invalid argument to the import-alias-naming denyRegexp rule. Expecting %q to be a valid regular expression, got: %w", namingRule, err)
} }
r.denyRegexp = namingRuleRegexp r.denyRegexp = namingRuleRegexp
return nil
} }

View File

@ -17,19 +17,20 @@ type ImportsBlocklistRule struct {
var replaceImportRegexp = regexp.MustCompile(`/?\*\*/?`) var replaceImportRegexp = regexp.MustCompile(`/?\*\*/?`)
func (r *ImportsBlocklistRule) configure(arguments lint.Arguments) { func (r *ImportsBlocklistRule) configure(arguments lint.Arguments) error {
r.blocklist = []*regexp.Regexp{} r.blocklist = []*regexp.Regexp{}
for _, arg := range arguments { for _, arg := range arguments {
argStr, ok := arg.(string) argStr, ok := arg.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the imports-blocklist rule. Expecting a string, got %T", arg)) return fmt.Errorf("invalid argument to the imports-blocklist rule. Expecting a string, got %T", arg)
} }
regStr, err := regexp.Compile(fmt.Sprintf(`(?m)"%s"$`, replaceImportRegexp.ReplaceAllString(argStr, `(\W|\w)*`))) regStr, err := regexp.Compile(fmt.Sprintf(`(?m)"%s"$`, replaceImportRegexp.ReplaceAllString(argStr, `(\W|\w)*`)))
if err != nil { if err != nil {
panic(fmt.Sprintf("Invalid argument to the imports-blocklist rule. Expecting %q to be a valid regular expression, got: %v", argStr, err)) return fmt.Errorf("invalid argument to the imports-blocklist rule. Expecting %q to be a valid regular expression, got: %w", argStr, err)
} }
r.blocklist = append(r.blocklist, regStr) r.blocklist = append(r.blocklist, regStr)
} }
return nil
} }
func (r *ImportsBlocklistRule) isBlocklisted(path string) bool { func (r *ImportsBlocklistRule) isBlocklisted(path string) bool {
@ -43,7 +44,12 @@ func (r *ImportsBlocklistRule) isBlocklisted(path string) bool {
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *ImportsBlocklistRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *ImportsBlocklistRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -3,6 +3,7 @@ package rule
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"errors"
"fmt" "fmt"
"go/token" "go/token"
"strings" "strings"
@ -21,23 +22,29 @@ type LineLengthLimitRule struct {
const defaultLineLengthLimit = 80 const defaultLineLengthLimit = 80
func (r *LineLengthLimitRule) configure(arguments lint.Arguments) { func (r *LineLengthLimitRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultLineLengthLimit r.max = defaultLineLengthLimit
return return nil
} }
maxLength, ok := arguments[0].(int64) // Alt. non panicking version maxLength, ok := arguments[0].(int64) // Alt. non panicking version
if !ok || maxLength < 0 { if !ok || maxLength < 0 {
panic(`invalid value passed as argument number to the "line-length-limit" rule`) return errors.New(`invalid value passed as argument number to the "line-length-limit" rule`)
} }
r.max = int(maxLength) r.max = int(maxLength)
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *LineLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *LineLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"sync" "sync"
@ -19,7 +20,12 @@ const defaultMaxControlNesting = 5
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *MaxControlNestingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *MaxControlNestingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
@ -107,17 +113,21 @@ func (w *lintMaxControlNesting) walkControlledBlock(b ast.Node) {
w.nestingLevelAcc = oldNestingLevel w.nestingLevelAcc = oldNestingLevel
} }
func (r *MaxControlNestingRule) configure(arguments lint.Arguments) { func (r *MaxControlNestingRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultMaxControlNesting r.max = defaultMaxControlNesting
return return nil
} }
checkNumberOfArguments(1, arguments, r.Name()) check := checkNumberOfArguments(1, arguments, r.Name())
if check != nil {
return check
}
maxNesting, ok := arguments[0].(int64) // Alt. non panicking version maxNesting, ok := arguments[0].(int64) // Alt. non panicking version
if !ok { if !ok {
panic(`invalid value passed as argument number to the "max-control-nesting" rule`) return errors.New(`invalid value passed as argument number to the "max-control-nesting" rule`)
} }
r.max = maxNesting r.max = maxNesting
return nil
} }

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"strings" "strings"
@ -18,24 +19,33 @@ type MaxPublicStructsRule struct {
const defaultMaxPublicStructs = 5 const defaultMaxPublicStructs = 5
func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) { func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultMaxPublicStructs r.max = defaultMaxPublicStructs
return return nil
} }
checkNumberOfArguments(1, arguments, r.Name()) err := checkNumberOfArguments(1, arguments, r.Name())
if err != nil {
return err
}
maxStructs, ok := arguments[0].(int64) // Alt. non panicking version maxStructs, ok := arguments[0].(int64) // Alt. non panicking version
if !ok { if !ok {
panic(`invalid value passed as argument number to the "max-public-structs" rule`) return errors.New(`invalid value passed as argument number to the "max-public-structs" rule`)
} }
r.max = maxStructs r.max = maxStructs
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *MaxPublicStructsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *MaxPublicStructsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -18,15 +18,15 @@ type ReceiverNamingRule struct {
const defaultReceiverNameMaxLength = -1 // thus will not check const defaultReceiverNameMaxLength = -1 // thus will not check
func (r *ReceiverNamingRule) configure(arguments lint.Arguments) { func (r *ReceiverNamingRule) configure(arguments lint.Arguments) error {
r.receiverNameMaxLength = defaultReceiverNameMaxLength r.receiverNameMaxLength = defaultReceiverNameMaxLength
if len(arguments) < 1 { if len(arguments) < 1 {
return return nil
} }
args, ok := arguments[0].(map[string]any) args, ok := arguments[0].(map[string]any)
if !ok { if !ok {
panic(fmt.Sprintf("Unable to get arguments for rule %s. Expected object of key-value-pairs.", r.Name())) return fmt.Errorf("unable to get arguments for rule %s. Expected object of key-value-pairs", r.Name())
} }
for k, v := range args { for k, v := range args {
@ -34,18 +34,24 @@ func (r *ReceiverNamingRule) configure(arguments lint.Arguments) {
case "maxLength": case "maxLength":
value, ok := v.(int64) value, ok := v.(int64)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid value %v for argument %s of rule %s, expected integer value got %T", v, k, r.Name(), v)) return fmt.Errorf("invalid value %v for argument %s of rule %s, expected integer value got %T", v, k, r.Name(), v)
} }
r.receiverNameMaxLength = int(value) r.receiverNameMaxLength = int(value)
default: default:
panic(fmt.Sprintf("Unknown argument %s for %s rule.", k, r.Name())) return fmt.Errorf("unknown argument %s for %s rule", k, r.Name())
} }
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *ReceiverNamingRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *ReceiverNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
typeReceiver := map[string]string{} typeReceiver := map[string]string{}
var failures []lint.Failure var failures []lint.Failure

View File

@ -23,7 +23,11 @@ func (*StringFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint
} }
w := lintStringFormatRule{onFailure: onFailure} w := lintStringFormatRule{onFailure: onFailure}
w.parseArguments(arguments) err := w.parseArguments(arguments)
if err != nil {
return newInternalFailureError(err)
}
ast.Walk(w, file.AST) ast.Walk(w, file.AST)
return failures return failures
@ -41,10 +45,9 @@ func (StringFormatRule) ParseArgumentsTest(arguments lint.Arguments) *string {
// Parse the arguments in a goroutine, defer a recover() call, return the error encountered (or nil if there was no error) // Parse the arguments in a goroutine, defer a recover() call, return the error encountered (or nil if there was no error)
go func() { go func() {
defer func() { defer func() {
err := recover() err := w.parseArguments(arguments)
c <- err c <- err
}() }()
w.parseArguments(arguments)
}() }()
err := <-c err := <-c
if err != nil { if err != nil {
@ -81,9 +84,12 @@ const identRegex = "[_A-Za-z][_A-Za-z0-9]*"
var parseStringFormatScope = regexp.MustCompile( var parseStringFormatScope = regexp.MustCompile(
fmt.Sprintf("^(%s(?:\\.%s)?)(?:\\[([0-9]+)\\](?:\\.(%s))?)?$", identRegex, identRegex, identRegex)) fmt.Sprintf("^(%s(?:\\.%s)?)(?:\\[([0-9]+)\\](?:\\.(%s))?)?$", identRegex, identRegex, identRegex))
func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) { func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) error {
for i, argument := range arguments { for i, argument := range arguments {
scopes, regex, negated, errorMessage := w.parseArgument(argument, i) scopes, regex, negated, errorMessage, err := w.parseArgument(argument, i)
if err != nil {
return err
}
w.rules = append(w.rules, stringFormatSubrule{ w.rules = append(w.rules, stringFormatSubrule{
parent: w, parent: w,
scopes: scopes, scopes: scopes,
@ -92,30 +98,31 @@ func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) {
errorMessage: errorMessage, errorMessage: errorMessage,
}) })
} }
return nil
} }
func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes stringFormatSubruleScopes, regex *regexp.Regexp, negated bool, errorMessage string) { func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes stringFormatSubruleScopes, regex *regexp.Regexp, negated bool, errorMessage string, err error) {
g, ok := argument.([]any) // Cast to generic slice first g, ok := argument.([]any) // Cast to generic slice first
if !ok { if !ok {
w.configError("argument is not a slice", ruleNum, 0) return stringFormatSubruleScopes{}, regex, false, "", w.configError("argument is not a slice", ruleNum, 0)
} }
if len(g) < 2 { if len(g) < 2 {
w.configError("less than two slices found in argument, scope and regex are required", ruleNum, len(g)-1) return stringFormatSubruleScopes{}, regex, false, "", w.configError("less than two slices found in argument, scope and regex are required", ruleNum, len(g)-1)
} }
rule := make([]string, len(g)) rule := make([]string, len(g))
for i, obj := range g { for i, obj := range g {
val, ok := obj.(string) val, ok := obj.(string)
if !ok { if !ok {
w.configError("unexpected value, string was expected", ruleNum, i) return stringFormatSubruleScopes{}, regex, false, "", w.configError("unexpected value, string was expected", ruleNum, i)
} }
rule[i] = val rule[i] = val
} }
// Validate scope and regex length // Validate scope and regex length
if rule[0] == "" { if rule[0] == "" {
w.configError("empty scope provided", ruleNum, 0) return stringFormatSubruleScopes{}, regex, false, "", w.configError("empty scope provided", ruleNum, 0)
} else if len(rule[1]) < 2 { } else if len(rule[1]) < 2 {
w.configError("regex is too small (regexes should begin and end with '/')", ruleNum, 1) return stringFormatSubruleScopes{}, regex, false, "", w.configError("regex is too small (regexes should begin and end with '/')", ruleNum, 1)
} }
// Parse rule scopes // Parse rule scopes
@ -126,24 +133,24 @@ func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes s
rawScope = strings.TrimSpace(rawScope) rawScope = strings.TrimSpace(rawScope)
if len(rawScope) == 0 { if len(rawScope) == 0 {
w.parseScopeError("empty scope in rule scopes:", ruleNum, 0, scopeNum) return stringFormatSubruleScopes{}, regex, false, "", w.parseScopeError("empty scope in rule scopes:", ruleNum, 0, scopeNum)
} }
scope := stringFormatSubruleScope{} scope := stringFormatSubruleScope{}
matches := parseStringFormatScope.FindStringSubmatch(rawScope) matches := parseStringFormatScope.FindStringSubmatch(rawScope)
if matches == nil { if matches == nil {
// The rule's scope didn't match the parsing regex at all, probably a configuration error // The rule's scope didn't match the parsing regex at all, probably a configuration error
w.parseScopeError("unable to parse rule scope", ruleNum, 0, scopeNum) return stringFormatSubruleScopes{}, regex, false, "", w.parseScopeError("unable to parse rule scope", ruleNum, 0, scopeNum)
} else if len(matches) != 4 { } else if len(matches) != 4 {
// The rule's scope matched the parsing regex, but an unexpected number of submatches was returned, probably a bug // The rule's scope matched the parsing regex, but an unexpected number of submatches was returned, probably a bug
w.parseScopeError(fmt.Sprintf("unexpected number of submatches when parsing scope: %d, expected 4", len(matches)), ruleNum, 0, scopeNum) return stringFormatSubruleScopes{}, regex, false, "", w.parseScopeError(fmt.Sprintf("unexpected number of submatches when parsing scope: %d, expected 4", len(matches)), ruleNum, 0, scopeNum)
} }
scope.funcName = matches[1] scope.funcName = matches[1]
if len(matches[2]) > 0 { if len(matches[2]) > 0 {
var err error var err error
scope.argument, err = strconv.Atoi(matches[2]) scope.argument, err = strconv.Atoi(matches[2])
if err != nil { if err != nil {
w.parseScopeError("unable to parse argument number in rule scope", ruleNum, 0, scopeNum) return stringFormatSubruleScopes{}, regex, false, "", w.parseScopeError("unable to parse argument number in rule scope", ruleNum, 0, scopeNum)
} }
} }
if len(matches[3]) > 0 { if len(matches[3]) > 0 {
@ -159,31 +166,31 @@ func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes s
if negated { if negated {
offset++ offset++
} }
regex, err := regexp.Compile(rule[1][offset : len(rule[1])-1]) regex, errr := regexp.Compile(rule[1][offset : len(rule[1])-1])
if err != nil { if errr != nil {
w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1) return stringFormatSubruleScopes{}, regex, false, "", w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1)
} }
// Use custom error message if provided // Use custom error message if provided
if len(rule) == 3 { if len(rule) == 3 {
errorMessage = rule[2] errorMessage = rule[2]
} }
return scopes, regex, negated, errorMessage return scopes, regex, negated, errorMessage, nil
} }
// Report an invalid config, this is specifically the user's fault // Report an invalid config, this is specifically the user's fault
func (lintStringFormatRule) configError(msg string, ruleNum, option int) { func (lintStringFormatRule) configError(msg string, ruleNum, option int) error {
panic(fmt.Sprintf("invalid configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) return fmt.Errorf("invalid configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)
} }
// Report a general config parsing failure, this may be the user's fault, but it isn't known for certain // Report a general config parsing failure, this may be the user's fault, but it isn't known for certain
func (lintStringFormatRule) parseError(msg string, ruleNum, option int) { func (lintStringFormatRule) parseError(msg string, ruleNum, option int) error {
panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) return fmt.Errorf("failed to parse configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)
} }
// Report a general scope config parsing failure, this may be the user's fault, but it isn't known for certain // Report a general scope config parsing failure, this may be the user's fault, but it isn't known for certain
func (lintStringFormatRule) parseScopeError(msg string, ruleNum, option, scopeNum int) { func (lintStringFormatRule) parseScopeError(msg string, ruleNum, option, scopeNum int) error {
panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d, scope index %d]", msg, ruleNum, option, scopeNum)) return fmt.Errorf("failed to parse configuration for string-format: %s [argument %d, option %d, scope index %d]", msg, ruleNum, option, scopeNum)
} }
func (w lintStringFormatRule) Visit(node ast.Node) ast.Visitor { func (w lintStringFormatRule) Visit(node ast.Node) ast.Visitor {

View File

@ -18,21 +18,24 @@ type StructTagRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *StructTagRule) configure(arguments lint.Arguments) { func (r *StructTagRule) configure(arguments lint.Arguments) error {
if len(arguments) == 0 { if len(arguments) == 0 {
return return nil
} }
checkNumberOfArguments(1, arguments, r.Name()) err := checkNumberOfArguments(1, arguments, r.Name())
if err != nil {
return err
}
r.userDefined = make(map[string][]string, len(arguments)) r.userDefined = make(map[string][]string, len(arguments))
for _, arg := range arguments { for _, arg := range arguments {
item, ok := arg.(string) item, ok := arg.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the %s rule. Expecting a string, got %v (of type %T)", r.Name(), arg, arg)) return fmt.Errorf("invalid argument to the %s rule. Expecting a string, got %v (of type %T)", r.Name(), arg, arg)
} }
parts := strings.Split(item, ",") parts := strings.Split(item, ",")
if len(parts) < 2 { if len(parts) < 2 {
panic(fmt.Sprintf("Invalid argument to the %s rule. Expecting a string of the form key[,option]+, got %s", r.Name(), item)) return fmt.Errorf("invalid argument to the %s rule. Expecting a string of the form key[,option]+, got %s", r.Name(), item)
} }
key := strings.TrimSpace(parts[0]) key := strings.TrimSpace(parts[0])
for i := 1; i < len(parts); i++ { for i := 1; i < len(parts); i++ {
@ -40,11 +43,17 @@ func (r *StructTagRule) configure(arguments lint.Arguments) {
r.userDefined[key] = append(r.userDefined[key], option) r.userDefined[key] = append(r.userDefined[key], option)
} }
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *StructTagRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *StructTagRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
onFailure := func(failure lint.Failure) { onFailure := func(failure lint.Failure) {

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"sync" "sync"
@ -20,14 +21,14 @@ type UncheckedTypeAssertionRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) { func (r *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) error {
if len(arguments) == 0 { if len(arguments) == 0 {
return return nil
} }
args, ok := arguments[0].(map[string]any) args, ok := arguments[0].(map[string]any)
if !ok { if !ok {
panic("Unable to get arguments. Expected object of key-value-pairs.") return errors.New("unable to get arguments. Expected object of key-value-pairs")
} }
for k, v := range args { for k, v := range args {
@ -35,17 +36,23 @@ func (r *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) {
case "acceptIgnoredAssertionResult": case "acceptIgnoredAssertionResult":
r.acceptIgnoredAssertionResult, ok = v.(bool) r.acceptIgnoredAssertionResult, ok = v.(bool)
if !ok { if !ok {
panic(fmt.Sprintf("Unable to parse argument '%s'. Expected boolean.", k)) return fmt.Errorf("unable to parse argument '%s'. Expected boolean", k)
} }
default: default:
panic(fmt.Sprintf("Unknown argument: %s", k)) return fmt.Errorf("unknown argument: %s", k)
} }
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *UncheckedTypeAssertionRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *UncheckedTypeAssertionRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -1,6 +1,7 @@
package rule package rule
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"go/types" "go/types"
@ -18,30 +19,36 @@ type UnhandledErrorRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *UnhandledErrorRule) configure(arguments lint.Arguments) { func (r *UnhandledErrorRule) configure(arguments lint.Arguments) error {
for _, arg := range arguments { for _, arg := range arguments {
argStr, ok := arg.(string) argStr, ok := arg.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)) return fmt.Errorf("invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)
} }
argStr = strings.Trim(argStr, " ") argStr = strings.Trim(argStr, " ")
if argStr == "" { if argStr == "" {
panic("Invalid argument to the unhandled-error rule, expected regular expression must not be empty.") return errors.New("invalid argument to the unhandled-error rule, expected regular expression must not be empty")
} }
exp, err := regexp.Compile(argStr) exp, err := regexp.Compile(argStr)
if err != nil { if err != nil {
panic(fmt.Sprintf("Invalid argument to the unhandled-error rule: regexp %q does not compile: %v", argStr, err)) return fmt.Errorf("invalid argument to the unhandled-error rule: regexp %q does not compile: %w", argStr, err)
} }
r.ignoreList = append(r.ignoreList, exp) r.ignoreList = append(r.ignoreList, exp)
} }
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *UnhandledErrorRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure

View File

@ -20,20 +20,20 @@ type UnusedParamRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *UnusedParamRule) configure(args lint.Arguments) { func (r *UnusedParamRule) configure(args lint.Arguments) error {
// while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives // while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives
// it's more compatible to JSON nature of configurations // it's more compatible to JSON nature of configurations
r.allowRegex = allowBlankIdentifierRegex r.allowRegex = allowBlankIdentifierRegex
r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it as _" r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it as _"
if len(args) == 0 { if len(args) == 0 {
return return nil
} }
// Arguments = [{}] // Arguments = [{}]
options := args[0].(map[string]any) options := args[0].(map[string]any)
allowRegexParam, ok := options["allowRegex"] allowRegexParam, ok := options["allowRegex"]
if !ok { if !ok {
return return nil
} }
// Arguments = [{allowRegex="^_"}] // Arguments = [{allowRegex="^_"}]
allowRegexStr, ok := allowRegexParam.(string) allowRegexStr, ok := allowRegexParam.(string)
@ -43,14 +43,21 @@ func (r *UnusedParamRule) configure(args lint.Arguments) {
var err error var err error
r.allowRegex, err = regexp.Compile(allowRegexStr) r.allowRegex, err = regexp.Compile(allowRegexStr)
if err != nil { if err != nil {
panic(fmt.Errorf("error configuring %s rule: allowRegex is not valid regex [%s]: %v", r.Name(), allowRegexStr, err)) return fmt.Errorf("error configuring %s rule: allowRegex is not valid regex [%s]: %w", r.Name(), allowRegexStr, err)
} }
r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it to match " + r.allowRegex.String() r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it to match " + r.allowRegex.String()
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *UnusedParamRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *UnusedParamRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
onFailure := func(failure lint.Failure) { onFailure := func(failure lint.Failure) {

View File

@ -18,20 +18,20 @@ type UnusedReceiverRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *UnusedReceiverRule) configure(args lint.Arguments) { func (r *UnusedReceiverRule) configure(args lint.Arguments) error {
// while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives // while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives
// it's more compatible to JSON nature of configurations // it's more compatible to JSON nature of configurations
r.allowRegex = allowBlankIdentifierRegex r.allowRegex = allowBlankIdentifierRegex
r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _" r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _"
if len(args) == 0 { if len(args) == 0 {
return return nil
} }
// Arguments = [{}] // Arguments = [{}]
options := args[0].(map[string]any) options := args[0].(map[string]any)
allowRegexParam, ok := options["allowRegex"] allowRegexParam, ok := options["allowRegex"]
if !ok { if !ok {
return return nil
} }
// Arguments = [{allowRegex="^_"}] // Arguments = [{allowRegex="^_"}]
allowRegexStr, ok := allowRegexParam.(string) allowRegexStr, ok := allowRegexParam.(string)
@ -41,14 +41,21 @@ func (r *UnusedReceiverRule) configure(args lint.Arguments) {
var err error var err error
r.allowRegex, err = regexp.Compile(allowRegexStr) r.allowRegex, err = regexp.Compile(allowRegexStr)
if err != nil { if err != nil {
panic(fmt.Errorf("error configuring [unused-receiver] rule: allowRegex is not valid regex [%s]: %v", allowRegexStr, err)) return fmt.Errorf("error configuring [unused-receiver] rule: allowRegex is not valid regex [%s]: %w", allowRegexStr, err)
} }
r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match " + r.allowRegex.String() r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match " + r.allowRegex.String()
return nil
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *UnusedReceiverRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *UnusedReceiverRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
for _, decl := range file.AST.Decls { for _, decl := range file.AST.Decls {

View File

@ -105,10 +105,11 @@ func gofmt(x any) string {
} }
// checkNumberOfArguments fails if the given number of arguments is not, at least, the expected one // checkNumberOfArguments fails if the given number of arguments is not, at least, the expected one
func checkNumberOfArguments(expected int, args lint.Arguments, ruleName string) { func checkNumberOfArguments(expected int, args lint.Arguments, ruleName string) error {
if len(args) < expected { if len(args) < expected {
panic(fmt.Sprintf("not enough arguments for %s rule, expected %d, got %d. Please check the rule's documentation", ruleName, expected, len(args))) return fmt.Errorf("not enough arguments for %s rule, expected %d, got %d. Please check the rule's documentation", ruleName, expected, len(args))
} }
return nil
} }
var directiveCommentRE = regexp.MustCompile("^//(line |extern |export |[a-z0-9]+:[a-z0-9])") // see https://go-review.googlesource.com/c/website/+/442516/1..2/_content/doc/comment.md#494 var directiveCommentRE = regexp.MustCompile("^//(line |extern |export |[a-z0-9]+:[a-z0-9])") // see https://go-review.googlesource.com/c/website/+/442516/1..2/_content/doc/comment.md#494
@ -121,3 +122,8 @@ func isDirectiveComment(line string) bool {
func isCallToExitFunction(pkgName, functionName string) bool { func isCallToExitFunction(pkgName, functionName string) bool {
return exitFunctions[pkgName] != nil && exitFunctions[pkgName][functionName] return exitFunctions[pkgName] != nil && exitFunctions[pkgName][functionName]
} }
// newInternalFailureError returns an slice of Failure with a single internal failure in it
func newInternalFailureError(e error) []lint.Failure {
return []lint.Failure{lint.NewInternalFailure(e.Error())}
}

View File

@ -33,13 +33,21 @@ type VarNamingRule struct {
configureOnce sync.Once configureOnce sync.Once
} }
func (r *VarNamingRule) configure(arguments lint.Arguments) { func (r *VarNamingRule) configure(arguments lint.Arguments) error {
if len(arguments) >= 1 { if len(arguments) >= 1 {
r.allowList = getList(arguments[0], "allowlist") list, err := getList(arguments[0], "allowlist")
if err != nil {
return err
}
r.allowList = list
} }
if len(arguments) >= 2 { if len(arguments) >= 2 {
r.blockList = getList(arguments[1], "blocklist") list, err := getList(arguments[1], "blocklist")
if err != nil {
return err
}
r.blockList = list
} }
if len(arguments) >= 3 { if len(arguments) >= 3 {
@ -47,18 +55,19 @@ func (r *VarNamingRule) configure(arguments lint.Arguments) {
thirdArgument := arguments[2] thirdArgument := arguments[2]
asSlice, ok := thirdArgument.([]any) asSlice, ok := thirdArgument.([]any)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, got %T", "options", arguments[2])) return fmt.Errorf("invalid third argument to the var-naming rule. Expecting a %s of type slice, got %T", "options", arguments[2])
} }
if len(asSlice) != 1 { if len(asSlice) != 1 {
panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, but %d", "options", len(asSlice))) return fmt.Errorf("invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, but %d", "options", len(asSlice))
} }
args, ok := asSlice[0].(map[string]any) args, ok := asSlice[0].(map[string]any)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, with map, but %T", "options", asSlice[0])) return fmt.Errorf("invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, with map, but %T", "options", asSlice[0])
} }
r.allowUpperCaseConst = fmt.Sprint(args["upperCaseConst"]) == "true" r.allowUpperCaseConst = fmt.Sprint(args["upperCaseConst"]) == "true"
r.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true" r.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true"
} }
return nil
} }
func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) { func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) {
@ -83,7 +92,12 @@ func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) {
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(arguments) }) var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure var failures []lint.Failure
@ -263,18 +277,18 @@ func (w *lintNames) Visit(n ast.Node) ast.Visitor {
return w return w
} }
func getList(arg any, argName string) []string { func getList(arg any, argName string) ([]string, error) {
args, ok := arg.([]any) args, ok := arg.([]any)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid argument to the var-naming rule. Expecting a %s of type slice with initialisms, got %T", argName, arg)) return nil, fmt.Errorf("invalid argument to the var-naming rule. Expecting a %s of type slice with initialisms, got %T", argName, arg)
} }
var list []string var list []string
for _, v := range args { for _, v := range args {
val, ok := v.(string) val, ok := v.(string)
if !ok { if !ok {
panic(fmt.Sprintf("Invalid %s values of the var-naming rule. Expecting slice of strings but got element of type %T", val, arg)) return nil, fmt.Errorf("invalid %s values of the var-naming rule. Expecting slice of strings but got element of type %T", val, arg)
} }
list = append(list, val) list = append(list, val)
} }
return list return list, nil
} }