github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/golinters/goconst.go (about)

     1  package golinters
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	goconstAPI "github.com/jgautheron/goconst"
     8  	"golang.org/x/tools/go/analysis"
     9  
    10  	"github.com/elek/golangci-lint/pkg/golinters/goanalysis"
    11  	"github.com/elek/golangci-lint/pkg/lint/linter"
    12  	"github.com/elek/golangci-lint/pkg/result"
    13  )
    14  
    15  const goconstName = "goconst"
    16  
    17  func NewGoconst() *goanalysis.Linter {
    18  	var mu sync.Mutex
    19  	var resIssues []goanalysis.Issue
    20  
    21  	analyzer := &analysis.Analyzer{
    22  		Name: goconstName,
    23  		Doc:  goanalysis.TheOnlyanalyzerDoc,
    24  	}
    25  	return goanalysis.NewLinter(
    26  		goconstName,
    27  		"Finds repeated strings that could be replaced by a constant",
    28  		[]*analysis.Analyzer{analyzer},
    29  		nil,
    30  	).WithContextSetter(func(lintCtx *linter.Context) {
    31  		analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
    32  			issues, err := checkConstants(pass, lintCtx)
    33  			if err != nil || len(issues) == 0 {
    34  				return nil, err
    35  			}
    36  
    37  			mu.Lock()
    38  			resIssues = append(resIssues, issues...)
    39  			mu.Unlock()
    40  
    41  			return nil, nil
    42  		}
    43  	}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
    44  		return resIssues
    45  	}).WithLoadMode(goanalysis.LoadModeSyntax)
    46  }
    47  
    48  func checkConstants(pass *analysis.Pass, lintCtx *linter.Context) ([]goanalysis.Issue, error) {
    49  	settings := lintCtx.Settings().Goconst
    50  
    51  	cfg := goconstAPI.Config{
    52  		IgnoreTests:        settings.IgnoreTests,
    53  		MatchWithConstants: settings.MatchWithConstants,
    54  		MinStringLength:    settings.MinStringLen,
    55  		MinOccurrences:     settings.MinOccurrencesCount,
    56  		ParseNumbers:       settings.ParseNumbers,
    57  		NumberMin:          settings.NumberMin,
    58  		NumberMax:          settings.NumberMax,
    59  		ExcludeTypes:       map[goconstAPI.Type]bool{},
    60  	}
    61  
    62  	if settings.IgnoreCalls {
    63  		cfg.ExcludeTypes[goconstAPI.Call] = true
    64  	}
    65  
    66  	goconstIssues, err := goconstAPI.Run(pass.Files, pass.Fset, &cfg)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	if len(goconstIssues) == 0 {
    72  		return nil, nil
    73  	}
    74  
    75  	res := make([]goanalysis.Issue, 0, len(goconstIssues))
    76  	for _, i := range goconstIssues {
    77  		textBegin := fmt.Sprintf("string %s has %d occurrences", formatCode(i.Str, lintCtx.Cfg), i.OccurrencesCount)
    78  		var textEnd string
    79  		if i.MatchingConst == "" {
    80  			textEnd = ", make it a constant"
    81  		} else {
    82  			textEnd = fmt.Sprintf(", but such constant %s already exists", formatCode(i.MatchingConst, lintCtx.Cfg))
    83  		}
    84  		res = append(res, goanalysis.NewIssue(&result.Issue{
    85  			Pos:        i.Pos,
    86  			Text:       textBegin + textEnd,
    87  			FromLinter: goconstName,
    88  		}, pass))
    89  	}
    90  
    91  	return res, nil
    92  }