st1008.go raw

   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.RawDocumentation{
  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  }
  59