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:
parent
10d9697cc2
commit
9b15f3fcb6
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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{
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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())}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user