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 {
go func(pkg []string, gover *goversion.Version) {
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)
}
wg.Done()

View File

@ -1,6 +1,7 @@
package rule
import (
"errors"
"fmt"
"go/ast"
"regexp"
@ -45,7 +46,7 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return []lint.Failure{lint.NewInternalFailure(configureErr.Error())}
return newInternalFailureError(configureErr)
}
var failures []lint.Failure
@ -232,35 +233,35 @@ func (r *AddConstantRule) configure(arguments lint.Arguments) error {
}
list, ok := v.(string)
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)
case "maxLitCount":
sl, ok := v.(string)
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)
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
case "ignoreFuncs":
excludes, ok := v.(string)
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, ",") {
exclude = strings.Trim(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)
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)

View File

@ -1,6 +1,7 @@
package rule
import (
"errors"
"fmt"
"go/ast"
"sync"
@ -17,22 +18,28 @@ type ArgumentsLimitRule struct {
const defaultArgumentsLimit = 8
func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) {
func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.max = defaultArgumentsLimit
return
return nil
}
maxArguments, ok := arguments[0].(int64) // Alt. non panicking version
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)
return nil
}
// Apply applies the rule to given file.
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

View File

@ -18,16 +18,30 @@ type BannedCharsRule struct {
const bannedCharsRuleName = "banned-characters"
func (r *BannedCharsRule) configure(arguments lint.Arguments) {
func (r *BannedCharsRule) configure(arguments lint.Arguments) error {
if len(arguments) > 0 {
checkNumberOfArguments(1, arguments, bannedCharsRuleName)
r.bannedCharList = r.getBannedCharsList(arguments)
err := checkNumberOfArguments(1, arguments, bannedCharsRuleName)
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.
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
onFailure := func(failure lint.Failure) {
@ -49,17 +63,17 @@ func (*BannedCharsRule) Name() string {
}
// 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
for _, char := range args {
charStr, ok := char.(string)
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)
}
return bannedChars
return bannedChars, nil
}
type lintBannedCharsRule struct {

View File

@ -19,23 +19,29 @@ type CognitiveComplexityRule struct {
const defaultMaxCognitiveComplexity = 7
func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) {
func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.maxComplexity = defaultMaxCognitiveComplexity
return
return nil
}
complexity, ok := arguments[0].(int64)
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)
return nil
}
// Apply applies the rule to given file.
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

View File

@ -16,20 +16,26 @@ type CommentSpacingsRule struct {
configureOnce sync.Once
}
func (r *CommentSpacingsRule) configure(arguments lint.Arguments) {
func (r *CommentSpacingsRule) configure(arguments lint.Arguments) error {
r.allowList = []string{}
for _, arg := range arguments {
allow, ok := arg.(string) // Alt. non panicking version
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)
}
return nil
}
// Apply the rule.
func (r *CommentSpacingsRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *CommentSpacingsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure

View File

@ -18,23 +18,29 @@ type CommentsDensityRule struct {
const defaultMinimumCommentsPercentage = 0
func (r *CommentsDensityRule) configure(arguments lint.Arguments) {
func (r *CommentsDensityRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.minimumCommentsDensity = defaultMinimumCommentsPercentage
return
return nil
}
var ok bool
r.minimumCommentsDensity, ok = arguments[0].(int64)
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.
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)
statementsCount := countStatements(file.AST)
density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100

View File

@ -17,8 +17,13 @@ type ContextAsArgumentRule struct {
}
// Apply applies the rule to given file.
func (r *ContextAsArgumentRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *ContextAsArgumentRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure
for _, decl := range file.AST.Decls {
@ -59,27 +64,32 @@ func (*ContextAsArgumentRule) Name() string {
return "context-as-argument"
}
func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) {
r.allowTypes = r.getAllowTypesFromArguments(arguments)
func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) error {
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{}
if len(args) >= 1 {
argKV, ok := args[0].(map[string]any)
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 {
switch k {
case "allowTypesBefore":
typesBefore, ok := v.(string)
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, ",")...)
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
return result
return result, nil
}

View File

@ -20,22 +20,28 @@ type CyclomaticRule struct {
const defaultMaxCyclomaticComplexity = 10
func (r *CyclomaticRule) configure(arguments lint.Arguments) {
func (r *CyclomaticRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.maxComplexity = defaultMaxCyclomaticComplexity
return
return nil
}
complexity, ok := arguments[0].(int64) // Alt. non panicking version
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)
return nil
}
// Apply applies the rule to given file.
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
for _, decl := range file.AST.Decls {

View File

@ -15,13 +15,23 @@ type DeferRule struct {
configureOnce sync.Once
}
func (r *DeferRule) configure(arguments lint.Arguments) {
r.allow = r.allowFromArgs(arguments)
func (r *DeferRule) configure(arguments lint.Arguments) error {
list, err := r.allowFromArgs(arguments)
if err != nil {
return err
}
r.allow = list
return nil
}
// Apply applies the rule to given file.
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
onFailure := func(failure lint.Failure) {
@ -39,7 +49,7 @@ func (*DeferRule) Name() string {
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 {
allow := map[string]bool{
"loop": true,
@ -50,24 +60,24 @@ func (*DeferRule) allowFromArgs(args lint.Arguments) map[string]bool {
"immediate-recover": true,
}
return allow
return allow, nil
}
aa, ok := args[0].([]any)
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))
for _, subcase := range aa {
sc, ok := subcase.(string)
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
}
return allow
return allow, nil
}
type lintDeferRule struct {

View File

@ -17,7 +17,12 @@ type DotImportsRule struct {
// Apply applies the rule to given file.
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
@ -41,30 +46,31 @@ func (*DotImportsRule) Name() string {
return "dot-imports"
}
func (r *DotImportsRule) configure(arguments lint.Arguments) {
func (r *DotImportsRule) configure(arguments lint.Arguments) error {
r.allowedPackages = allowPackages{}
if len(arguments) == 0 {
return
return nil
}
args, ok := arguments[0].(map[string]any)
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 {
pkgs, ok := allowedPkgArg.([]any)
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 {
pkg, ok := p.(string)
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)
}
}
return nil
}
type lintImports struct {

View File

@ -44,35 +44,40 @@ type EnforceMapStyleRule struct {
configureOnce sync.Once
}
func (r *EnforceMapStyleRule) configure(arguments lint.Arguments) {
func (r *EnforceMapStyleRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.enforceMapStyle = enforceMapStyleTypeAny
return
return nil
}
enforceMapStyle, ok := arguments[0].(string)
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
r.enforceMapStyle, err = mapStyleFromString(enforceMapStyle)
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.
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 {
// this linter is not configured
return nil
}
var failures []lint.Failure
astFile := file.AST
ast.Inspect(astFile, func(n ast.Node) bool {
switch v := n.(type) {

View File

@ -16,14 +16,14 @@ const (
enforceRepeatedArgTypeStyleTypeFull enforceRepeatedArgTypeStyleType = "full"
)
func repeatedArgTypeStyleFromString(s string) enforceRepeatedArgTypeStyleType {
func repeatedArgTypeStyleFromString(s string) (enforceRepeatedArgTypeStyleType, error) {
switch s {
case string(enforceRepeatedArgTypeStyleTypeAny), "":
return enforceRepeatedArgTypeStyleTypeAny
return enforceRepeatedArgTypeStyleTypeAny, nil
case string(enforceRepeatedArgTypeStyleTypeShort):
return enforceRepeatedArgTypeStyleTypeShort
return enforceRepeatedArgTypeStyleTypeShort, nil
case string(enforceRepeatedArgTypeStyleTypeFull):
return enforceRepeatedArgTypeStyleTypeFull
return enforceRepeatedArgTypeStyleTypeFull, nil
default:
err := fmt.Errorf(
"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
}
func (r *EnforceRepeatedArgTypeStyleRule) configure(arguments lint.Arguments) {
func (r *EnforceRepeatedArgTypeStyleRule) configure(arguments lint.Arguments) error {
r.funcArgStyle = enforceRepeatedArgTypeStyleTypeAny
r.funcRetValStyle = enforceRepeatedArgTypeStyleTypeAny
if len(arguments) == 0 {
return
return nil
}
switch funcArgStyle := arguments[0].(type) {
case string:
r.funcArgStyle = repeatedArgTypeStyleFromString(funcArgStyle)
r.funcRetValStyle = repeatedArgTypeStyleFromString(funcArgStyle)
argstyle, err := 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
for k, v := range funcArgStyle {
switch k {
case "funcArgStyle":
val, ok := v.(string)
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":
val, ok := v.(string)
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:
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:
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.
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 {
// This linter is not configured, return no failures.

View File

@ -48,27 +48,33 @@ type EnforceSliceStyleRule struct {
configureOnce sync.Once
}
func (r *EnforceSliceStyleRule) configure(arguments lint.Arguments) {
func (r *EnforceSliceStyleRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.enforceSliceStyle = enforceSliceStyleTypeAny
return
return nil
}
enforceSliceStyle, ok := arguments[0].(string)
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
r.enforceSliceStyle, err = sliceStyleFromString(enforceSliceStyle)
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.
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 {
// this linter is not configured

View File

@ -1,6 +1,7 @@
package rule
import (
"fmt"
"go/ast"
"go/token"
"strconv"
@ -19,7 +20,7 @@ type ErrorStringsRule struct {
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{}{
"fmt": {
"Errorf": {},
@ -46,15 +47,21 @@ func (r *ErrorStringsRule) configure(arguments lint.Arguments) {
}
}
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.
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
walker := lintErrorStrings{

View File

@ -70,7 +70,7 @@ type ExportedRule struct {
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.stuttersMsg = "stutters"
for _, flag := range arguments {
@ -96,17 +96,24 @@ func (r *ExportedRule) configure(arguments lint.Arguments) {
case "disableChecksOnVariables":
r.disabledChecks.Var = true
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:
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.
func (r *ExportedRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *ExportedRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure
if file.IsTest() {

View File

@ -20,21 +20,27 @@ var (
singleRegexp = regexp.MustCompile("^//")
)
func (r *FileHeaderRule) configure(arguments lint.Arguments) {
func (r *FileHeaderRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
return
return nil
}
var ok bool
r.header, ok = arguments[0].(string)
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.
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 == "" {
return nil
@ -69,7 +75,7 @@ func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint
regex, err := regexp.Compile(r.header)
if err != nil {
panic(err.Error())
return newInternalFailureError(err)
}
if !regex.MatchString(comment) {

View File

@ -26,7 +26,12 @@ type FileLengthLimitRule struct {
// Apply applies the rule to given file.
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 {
// 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 {
panic(err.Error())
return newInternalFailureError(err)
}
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 {
return // use default
return nil // use default
}
argKV, ok := arguments[0].(map[string]any)
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 {
switch k {
case "max":
maxLines, ok := v.(int64)
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)
case "skipComments":
skipComments, ok := v.(bool)
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
case "skipBlankLines":
skipBlankLines, ok := v.(bool)
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
}
}
return nil
}
// Name returns the rule name.

View File

@ -19,7 +19,12 @@ type FilenameFormatRule struct {
// Apply applies the rule to the given file.
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)
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$`)
func (r *FilenameFormatRule) configure(arguments lint.Arguments) {
func (r *FilenameFormatRule) configure(arguments lint.Arguments) error {
argsCount := len(arguments)
if argsCount == 0 {
r.format = defaultFormat
return
return nil
}
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]
str, ok := arg.(string)
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)
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
return nil
}

View File

@ -17,15 +17,24 @@ type FunctionLength struct {
configureOnce sync.Once
}
func (r *FunctionLength) configure(arguments lint.Arguments) {
maxStmt, maxLines := r.parseArguments(arguments)
func (r *FunctionLength) configure(arguments lint.Arguments) error {
maxStmt, maxLines, err := r.parseArguments(arguments)
if err != nil {
return err
}
r.maxStmt = int(maxStmt)
r.maxLines = int(maxLines)
return nil
}
// Apply applies the rule to given file.
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
for _, decl := range file.AST.Decls {
@ -74,33 +83,33 @@ func (*FunctionLength) Name() string {
const defaultFuncStmtsLimit = 50
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 {
return defaultFuncStmtsLimit, defaultFuncLinesLimit
return defaultFuncStmtsLimit, defaultFuncLinesLimit, nil
}
const minArguments = 2
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)
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 {
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)
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 {
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 {

View File

@ -1,6 +1,7 @@
package rule
import (
"errors"
"fmt"
"go/ast"
"sync"
@ -17,7 +18,12 @@ type FunctionResultsLimitRule struct {
// Apply applies the rule to given file.
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
for _, decl := range file.AST.Decls {
@ -53,19 +59,20 @@ func (*FunctionResultsLimitRule) Name() string {
const defaultResultsLimit = 3
func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) {
func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.max = defaultResultsLimit
return
return nil
}
maxResults, ok := arguments[0].(int64) // Alt. non panicking version
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 {
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)
return nil
}

View File

@ -20,38 +20,54 @@ const defaultImportAliasNamingAllowRule = "^[a-z][a-z0-9]{0,}$"
var defaultImportAliasNamingAllowRegexp = regexp.MustCompile(defaultImportAliasNamingAllowRule)
func (r *ImportAliasNamingRule) configure(arguments lint.Arguments) {
func (r *ImportAliasNamingRule) configure(arguments lint.Arguments) error {
if len(arguments) == 0 {
r.allowRegexp = defaultImportAliasNamingAllowRegexp
return
return nil
}
switch namingRule := arguments[0].(type) {
case string:
r.setAllowRule(namingRule)
err := r.setAllowRule(namingRule)
if err != nil {
return err
}
case map[string]any: // expecting map[string]string
for k, v := range namingRule {
switch k {
case "allowRegex":
r.setAllowRule(v)
err := r.setAllowRule(v)
if err != nil {
return err
}
case "denyRegex":
r.setDenyRule(v)
err := r.setDenyRule(v)
if err != nil {
return err
}
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:
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 {
r.allowRegexp = defaultImportAliasNamingAllowRegexp
}
return nil
}
// Apply applies the rule to given file.
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
@ -93,28 +109,30 @@ func (*ImportAliasNamingRule) Name() string {
return "import-alias-naming"
}
func (r *ImportAliasNamingRule) setAllowRule(value any) {
func (r *ImportAliasNamingRule) setAllowRule(value any) error {
namingRule, ok := value.(string)
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)
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
return nil
}
func (r *ImportAliasNamingRule) setDenyRule(value any) {
func (r *ImportAliasNamingRule) setDenyRule(value any) error {
namingRule, ok := value.(string)
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)
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
return nil
}

View File

@ -17,19 +17,20 @@ type ImportsBlocklistRule struct {
var replaceImportRegexp = regexp.MustCompile(`/?\*\*/?`)
func (r *ImportsBlocklistRule) configure(arguments lint.Arguments) {
func (r *ImportsBlocklistRule) configure(arguments lint.Arguments) error {
r.blocklist = []*regexp.Regexp{}
for _, arg := range arguments {
argStr, ok := arg.(string)
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)*`)))
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)
}
return nil
}
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.
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

View File

@ -3,6 +3,7 @@ package rule
import (
"bufio"
"bytes"
"errors"
"fmt"
"go/token"
"strings"
@ -21,23 +22,29 @@ type LineLengthLimitRule struct {
const defaultLineLengthLimit = 80
func (r *LineLengthLimitRule) configure(arguments lint.Arguments) {
func (r *LineLengthLimitRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
r.max = defaultLineLengthLimit
return
return nil
}
maxLength, ok := arguments[0].(int64) // Alt. non panicking version
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)
return nil
}
// Apply applies the rule to given file.
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

View File

@ -1,6 +1,7 @@
package rule
import (
"errors"
"fmt"
"go/ast"
"sync"
@ -19,7 +20,12 @@ const defaultMaxControlNesting = 5
// Apply applies the rule to given file.
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
@ -107,17 +113,21 @@ func (w *lintMaxControlNesting) walkControlledBlock(b ast.Node) {
w.nestingLevelAcc = oldNestingLevel
}
func (r *MaxControlNestingRule) configure(arguments lint.Arguments) {
func (r *MaxControlNestingRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
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
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
return nil
}

View File

@ -1,6 +1,7 @@
package rule
import (
"errors"
"fmt"
"go/ast"
"strings"
@ -18,24 +19,33 @@ type MaxPublicStructsRule struct {
const defaultMaxPublicStructs = 5
func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) {
func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
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
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
return nil
}
// Apply applies the rule to given file.
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

View File

@ -18,15 +18,15 @@ type ReceiverNamingRule struct {
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
if len(arguments) < 1 {
return
return nil
}
args, ok := arguments[0].(map[string]any)
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 {
@ -34,18 +34,24 @@ func (r *ReceiverNamingRule) configure(arguments lint.Arguments) {
case "maxLength":
value, ok := v.(int64)
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)
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.
func (r *ReceiverNamingRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *ReceiverNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
typeReceiver := map[string]string{}
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.parseArguments(arguments)
err := w.parseArguments(arguments)
if err != nil {
return newInternalFailureError(err)
}
ast.Walk(w, file.AST)
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)
go func() {
defer func() {
err := recover()
err := w.parseArguments(arguments)
c <- err
}()
w.parseArguments(arguments)
}()
err := <-c
if err != nil {
@ -81,9 +84,12 @@ const identRegex = "[_A-Za-z][_A-Za-z0-9]*"
var parseStringFormatScope = regexp.MustCompile(
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 {
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{
parent: w,
scopes: scopes,
@ -92,30 +98,31 @@ func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) {
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
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 {
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))
for i, obj := range g {
val, ok := obj.(string)
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
}
// Validate scope and regex length
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 {
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
@ -126,24 +133,24 @@ func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes s
rawScope = strings.TrimSpace(rawScope)
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{}
matches := parseStringFormatScope.FindStringSubmatch(rawScope)
if matches == nil {
// 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 {
// 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]
if len(matches[2]) > 0 {
var err error
scope.argument, err = strconv.Atoi(matches[2])
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 {
@ -159,31 +166,31 @@ func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes s
if negated {
offset++
}
regex, err := regexp.Compile(rule[1][offset : len(rule[1])-1])
if err != nil {
w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1)
regex, errr := regexp.Compile(rule[1][offset : len(rule[1])-1])
if errr != nil {
return stringFormatSubruleScopes{}, regex, false, "", w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1)
}
// Use custom error message if provided
if len(rule) == 3 {
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
func (lintStringFormatRule) configError(msg string, ruleNum, option int) {
panic(fmt.Sprintf("invalid configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option))
func (lintStringFormatRule) configError(msg string, ruleNum, option int) error {
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
func (lintStringFormatRule) parseError(msg string, ruleNum, option int) {
panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option))
func (lintStringFormatRule) parseError(msg string, ruleNum, option int) error {
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
func (lintStringFormatRule) parseScopeError(msg string, ruleNum, option, scopeNum int) {
panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d, scope index %d]", msg, ruleNum, option, scopeNum))
func (lintStringFormatRule) parseScopeError(msg string, ruleNum, option, scopeNum int) error {
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 {

View File

@ -18,21 +18,24 @@ type StructTagRule struct {
configureOnce sync.Once
}
func (r *StructTagRule) configure(arguments lint.Arguments) {
func (r *StructTagRule) configure(arguments lint.Arguments) error {
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))
for _, arg := range arguments {
item, ok := arg.(string)
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, ",")
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])
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)
}
}
return nil
}
// Apply applies the rule to given file.
func (r *StructTagRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *StructTagRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure
onFailure := func(failure lint.Failure) {

View File

@ -1,6 +1,7 @@
package rule
import (
"errors"
"fmt"
"go/ast"
"sync"
@ -20,14 +21,14 @@ type UncheckedTypeAssertionRule struct {
configureOnce sync.Once
}
func (r *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) {
func (r *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) error {
if len(arguments) == 0 {
return
return nil
}
args, ok := arguments[0].(map[string]any)
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 {
@ -35,17 +36,23 @@ func (r *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) {
case "acceptIgnoredAssertionResult":
r.acceptIgnoredAssertionResult, ok = v.(bool)
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:
panic(fmt.Sprintf("Unknown argument: %s", k))
return fmt.Errorf("unknown argument: %s", k)
}
}
return nil
}
// Apply applies the rule to given file.
func (r *UncheckedTypeAssertionRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *UncheckedTypeAssertionRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure

View File

@ -1,6 +1,7 @@
package rule
import (
"errors"
"fmt"
"go/ast"
"go/types"
@ -18,30 +19,36 @@ type UnhandledErrorRule struct {
configureOnce sync.Once
}
func (r *UnhandledErrorRule) configure(arguments lint.Arguments) {
func (r *UnhandledErrorRule) configure(arguments lint.Arguments) error {
for _, arg := range arguments {
argStr, ok := arg.(string)
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, " ")
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)
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)
}
return nil
}
// Apply applies the rule to given file.
func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *UnhandledErrorRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure

View File

@ -20,20 +20,20 @@ type UnusedParamRule struct {
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
// it's more compatible to JSON nature of configurations
r.allowRegex = allowBlankIdentifierRegex
r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it as _"
if len(args) == 0 {
return
return nil
}
// Arguments = [{}]
options := args[0].(map[string]any)
allowRegexParam, ok := options["allowRegex"]
if !ok {
return
return nil
}
// Arguments = [{allowRegex="^_"}]
allowRegexStr, ok := allowRegexParam.(string)
@ -43,14 +43,21 @@ func (r *UnusedParamRule) configure(args lint.Arguments) {
var err error
r.allowRegex, err = regexp.Compile(allowRegexStr)
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()
return nil
}
// Apply applies the rule to given file.
func (r *UnusedParamRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *UnusedParamRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure
onFailure := func(failure lint.Failure) {

View File

@ -18,20 +18,20 @@ type UnusedReceiverRule struct {
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
// it's more compatible to JSON nature of configurations
r.allowRegex = allowBlankIdentifierRegex
r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _"
if len(args) == 0 {
return
return nil
}
// Arguments = [{}]
options := args[0].(map[string]any)
allowRegexParam, ok := options["allowRegex"]
if !ok {
return
return nil
}
// Arguments = [{allowRegex="^_"}]
allowRegexStr, ok := allowRegexParam.(string)
@ -41,14 +41,21 @@ func (r *UnusedReceiverRule) configure(args lint.Arguments) {
var err error
r.allowRegex, err = regexp.Compile(allowRegexStr)
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()
return nil
}
// Apply applies the rule to given file.
func (r *UnusedReceiverRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
r.configureOnce.Do(func() { r.configure(args) })
func (r *UnusedReceiverRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var configureErr error
r.configureOnce.Do(func() { configureErr = r.configure(arguments) })
if configureErr != nil {
return newInternalFailureError(configureErr)
}
var failures []lint.Failure
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
func checkNumberOfArguments(expected int, args lint.Arguments, ruleName string) {
func checkNumberOfArguments(expected int, args lint.Arguments, ruleName string) error {
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
@ -121,3 +122,8 @@ func isDirectiveComment(line string) bool {
func isCallToExitFunction(pkgName, functionName string) bool {
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
}
func (r *VarNamingRule) configure(arguments lint.Arguments) {
func (r *VarNamingRule) configure(arguments lint.Arguments) error {
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 {
r.blockList = getList(arguments[1], "blocklist")
list, err := getList(arguments[1], "blocklist")
if err != nil {
return err
}
r.blockList = list
}
if len(arguments) >= 3 {
@ -47,18 +55,19 @@ func (r *VarNamingRule) configure(arguments lint.Arguments) {
thirdArgument := arguments[2]
asSlice, ok := thirdArgument.([]any)
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 {
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)
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.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true"
}
return nil
}
func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) {
@ -83,7 +92,12 @@ func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) {
// Apply applies the rule to given file.
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
@ -263,18 +277,18 @@ func (w *lintNames) Visit(n ast.Node) ast.Visitor {
return w
}
func getList(arg any, argName string) []string {
func getList(arg any, argName string) ([]string, error) {
args, ok := arg.([]any)
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
for _, v := range args {
val, ok := v.(string)
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)
}
return list
return list, nil
}