github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/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/vanstinator/golangci-lint/pkg/config" 12 "github.com/vanstinator/golangci-lint/pkg/golinters/goanalysis" 13 "github.com/vanstinator/golangci-lint/pkg/lint/linter" 14 "github.com/vanstinator/golangci-lint/pkg/result" 15 ) 16 17 const dogsledName = "dogsled" 18 19 //nolint:dupl 20 func NewDogsled(settings *config.DogsledSettings) *goanalysis.Linter { 21 var mu sync.Mutex 22 var resIssues []goanalysis.Issue 23 24 analyzer := &analysis.Analyzer{ 25 Name: dogsledName, 26 Doc: goanalysis.TheOnlyanalyzerDoc, 27 Run: func(pass *analysis.Pass) (any, error) { 28 issues := runDogsled(pass, settings) 29 30 if len(issues) == 0 { 31 return nil, nil 32 } 33 34 mu.Lock() 35 resIssues = append(resIssues, issues...) 36 mu.Unlock() 37 38 return nil, nil 39 }, 40 } 41 42 return goanalysis.NewLinter( 43 dogsledName, 44 "Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())", 45 []*analysis.Analyzer{analyzer}, 46 nil, 47 ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { 48 return resIssues 49 }).WithLoadMode(goanalysis.LoadModeSyntax) 50 } 51 52 func runDogsled(pass *analysis.Pass, settings *config.DogsledSettings) []goanalysis.Issue { 53 var reports []goanalysis.Issue 54 for _, f := range pass.Files { 55 v := &returnsVisitor{ 56 maxBlanks: settings.MaxBlankIdentifiers, 57 f: pass.Fset, 58 } 59 60 ast.Walk(v, f) 61 62 for i := range v.issues { 63 reports = append(reports, goanalysis.NewIssue(&v.issues[i], pass)) 64 } 65 } 66 67 return reports 68 } 69 70 type returnsVisitor struct { 71 f *token.FileSet 72 maxBlanks int 73 issues []result.Issue 74 } 75 76 func (v *returnsVisitor) Visit(node ast.Node) ast.Visitor { 77 funcDecl, ok := node.(*ast.FuncDecl) 78 if !ok { 79 return v 80 } 81 if funcDecl.Body == nil { 82 return v 83 } 84 85 for _, expr := range funcDecl.Body.List { 86 assgnStmt, ok := expr.(*ast.AssignStmt) 87 if !ok { 88 continue 89 } 90 91 numBlank := 0 92 for _, left := range assgnStmt.Lhs { 93 ident, ok := left.(*ast.Ident) 94 if !ok { 95 continue 96 } 97 if ident.Name == "_" { 98 numBlank++ 99 } 100 } 101 102 if numBlank > v.maxBlanks { 103 v.issues = append(v.issues, result.Issue{ 104 FromLinter: dogsledName, 105 Text: fmt.Sprintf("declaration has %v blank identifiers", numBlank), 106 Pos: v.f.Position(assgnStmt.Pos()), 107 }) 108 } 109 } 110 return v 111 }