diff --git a/lint/failure.go b/lint/failure.go index 479b0cb48..17302a706 100644 --- a/lint/failure.go +++ b/lint/failure.go @@ -37,3 +37,18 @@ type Failure struct { func (f *Failure) GetFilename() string { return f.Position.Start.Filename } + +const internalFailure = "REVIVE_INTERNAL" + +// IsInternal returns true if this failure is internal, false otherwise. +func (f *Failure) IsInternal() bool { + return f.Category == internalFailure +} + +// NewInternalFailure yields an internal failure with the given message as failure message. +func NewInternalFailure(message string) Failure { + return Failure{ + Category: internalFailure, + Failure: message, + } +} diff --git a/lint/file.go b/lint/file.go index c695d63f5..4da078906 100644 --- a/lint/file.go +++ b/lint/file.go @@ -2,6 +2,7 @@ package lint import ( "bytes" + "errors" "go/ast" "go/parser" "go/printer" @@ -96,7 +97,7 @@ func (f *File) isMain() bool { const directiveSpecifyDisableReason = "specify-disable-reason" -func (f *File) lint(rules []Rule, config Config, failures chan Failure) { +func (f *File) lint(rules []Rule, config Config, failures chan Failure) error { rulesConfig := config.Rules _, mustSpecifyDisableReason := config.Directives[directiveSpecifyDisableReason] disabledIntervals := f.disabledIntervals(rules, mustSpecifyDisableReason, failures) @@ -107,6 +108,9 @@ func (f *File) lint(rules []Rule, config Config, failures chan Failure) { } currentFailures := currentRule.Apply(f, ruleConfig.Arguments) for idx, failure := range currentFailures { + if failure.IsInternal() { + return errors.New(failure.Failure) + } if failure.RuleName == "" { failure.RuleName = currentRule.Name() } @@ -122,6 +126,7 @@ func (f *File) lint(rules []Rule, config Config, failures chan Failure) { } } } + return nil } type enableDisableConfig struct { diff --git a/lint/linter.go b/lint/linter.go index 10f5cac36..0da195bb0 100644 --- a/lint/linter.go +++ b/lint/linter.go @@ -152,9 +152,7 @@ func (l *Linter) lintPackage(filenames []string, gover *goversion.Version, ruleS return nil } - pkg.lint(ruleSet, config, failures) - - return nil + return pkg.lint(ruleSet, config, failures) } func detectGoMod(dir string) (rootDir string, ver *goversion.Version, err error) { diff --git a/lint/package.go b/lint/package.go index 41575f480..78c8a802b 100644 --- a/lint/package.go +++ b/lint/package.go @@ -181,17 +181,34 @@ func (p *Package) scanSortable() { } } -func (p *Package) lint(rules []Rule, config Config, failures chan Failure) { +func (p *Package) lint(rules []Rule, config Config, failures chan Failure) error { p.scanSortable() var wg sync.WaitGroup + errChan := make(chan error) + doneChan := make(chan struct{}) + + wg.Add(len(p.files)) + go func() { // This goroutine will signal when all files where linted + wg.Wait() + doneChan <- struct{}{} + }() + for _, file := range p.files { - wg.Add(1) go (func(file *File) { - file.lint(rules, config, failures) + err := file.lint(rules, config, failures) + if err != nil { + errChan <- err // signal the error + } wg.Done() })(file) } - wg.Wait() + + select { // We block until... + case <-doneChan: //...all files were linted + return nil + case err := <-errChan: //...or there is an error + return err + } } // IsAtLeastGo121 returns true if the Go version for this package is 1.21 or higher, false otherwise diff --git a/rule/add_constant.go b/rule/add_constant.go index 5d7cd6095..4575cb4d8 100644 --- a/rule/add_constant.go +++ b/rule/add_constant.go @@ -42,7 +42,11 @@ type AddConstantRule struct { // Apply applies the rule to given file. func (r *AddConstantRule) 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 []lint.Failure{lint.NewInternalFailure(configureErr.Error())} + } var failures []lint.Failure @@ -201,15 +205,16 @@ func (w *lintAddConstantRule) isStructTag(n *ast.BasicLit) bool { return ok } -func (r *AddConstantRule) configure(arguments lint.Arguments) { +func (r *AddConstantRule) configure(arguments lint.Arguments) error { + println(">>>> configuring") r.strLitLimit = defaultStrLitLimit r.allowList = newAllowList() if len(arguments) == 0 { - return + return nil } args, ok := arguments[0].(map[string]any) if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting a k,v map. Got %T", arguments[0])) + return fmt.Errorf("invalid argument to the add-constant rule, expecting a k,v map. Got %T", arguments[0]) } for k, v := range args { kind := "" @@ -228,39 +233,41 @@ func (r *AddConstantRule) configure(arguments lint.Arguments) { } list, ok := v.(string) if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v)) + 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 { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v)) + 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 { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)) + 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 { - panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v)) + 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 == "" { - panic("Invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.") + fmt.Errorf("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 { - panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err)) + fmt.Errorf("invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err) } r.ignoreFunctions = append(r.ignoreFunctions, exp) } } } + + return nil }