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

     1  package golinters
     2  
     3  import (
     4  	"fmt"
     5  	"go/token"
     6  	"sync"
     7  
     8  	duplAPI "github.com/golangci/dupl"
     9  	"github.com/pkg/errors"
    10  	"golang.org/x/tools/go/analysis"
    11  
    12  	"github.com/elek/golangci-lint/pkg/fsutils"
    13  	"github.com/elek/golangci-lint/pkg/golinters/goanalysis"
    14  	"github.com/elek/golangci-lint/pkg/lint/linter"
    15  	"github.com/elek/golangci-lint/pkg/result"
    16  )
    17  
    18  const duplLinterName = "dupl"
    19  
    20  func NewDupl() *goanalysis.Linter {
    21  	var mu sync.Mutex
    22  	var resIssues []goanalysis.Issue
    23  
    24  	analyzer := &analysis.Analyzer{
    25  		Name: duplLinterName,
    26  		Doc:  goanalysis.TheOnlyanalyzerDoc,
    27  	}
    28  	return goanalysis.NewLinter(
    29  		duplLinterName,
    30  		"Tool for code clone detection",
    31  		[]*analysis.Analyzer{analyzer},
    32  		nil,
    33  	).WithContextSetter(func(lintCtx *linter.Context) {
    34  		analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
    35  			var fileNames []string
    36  			for _, f := range pass.Files {
    37  				pos := pass.Fset.PositionFor(f.Pos(), false)
    38  				fileNames = append(fileNames, pos.Filename)
    39  			}
    40  
    41  			issues, err := duplAPI.Run(fileNames, lintCtx.Settings().Dupl.Threshold)
    42  			if err != nil {
    43  				return nil, err
    44  			}
    45  
    46  			if len(issues) == 0 {
    47  				return nil, nil
    48  			}
    49  
    50  			res := make([]goanalysis.Issue, 0, len(issues))
    51  			for _, i := range issues {
    52  				toFilename, err := fsutils.ShortestRelPath(i.To.Filename(), "")
    53  				if err != nil {
    54  					return nil, errors.Wrapf(err, "failed to get shortest rel path for %q", i.To.Filename())
    55  				}
    56  				dupl := fmt.Sprintf("%s:%d-%d", toFilename, i.To.LineStart(), i.To.LineEnd())
    57  				text := fmt.Sprintf("%d-%d lines are duplicate of %s",
    58  					i.From.LineStart(), i.From.LineEnd(),
    59  					formatCode(dupl, lintCtx.Cfg))
    60  				res = append(res, goanalysis.NewIssue(&result.Issue{
    61  					Pos: token.Position{
    62  						Filename: i.From.Filename(),
    63  						Line:     i.From.LineStart(),
    64  					},
    65  					LineRange: &result.Range{
    66  						From: i.From.LineStart(),
    67  						To:   i.From.LineEnd(),
    68  					},
    69  					Text:       text,
    70  					FromLinter: duplLinterName,
    71  				}, pass))
    72  			}
    73  
    74  			mu.Lock()
    75  			resIssues = append(resIssues, res...)
    76  			mu.Unlock()
    77  
    78  			return nil, nil
    79  		}
    80  	}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
    81  		return resIssues
    82  	}).WithLoadMode(goanalysis.LoadModeSyntax)
    83  }