github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/stylecheck/st1008/st1008.go (about) 1 package st1008 2 3 import ( 4 "go/types" 5 6 "github.com/amarpal/go-tools/analysis/lint" 7 "github.com/amarpal/go-tools/analysis/report" 8 "github.com/amarpal/go-tools/internal/passes/buildir" 9 10 "golang.org/x/tools/go/analysis" 11 ) 12 13 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 14 Analyzer: &analysis.Analyzer{ 15 Name: "ST1008", 16 Run: run, 17 Requires: []*analysis.Analyzer{buildir.Analyzer}, 18 }, 19 Doc: &lint.Documentation{ 20 Title: `A function's error value should be its last return value`, 21 Text: `A function's error value should be its last return value.`, 22 Since: `2019.1`, 23 MergeIf: lint.MergeIfAny, 24 }, 25 }) 26 27 var Analyzer = SCAnalyzer.Analyzer 28 29 func run(pass *analysis.Pass) (interface{}, error) { 30 fnLoop: 31 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 32 sig := fn.Type().(*types.Signature) 33 rets := sig.Results() 34 if rets == nil || rets.Len() < 2 { 35 continue 36 } 37 38 if rets.At(rets.Len()-1).Type() == types.Universe.Lookup("error").Type() { 39 // Last return type is error. If the function also returns 40 // errors in other positions, that's fine. 41 continue 42 } 43 44 if rets.Len() >= 2 && rets.At(rets.Len()-1).Type() == types.Universe.Lookup("bool").Type() && rets.At(rets.Len()-2).Type() == types.Universe.Lookup("error").Type() { 45 // Accept (..., error, bool) and assume it's a comma-ok function. It's not clear whether the bool should come last or not for these kinds of functions. 46 continue 47 } 48 for i := rets.Len() - 2; i >= 0; i-- { 49 if rets.At(i).Type() == types.Universe.Lookup("error").Type() { 50 report.Report(pass, rets.At(i), "error should be returned as the last argument", report.ShortRange()) 51 continue fnLoop 52 } 53 } 54 } 55 return nil, nil 56 }