mirror of
				https://github.com/mgechev/revive.git
				synced 2025-10-30 23:37:49 +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:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						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 | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user