github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/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 "golang.org/x/tools/go/analysis" 10 11 "github.com/vanstinator/golangci-lint/pkg/config" 12 "github.com/vanstinator/golangci-lint/pkg/fsutils" 13 "github.com/vanstinator/golangci-lint/pkg/golinters/goanalysis" 14 "github.com/vanstinator/golangci-lint/pkg/lint/linter" 15 "github.com/vanstinator/golangci-lint/pkg/result" 16 ) 17 18 const duplName = "dupl" 19 20 //nolint:dupl 21 func NewDupl(settings *config.DuplSettings) *goanalysis.Linter { 22 var mu sync.Mutex 23 var resIssues []goanalysis.Issue 24 25 analyzer := &analysis.Analyzer{ 26 Name: duplName, 27 Doc: goanalysis.TheOnlyanalyzerDoc, 28 Run: func(pass *analysis.Pass) (any, error) { 29 issues, err := runDupl(pass, settings) 30 if err != nil { 31 return nil, err 32 } 33 34 if len(issues) == 0 { 35 return nil, nil 36 } 37 38 mu.Lock() 39 resIssues = append(resIssues, issues...) 40 mu.Unlock() 41 42 return nil, nil 43 }, 44 } 45 46 return goanalysis.NewLinter( 47 duplName, 48 "Tool for code clone detection", 49 []*analysis.Analyzer{analyzer}, 50 nil, 51 ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { 52 return resIssues 53 }).WithLoadMode(goanalysis.LoadModeSyntax) 54 } 55 56 func runDupl(pass *analysis.Pass, settings *config.DuplSettings) ([]goanalysis.Issue, error) { 57 fileNames := getFileNames(pass) 58 59 issues, err := duplAPI.Run(fileNames, settings.Threshold) 60 if err != nil { 61 return nil, err 62 } 63 64 if len(issues) == 0 { 65 return nil, nil 66 } 67 68 res := make([]goanalysis.Issue, 0, len(issues)) 69 70 for _, i := range issues { 71 toFilename, err := fsutils.ShortestRelPath(i.To.Filename(), "") 72 if err != nil { 73 return nil, fmt.Errorf("failed to get shortest rel path for %q: %w", i.To.Filename(), err) 74 } 75 76 dupl := fmt.Sprintf("%s:%d-%d", toFilename, i.To.LineStart(), i.To.LineEnd()) 77 text := fmt.Sprintf("%d-%d lines are duplicate of %s", 78 i.From.LineStart(), i.From.LineEnd(), 79 formatCode(dupl, nil)) 80 81 res = append(res, goanalysis.NewIssue(&result.Issue{ 82 Pos: token.Position{ 83 Filename: i.From.Filename(), 84 Line: i.From.LineStart(), 85 }, 86 LineRange: &result.Range{ 87 From: i.From.LineStart(), 88 To: i.From.LineEnd(), 89 }, 90 Text: text, 91 FromLinter: duplName, 92 }, pass)) 93 } 94 95 return res, nil 96 }