woodpecker/vendor/github.com/golangci/dupl/main.go
Lukas c28f7cb29f
Add golangci-lint (#502)
Initial part of #435
2021-11-14 21:01:54 +01:00

148 lines
3.3 KiB
Go

package dupl
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"github.com/golangci/dupl/job"
"github.com/golangci/dupl/printer"
"github.com/golangci/dupl/syntax"
)
const defaultThreshold = 15
var (
paths = []string{"."}
vendor = flag.Bool("dupl.vendor", false, "")
verbose = flag.Bool("dupl.verbose", false, "")
files = flag.Bool("dupl.files", false, "")
html = flag.Bool("dupl.html", false, "")
plumbing = flag.Bool("dupl.plumbing", false, "")
)
const (
vendorDirPrefix = "vendor" + string(filepath.Separator)
vendorDirInPath = string(filepath.Separator) + vendorDirPrefix
)
func init() {
flag.BoolVar(verbose, "dupl.v", false, "alias for -verbose")
}
func Run(files []string, threshold int) ([]printer.Issue, error) {
fchan := make(chan string, 1024)
go func() {
for _, f := range files {
fchan <- f
}
close(fchan)
}()
schan := job.Parse(fchan)
t, data, done := job.BuildTree(schan)
<-done
// finish stream
t.Update(&syntax.Node{Type: -1})
mchan := t.FindDuplOver(threshold)
duplChan := make(chan syntax.Match)
go func() {
for m := range mchan {
match := syntax.FindSyntaxUnits(*data, m, threshold)
if len(match.Frags) > 0 {
duplChan <- match
}
}
close(duplChan)
}()
return makeIssues(duplChan)
}
func makeIssues(duplChan <-chan syntax.Match) ([]printer.Issue, error) {
groups := make(map[string][][]*syntax.Node)
for dupl := range duplChan {
groups[dupl.Hash] = append(groups[dupl.Hash], dupl.Frags...)
}
keys := make([]string, 0, len(groups))
for k := range groups {
keys = append(keys, k)
}
sort.Strings(keys)
p := printer.NewPlumbing(ioutil.ReadFile)
var issues []printer.Issue
for _, k := range keys {
uniq := unique(groups[k])
if len(uniq) > 1 {
i, err := p.MakeIssues(uniq)
if err != nil {
return nil, err
}
issues = append(issues, i...)
}
}
return issues, nil
}
func unique(group [][]*syntax.Node) [][]*syntax.Node {
fileMap := make(map[string]map[int]struct{})
var newGroup [][]*syntax.Node
for _, seq := range group {
node := seq[0]
file, ok := fileMap[node.Filename]
if !ok {
file = make(map[int]struct{})
fileMap[node.Filename] = file
}
if _, ok := file[node.Pos]; !ok {
file[node.Pos] = struct{}{}
newGroup = append(newGroup, seq)
}
}
return newGroup
}
func usage() {
fmt.Fprintln(os.Stderr, `Usage: dupl [flags] [paths]
Paths:
If the given path is a file, dupl will use it regardless of
the file extension. If it is a directory, it will recursively
search for *.go files in that directory.
If no path is given, dupl will recursively search for *.go
files in the current directory.
Flags:
-files
read file names from stdin one at each line
-html
output the results as HTML, including duplicate code fragments
-plumbing
plumbing (easy-to-parse) output for consumption by scripts or tools
-t, -threshold size
minimum token sequence size as a clone (default 15)
-vendor
check files in vendor directory
-v, -verbose
explain what is being done
Examples:
dupl -t 100
Search clones in the current directory of size at least
100 tokens.
dupl $(find app/ -name '*_test.go')
Search for clones in tests in the app directory.
find app/ -name '*_test.go' |dupl -files
The same as above.`)
os.Exit(2)
}