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

     1  package golinters
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/token"
     7  	"sync"
     8  
     9  	"golang.org/x/tools/go/analysis"
    10  
    11  	"github.com/elek/golangci-lint/pkg/golinters/goanalysis"
    12  	"github.com/elek/golangci-lint/pkg/lint/linter"
    13  	"github.com/elek/golangci-lint/pkg/result"
    14  )
    15  
    16  const dogsledLinterName = "dogsled"
    17  
    18  func NewDogsled() *goanalysis.Linter {
    19  	var mu sync.Mutex
    20  	var resIssues []goanalysis.Issue
    21  
    22  	analyzer := &analysis.Analyzer{
    23  		Name: dogsledLinterName,
    24  		Doc:  goanalysis.TheOnlyanalyzerDoc,
    25  	}
    26  	return goanalysis.NewLinter(
    27  		dogsledLinterName,
    28  		"Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())",
    29  		[]*analysis.Analyzer{analyzer},
    30  		nil,
    31  	).WithContextSetter(func(lintCtx *linter.Context) {
    32  		analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
    33  			var pkgIssues []goanalysis.Issue
    34  			for _, f := range pass.Files {
    35  				v := returnsVisitor{
    36  					maxBlanks: lintCtx.Settings().Dogsled.MaxBlankIdentifiers,
    37  					f:         pass.Fset,
    38  				}
    39  				ast.Walk(&v, f)
    40  				for i := range v.issues {
    41  					pkgIssues = append(pkgIssues, goanalysis.NewIssue(&v.issues[i], pass))
    42  				}
    43  			}
    44  
    45  			mu.Lock()
    46  			resIssues = append(resIssues, pkgIssues...)
    47  			mu.Unlock()
    48  
    49  			return nil, nil
    50  		}
    51  	}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
    52  		return resIssues
    53  	}).WithLoadMode(goanalysis.LoadModeSyntax)
    54  }
    55  
    56  type returnsVisitor struct {
    57  	f         *token.FileSet
    58  	maxBlanks int
    59  	issues    []result.Issue
    60  }
    61  
    62  func (v *returnsVisitor) Visit(node ast.Node) ast.Visitor {
    63  	funcDecl, ok := node.(*ast.FuncDecl)
    64  	if !ok {
    65  		return v
    66  	}
    67  	if funcDecl.Body == nil {
    68  		return v
    69  	}
    70  
    71  	for _, expr := range funcDecl.Body.List {
    72  		assgnStmt, ok := expr.(*ast.AssignStmt)
    73  		if !ok {
    74  			continue
    75  		}
    76  
    77  		numBlank := 0
    78  		for _, left := range assgnStmt.Lhs {
    79  			ident, ok := left.(*ast.Ident)
    80  			if !ok {
    81  				continue
    82  			}
    83  			if ident.Name == "_" {
    84  				numBlank++
    85  			}
    86  		}
    87  
    88  		if numBlank > v.maxBlanks {
    89  			v.issues = append(v.issues, result.Issue{
    90  				FromLinter: dogsledLinterName,
    91  				Text:       fmt.Sprintf("declaration has %v blank identifiers", numBlank),
    92  				Pos:        v.f.Position(assgnStmt.Pos()),
    93  			})
    94  		}
    95  	}
    96  	return v
    97  }