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