github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/golinters/nakedret.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/golangci/golangci-lint/pkg/config" 12 "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" 13 "github.com/golangci/golangci-lint/pkg/lint/linter" 14 "github.com/golangci/golangci-lint/pkg/result" 15 ) 16 17 const nakedretName = "nakedret" 18 19 //nolint:dupl 20 func NewNakedret(settings *config.NakedretSettings) *goanalysis.Linter { 21 var mu sync.Mutex 22 var resIssues []goanalysis.Issue 23 24 analyzer := &analysis.Analyzer{ 25 Name: nakedretName, 26 Doc: goanalysis.TheOnlyanalyzerDoc, 27 Run: func(pass *analysis.Pass) (interface{}, error) { 28 issues := runNakedRet(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 nakedretName, 44 "Finds naked returns in functions greater than a specified function length", 45 []*analysis.Analyzer{analyzer}, 46 nil, 47 ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { 48 return resIssues 49 }).WithLoadMode(goanalysis.LoadModeSyntax) 50 } 51 52 func runNakedRet(pass *analysis.Pass, settings *config.NakedretSettings) []goanalysis.Issue { 53 var issues []goanalysis.Issue 54 55 for _, file := range pass.Files { 56 v := nakedretVisitor{ 57 maxLength: settings.MaxFuncLines, 58 f: pass.Fset, 59 } 60 61 ast.Walk(&v, file) 62 63 for i := range v.issues { 64 issues = append(issues, goanalysis.NewIssue(&v.issues[i], pass)) 65 } 66 } 67 68 return issues 69 } 70 71 type nakedretVisitor struct { 72 maxLength int 73 f *token.FileSet 74 issues []result.Issue 75 } 76 77 func (v *nakedretVisitor) processFuncDecl(funcDecl *ast.FuncDecl) { 78 file := v.f.File(funcDecl.Pos()) 79 functionLineLength := file.Position(funcDecl.End()).Line - file.Position(funcDecl.Pos()).Line 80 81 // Scan the body for usage of the named returns 82 for _, stmt := range funcDecl.Body.List { 83 s, ok := stmt.(*ast.ReturnStmt) 84 if !ok { 85 continue 86 } 87 88 if len(s.Results) != 0 { 89 continue 90 } 91 92 file := v.f.File(s.Pos()) 93 if file == nil || functionLineLength <= v.maxLength { 94 continue 95 } 96 if funcDecl.Name == nil { 97 continue 98 } 99 100 v.issues = append(v.issues, result.Issue{ 101 FromLinter: nakedretName, 102 Text: fmt.Sprintf("naked return in func `%s` with %d lines of code", 103 funcDecl.Name.Name, functionLineLength), 104 Pos: v.f.Position(s.Pos()), 105 }) 106 } 107 } 108 109 func (v *nakedretVisitor) Visit(node ast.Node) ast.Visitor { 110 funcDecl, ok := node.(*ast.FuncDecl) 111 if !ok { 112 return v 113 } 114 115 var namedReturns []*ast.Ident 116 117 // We've found a function 118 if funcDecl.Type != nil && funcDecl.Type.Results != nil { 119 for _, field := range funcDecl.Type.Results.List { 120 for _, ident := range field.Names { 121 if ident != nil { 122 namedReturns = append(namedReturns, ident) 123 } 124 } 125 } 126 } 127 128 if len(namedReturns) == 0 || funcDecl.Body == nil { 129 return v 130 } 131 132 v.processFuncDecl(funcDecl) 133 return v 134 }