1
0
mirror of https://github.com/mgechev/revive.git synced 2025-11-23 22:04:49 +02:00

refactor: enable gocritic linter; fix lint issues (#1375)

This commit is contained in:
Oleksandr Redko
2025-05-26 12:40:17 +03:00
committed by GitHub
parent 51ac5c2524
commit e8ed573739
17 changed files with 79 additions and 70 deletions

View File

@@ -6,6 +6,7 @@ version: "2"
linters: linters:
default: none default: none
enable: enable:
- gocritic
- govet - govet
- ineffassign - ineffassign
- misspell - misspell
@@ -15,6 +16,11 @@ linters:
- unused - unused
settings: settings:
gocritic:
enable-all: true
disabled-checks:
- hugeParam
- rangeValCopy
govet: govet:
enable-all: true enable-all: true
disable: disable:
@@ -73,3 +79,11 @@ linters:
- name: use-any - name: use-any
- name: var-declaration - name: var-declaration
- name: var-naming - name: var-naming
issues:
# Show all issues from a linter.
max-issues-per-linter: 0
# Show all issues with the same text.
max-same-issues: 0
# Show all issues for a line.
uniq-by-line: false

View File

@@ -190,7 +190,7 @@ func getVersion(builtBy, date, commit, version string) string {
bi, ok := debug.ReadBuildInfo() bi, ok := debug.ReadBuildInfo()
if ok { if ok {
version = strings.TrimPrefix(bi.Main.Version, "v") version = strings.TrimPrefix(bi.Main.Version, "v")
if len(buildInfo) == 0 { if buildInfo == "" {
return fmt.Sprintf("version %s\n", version) return fmt.Sprintf("version %s\n", version)
} }
} }

View File

@@ -25,13 +25,13 @@ func TestXDGConfigDirIsPreferredFirst(t *testing.T) {
xdgDirPath := filepath.FromSlash("/tmp-iofs/xdg/config") xdgDirPath := filepath.FromSlash("/tmp-iofs/xdg/config")
homeDirPath := filepath.FromSlash("/tmp-iofs/home/tester") homeDirPath := filepath.FromSlash("/tmp-iofs/home/tester")
AppFs.MkdirAll(xdgDirPath, 0755) AppFs.MkdirAll(xdgDirPath, 0o755)
AppFs.MkdirAll(homeDirPath, 0755) AppFs.MkdirAll(homeDirPath, 0o755)
afero.WriteFile(AppFs, filepath.Join(xdgDirPath, "revive.toml"), []byte("\n"), 0644) afero.WriteFile(AppFs, filepath.Join(xdgDirPath, "revive.toml"), []byte("\n"), 0o644)
t.Setenv("XDG_CONFIG_HOME", xdgDirPath) t.Setenv("XDG_CONFIG_HOME", xdgDirPath)
afero.WriteFile(AppFs, filepath.Join(homeDirPath, "revive.toml"), []byte("\n"), 0644) afero.WriteFile(AppFs, filepath.Join(homeDirPath, "revive.toml"), []byte("\n"), 0o644)
setHome(t, homeDirPath) setHome(t, homeDirPath)
got := buildDefaultConfigPath() got := buildDefaultConfigPath()
@@ -45,9 +45,9 @@ func TestXDGConfigDirIsPreferredFirst(t *testing.T) {
func TestHomeConfigDir(t *testing.T) { func TestHomeConfigDir(t *testing.T) {
t.Cleanup(func() { AppFs = afero.NewMemMapFs() }) t.Cleanup(func() { AppFs = afero.NewMemMapFs() })
homeDirPath := filepath.FromSlash("/tmp-iofs/home/tester") homeDirPath := filepath.FromSlash("/tmp-iofs/home/tester")
AppFs.MkdirAll(homeDirPath, 0755) AppFs.MkdirAll(homeDirPath, 0o755)
afero.WriteFile(AppFs, filepath.Join(homeDirPath, "revive.toml"), []byte("\n"), 0644) afero.WriteFile(AppFs, filepath.Join(homeDirPath, "revive.toml"), []byte("\n"), 0o644)
setHome(t, homeDirPath) setHome(t, homeDirPath)
got := buildDefaultConfigPath() got := buildDefaultConfigPath()
@@ -69,9 +69,9 @@ func setHome(t *testing.T, dir string) {
func TestXDGConfigDir(t *testing.T) { func TestXDGConfigDir(t *testing.T) {
t.Cleanup(func() { AppFs = afero.NewMemMapFs() }) t.Cleanup(func() { AppFs = afero.NewMemMapFs() })
xdgDirPath := filepath.FromSlash("/tmp-iofs/xdg/config") xdgDirPath := filepath.FromSlash("/tmp-iofs/xdg/config")
AppFs.MkdirAll(xdgDirPath, 0755) AppFs.MkdirAll(xdgDirPath, 0o755)
afero.WriteFile(AppFs, filepath.Join(xdgDirPath, "revive.toml"), []byte("\n"), 0644) afero.WriteFile(AppFs, filepath.Join(xdgDirPath, "revive.toml"), []byte("\n"), 0o644)
t.Setenv("XDG_CONFIG_HOME", xdgDirPath) t.Setenv("XDG_CONFIG_HOME", xdgDirPath)
got := buildDefaultConfigPath() got := buildDefaultConfigPath()

View File

@@ -222,7 +222,7 @@ func (f *File) disabledIntervals(rules []Rule, mustSpecifyDisableReason bool, fa
for _, name := range tempNames { for _, name := range tempNames {
name = strings.Trim(name, "\n") name = strings.Trim(name, "\n")
if len(name) > 0 { if name != "" {
ruleNames = append(ruleNames, name) ruleNames = append(ruleNames, name)
} }
} }

View File

@@ -31,7 +31,7 @@ func ParseFileFilter(rawFilter string) (*FileFilter, error) {
rawFilter = strings.TrimSpace(rawFilter) rawFilter = strings.TrimSpace(rawFilter)
result := new(FileFilter) result := new(FileFilter)
result.raw = rawFilter result.raw = rawFilter
result.matchesNothing = len(result.raw) == 0 result.matchesNothing = result.raw == ""
result.matchesAll = result.raw == "*" || result.raw == "~" result.matchesAll = result.raw == "*" || result.raw == "~"
if !result.matchesAll && !result.matchesNothing { if !result.matchesAll && !result.matchesNothing {
if err := result.prepareRegexp(); err != nil { if err := result.prepareRegexp(); err != nil {

View File

@@ -28,9 +28,10 @@ func Name(name string, allowlist, blocklist []string) (should string) {
w, i := 0, 0 // index of start of word, scan w, i := 0, 0 // index of start of word, scan
for i+1 <= len(runes) { for i+1 <= len(runes) {
eow := false // whether we hit the end of a word eow := false // whether we hit the end of a word
if i+1 == len(runes) { switch {
case i+1 == len(runes):
eow = true eow = true
} else if runes[i+1] == '_' { case runes[i+1] == '_':
// underscore; shift the remainder forward over any run of underscores // underscore; shift the remainder forward over any run of underscores
eow = true eow = true
n := 1 n := 1
@@ -45,7 +46,7 @@ func Name(name string, allowlist, blocklist []string) (should string) {
copy(runes[i+1:], runes[i+n+1:]) copy(runes[i+1:], runes[i+n+1:])
runes = runes[:len(runes)-n] runes = runes[:len(runes)-n]
} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) { case unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]):
// lower->non-lower // lower->non-lower
eow = true eow = true
} }

View File

@@ -17,18 +17,17 @@ import (
// Package represents a package in the project. // Package represents a package in the project.
type Package struct { type Package struct {
fset *token.FileSet fset *token.FileSet
mu sync.RWMutex
files map[string]*File files map[string]*File
goVersion *goversion.Version goVersion *goversion.Version
typesPkg *types.Package typesPkg *types.Package
typesInfo *types.Info typesInfo *types.Info
// sortable is the set of types in the package that implement sort.Interface. // sortable is the set of types in the package that implement sort.Interface.
sortable map[string]bool sortable map[string]bool
// main is whether this is a "main" package. // main is whether this is a "main" package.
main int main int
sync.RWMutex
} }
var ( var (
@@ -47,16 +46,16 @@ var (
// Files return package's files. // Files return package's files.
func (p *Package) Files() map[string]*File { func (p *Package) Files() map[string]*File {
p.RLock() p.mu.RLock()
defer p.RUnlock() defer p.mu.RUnlock()
return p.files return p.files
} }
// IsMain returns if that's the main package. // IsMain returns if that's the main package.
func (p *Package) IsMain() bool { func (p *Package) IsMain() bool {
p.Lock() p.mu.Lock()
defer p.Unlock() defer p.mu.Unlock()
switch p.main { switch p.main {
case trueValue: case trueValue:
@@ -76,32 +75,32 @@ func (p *Package) IsMain() bool {
// TypesPkg yields information on this package // TypesPkg yields information on this package
func (p *Package) TypesPkg() *types.Package { func (p *Package) TypesPkg() *types.Package {
p.RLock() p.mu.RLock()
defer p.RUnlock() defer p.mu.RUnlock()
return p.typesPkg return p.typesPkg
} }
// TypesInfo yields type information of this package identifiers // TypesInfo yields type information of this package identifiers
func (p *Package) TypesInfo() *types.Info { func (p *Package) TypesInfo() *types.Info {
p.RLock() p.mu.RLock()
defer p.RUnlock() defer p.mu.RUnlock()
return p.typesInfo return p.typesInfo
} }
// Sortable yields a map of sortable types in this package // Sortable yields a map of sortable types in this package
func (p *Package) Sortable() map[string]bool { func (p *Package) Sortable() map[string]bool {
p.RLock() p.mu.RLock()
defer p.RUnlock() defer p.mu.RUnlock()
return p.sortable return p.sortable
} }
// TypeCheck performs type checking for given package. // TypeCheck performs type checking for given package.
func (p *Package) TypeCheck() error { func (p *Package) TypeCheck() error {
p.Lock() p.mu.Lock()
defer p.Unlock() defer p.mu.Unlock()
alreadyTypeChecked := p.typesInfo != nil || p.typesPkg != nil alreadyTypeChecked := p.typesInfo != nil || p.typesPkg != nil
if alreadyTypeChecked { if alreadyTypeChecked {
@@ -157,8 +156,8 @@ func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.
// TypeOf returns the type of expression. // TypeOf returns the type of expression.
func (p *Package) TypeOf(expr ast.Expr) types.Type { func (p *Package) TypeOf(expr ast.Expr) types.Type {
p.RLock() p.mu.RLock()
defer p.RUnlock() defer p.mu.RUnlock()
if p.typesInfo == nil { if p.typesInfo == nil {
return nil return nil
@@ -177,8 +176,8 @@ const (
) )
func (p *Package) scanSortable() { func (p *Package) scanSortable() {
p.Lock() p.mu.Lock()
defer p.Unlock() defer p.mu.Unlock()
sortableFlags := map[string]sortableMethodsFlags{} sortableFlags := map[string]sortableMethodsFlags{}
for _, f := range p.files { for _, f := range p.files {
@@ -216,8 +215,8 @@ func (p *Package) lint(rules []Rule, config Config, failures chan Failure) error
// IsAtLeastGoVersion returns true if the Go version for this package is v or higher, false otherwise // IsAtLeastGoVersion returns true if the Go version for this package is v or higher, false otherwise
func (p *Package) IsAtLeastGoVersion(v *goversion.Version) bool { func (p *Package) IsAtLeastGoVersion(v *goversion.Version) bool {
p.RLock() p.mu.RLock()
defer p.RUnlock() defer p.mu.RUnlock()
return p.goVersion.GreaterThanOrEqual(v) return p.goVersion.GreaterThanOrEqual(v)
} }

View File

@@ -116,7 +116,7 @@ func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
func (r *Revive) Format( func (r *Revive) Format(
formatterName string, formatterName string,
failuresChan <-chan lint.Failure, failuresChan <-chan lint.Failure,
) (string, int, error) { ) (output string, exitCode int, err error) {
conf := r.config conf := r.config
formatChan := make(chan lint.Failure) formatChan := make(chan lint.Failure)
exitChan := make(chan bool) exitChan := make(chan bool)
@@ -126,19 +126,12 @@ func (r *Revive) Format(
return "", 0, fmt.Errorf("formatting - getting formatter: %w", err) return "", 0, fmt.Errorf("formatting - getting formatter: %w", err)
} }
var (
output string
formatErr error
)
go func() { go func() {
output, formatErr = formatter.Format(formatChan, *conf) output, err = formatter.Format(formatChan, *conf)
exitChan <- true exitChan <- true
}() }()
exitCode := 0
for failure := range failuresChan { for failure := range failuresChan {
if failure.Confidence < conf.Confidence { if failure.Confidence < conf.Confidence {
continue continue
@@ -162,8 +155,8 @@ func (r *Revive) Format(
close(formatChan) close(formatChan)
<-exitChan <-exitChan
if formatErr != nil { if err != nil {
return "", exitCode, fmt.Errorf("formatting: %w", formatErr) return "", exitCode, fmt.Errorf("formatting: %w", err)
} }
return output, exitCode, nil return output, exitCode, nil
@@ -188,7 +181,7 @@ func normalizeSplit(strs []string) []string {
for _, s := range strs { for _, s := range strs {
t := strings.Trim(s, " \t") t := strings.Trim(s, " \t")
if len(t) > 0 { if t != "" {
res = append(res, t) res = append(res, t)
} }
} }

View File

@@ -3,6 +3,7 @@ package rule
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"strconv"
"github.com/mgechev/revive/lint" "github.com/mgechev/revive/lint"
) )
@@ -94,7 +95,7 @@ func (w lintImports) Visit(_ ast.Node) ast.Visitor {
type allowPackages map[string]struct{} type allowPackages map[string]struct{}
func (ap allowPackages) add(pkg string) { func (ap allowPackages) add(pkg string) {
ap[fmt.Sprintf(`"%s"`, pkg)] = struct{}{} // import path strings are with double quotes ap[strconv.Quote(pkg)] = struct{}{} // import path strings are with double quotes
} }
func (ap allowPackages) isAllowedPackage(pkg string) bool { func (ap allowPackages) isAllowedPackage(pkg string) bool {

View File

@@ -15,6 +15,7 @@ type ImportAliasNamingRule struct {
const defaultImportAliasNamingAllowRule = "^[a-z][a-z0-9]{0,}$" const defaultImportAliasNamingAllowRule = "^[a-z][a-z0-9]{0,}$"
//nolint:gocritic // regexpSimplify: backward compatibility
var defaultImportAliasNamingAllowRegexp = regexp.MustCompile(defaultImportAliasNamingAllowRule) var defaultImportAliasNamingAllowRegexp = regexp.MustCompile(defaultImportAliasNamingAllowRule)
// Configure validates the rule configuration, and configures the rule accordingly. // Configure validates the rule configuration, and configures the rule accordingly.

View File

@@ -21,7 +21,7 @@ func TestImportAliasNamingRule_Configure(t *testing.T) {
name: "no arguments", name: "no arguments",
arguments: lint.Arguments{}, arguments: lint.Arguments{},
wantErr: nil, wantErr: nil,
wantAllowRegex: regexp.MustCompile("^[a-z][a-z0-9]{0,}$"), wantAllowRegex: regexp.MustCompile("^[a-z][a-z0-9]{0,}$"), //nolint:gocritic // regexpSimplify: backward compatibility
wantDenyRegex: nil, wantDenyRegex: nil,
}, },
{ {

View File

@@ -24,7 +24,7 @@ func (r *ImportsBlocklistRule) Configure(arguments lint.Arguments) error {
if !ok { if !ok {
return fmt.Errorf("invalid argument to the imports-blocklist rule. Expecting a string, got %T", arg) return fmt.Errorf("invalid argument to the imports-blocklist rule. Expecting a string, got %T", arg)
} }
regStr, err := regexp.Compile(fmt.Sprintf(`(?m)"%s"$`, replaceImportRegexp.ReplaceAllString(argStr, `(\W|\w)*`))) regStr, err := regexp.Compile(fmt.Sprintf(`(?m)"%s"$`, replaceImportRegexp.ReplaceAllString(argStr, `(\W|\w)*`))) //nolint:gocritic // regexpSimplify: false positive
if err != nil { if err != nil {
return fmt.Errorf("invalid argument to the imports-blocklist rule. Expecting %q to be a valid regular expression, got: %w", argStr, err) return fmt.Errorf("invalid argument to the imports-blocklist rule. Expecting %q to be a valid regular expression, got: %w", argStr, err)
} }

View File

@@ -23,7 +23,7 @@ func (*RedundantImportAlias) Apply(file *lint.File, _ lint.Arguments) []lint.Fai
if getImportPackageName(imp) == imp.Name.Name { if getImportPackageName(imp) == imp.Name.Name {
failures = append(failures, lint.Failure{ failures = append(failures, lint.Failure{
Confidence: 1, Confidence: 1,
Failure: fmt.Sprintf("Import alias \"%s\" is redundant", imp.Name.Name), Failure: fmt.Sprintf("Import alias %q is redundant", imp.Name.Name),
Node: imp, Node: imp,
Category: lint.FailureCategoryImports, Category: lint.FailureCategoryImports,
}) })

View File

@@ -118,7 +118,7 @@ func (r *StringFormatRule) parseArgument(argument any, ruleNum int) (scopes stri
for scopeNum, rawScope := range rawScopes { for scopeNum, rawScope := range rawScopes {
rawScope = strings.TrimSpace(rawScope) rawScope = strings.TrimSpace(rawScope)
if len(rawScope) == 0 { if rawScope == "" {
return stringFormatSubruleScopes{}, regex, false, "", r.parseScopeError("empty scope in rule scopes:", ruleNum, 0, scopeNum) return stringFormatSubruleScopes{}, regex, false, "", r.parseScopeError("empty scope in rule scopes:", ruleNum, 0, scopeNum)
} }
@@ -133,14 +133,14 @@ func (r *StringFormatRule) parseArgument(argument any, ruleNum int) (scopes stri
r.parseScopeError(fmt.Sprintf("unexpected number of submatches when parsing scope: %d, expected 4", len(matches)), ruleNum, 0, scopeNum) r.parseScopeError(fmt.Sprintf("unexpected number of submatches when parsing scope: %d, expected 4", len(matches)), ruleNum, 0, scopeNum)
} }
scope.funcName = matches[1] scope.funcName = matches[1]
if len(matches[2]) > 0 { if matches[2] != "" {
var err error var err error
scope.argument, err = strconv.Atoi(matches[2]) scope.argument, err = strconv.Atoi(matches[2])
if err != nil { if err != nil {
return stringFormatSubruleScopes{}, regex, false, "", r.parseScopeError("unable to parse argument number in rule scope", ruleNum, 0, scopeNum) return stringFormatSubruleScopes{}, regex, false, "", r.parseScopeError("unable to parse argument number in rule scope", ruleNum, 0, scopeNum)
} }
} }
if len(matches[3]) > 0 { if matches[3] != "" {
scope.field = matches[3] scope.field = matches[3]
} }
@@ -235,7 +235,7 @@ func (r *stringFormatSubrule) apply(call *ast.CallExpr, scope *stringFormatSubru
arg := call.Args[scope.argument] arg := call.Args[scope.argument]
var lit *ast.BasicLit var lit *ast.BasicLit
if len(scope.field) > 0 { if scope.field != "" {
// Try finding the scope's Field, treating arg as a composite literal // Try finding the scope's Field, treating arg as a composite literal
composite, ok := arg.(*ast.CompositeLit) composite, ok := arg.(*ast.CompositeLit)
if !ok { if !ok {
@@ -292,7 +292,7 @@ func (r *stringFormatSubrule) stringIsOK(s string) bool {
func (r *stringFormatSubrule) generateFailure(node ast.Node) { func (r *stringFormatSubrule) generateFailure(node ast.Node) {
var failure string var failure string
switch { switch {
case len(r.errorMessage) > 0: case r.errorMessage != "":
failure = r.errorMessage failure = r.errorMessage
case r.negated: case r.negated:
failure = fmt.Sprintf("string literal matches user defined regex /%s/", r.regexp.String()) failure = fmt.Sprintf("string literal matches user defined regex /%s/", r.regexp.String())

View File

@@ -235,7 +235,7 @@ func (lintStructTagRule) getTagName(tag *structtag.Tag) string {
} }
func checkASN1Tag(checkCtx *checkContext, tag *structtag.Tag, fieldType ast.Expr) (message string, succeeded bool) { func checkASN1Tag(checkCtx *checkContext, tag *structtag.Tag, fieldType ast.Expr) (message string, succeeded bool) {
checkList := append(tag.Options, tag.Name) checkList := slices.Concat(tag.Options, []string{tag.Name})
for _, opt := range checkList { for _, opt := range checkList {
switch opt { switch opt {
case "application", "explicit", "generalized", "ia5", "omitempty", "optional", "set", "utf8": case "application", "explicit", "generalized", "ia5", "omitempty", "optional", "set", "utf8":
@@ -605,7 +605,7 @@ func typeValueMatch(t ast.Expr, val string) bool {
return typeMatches return typeMatches
} }
func (w lintStructTagRule) addFailureWithTagKey(n ast.Node, msg string, tagKey string) { func (w lintStructTagRule) addFailureWithTagKey(n ast.Node, msg, tagKey string) {
w.addFailuref(n, "%s in %s tag", msg, tagKey) w.addFailuref(n, "%s in %s tag", msg, tagKey)
} }

View File

@@ -171,6 +171,15 @@ func (*VarNamingRule) pkgNameFailure(node ast.Node, msg string, args ...any) lin
} }
} }
type lintNames struct {
file *lint.File
fileAst *ast.File
onFailure func(lint.Failure)
allowList []string
blockList []string
upperCaseConst bool
}
func (w *lintNames) checkList(fl *ast.FieldList, thing string) { func (w *lintNames) checkList(fl *ast.FieldList, thing string) {
if fl == nil { if fl == nil {
return return
@@ -229,15 +238,6 @@ func (w *lintNames) check(id *ast.Ident, thing string) {
}) })
} }
type lintNames struct {
file *lint.File
fileAst *ast.File
onFailure func(lint.Failure)
allowList []string
blockList []string
upperCaseConst bool
}
func (w *lintNames) Visit(n ast.Node) ast.Visitor { func (w *lintNames) Visit(n ast.Node) ast.Visitor {
switch v := n.(type) { switch v := n.(type) {
case *ast.AssignStmt: case *ast.AssignStmt:
@@ -323,7 +323,7 @@ func (w *lintNames) Visit(n ast.Node) ast.Visitor {
// isUpperCaseConst checks if a string is in constant name format like `SOME_CONST`, `SOME_CONST_2`, `X123_3`, `_SOME_PRIVATE_CONST`. // isUpperCaseConst checks if a string is in constant name format like `SOME_CONST`, `SOME_CONST_2`, `X123_3`, `_SOME_PRIVATE_CONST`.
// See #851, #865. // See #851, #865.
func isUpperCaseConst(s string) bool { func isUpperCaseConst(s string) bool {
if len(s) == 0 { if s == "" {
return false return false
} }
r := []rune(s) r := []rune(s)

View File

@@ -197,7 +197,7 @@ func parseInstructions(t *testing.T, filename string, src []byte) []instruction
if i := strings.Index(line, "MATCH:"); i >= 0 { if i := strings.Index(line, "MATCH:"); i >= 0 {
// This is a match for a different line. // This is a match for a different line.
lns := strings.TrimPrefix(line[i:], "MATCH:") lns := strings.TrimPrefix(line[i:], "MATCH:")
lns = lns[:strings.Index(lns, " ")] lns = lns[:strings.Index(lns, " ")] //nolint:gocritic // offBy1: false positive
matchLine, err = strconv.Atoi(lns) matchLine, err = strconv.Atoi(lns)
if err != nil { if err != nil {
t.Fatalf("Bad match line number %q at %v:%d: %v", lns, filename, ln, err) t.Fatalf("Bad match line number %q at %v:%d: %v", lns, filename, ln, err)