honnef.co/go/tools@v0.5.0-0.dev.0.20240520180541-dcae280a5e87/stylecheck/st1008/st1008.go (about) 1 package st1008 2 3 import ( 4 "go/types" 5 6 "honnef.co/go/tools/analysis/lint" 7 "honnef.co/go/tools/analysis/report" 8 "honnef.co/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 types.Unalias(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 && 45 types.Unalias(rets.At(rets.Len()-1).Type()) == types.Universe.Lookup("bool").Type() && 46 types.Unalias(rets.At(rets.Len()-2).Type()) == types.Universe.Lookup("error").Type() { 47 // 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. 48 continue 49 } 50 for i := rets.Len() - 2; i >= 0; i-- { 51 if types.Unalias(rets.At(i).Type()) == types.Universe.Lookup("error").Type() { 52 report.Report(pass, rets.At(i), "error should be returned as the last argument", report.ShortRange()) 53 continue fnLoop 54 } 55 } 56 } 57 return nil, nil 58 }