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  }