sa4024.go raw

   1  package sa4024
   2  
   3  import (
   4  	"fmt"
   5  	"go/ast"
   6  
   7  	"honnef.co/go/tools/analysis/code"
   8  	"honnef.co/go/tools/analysis/lint"
   9  	"honnef.co/go/tools/analysis/report"
  10  	"honnef.co/go/tools/pattern"
  11  
  12  	"golang.org/x/tools/go/analysis"
  13  	"golang.org/x/tools/go/analysis/passes/inspect"
  14  )
  15  
  16  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  17  	Analyzer: &analysis.Analyzer{
  18  		Name:     "SA4024",
  19  		Run:      run,
  20  		Requires: []*analysis.Analyzer{inspect.Analyzer},
  21  	},
  22  	Doc: &lint.RawDocumentation{
  23  		Title: `Checking for impossible return value from a builtin function`,
  24  		Text: `Return values of the \'len\' and \'cap\' builtins cannot be negative.
  25  
  26  See https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap.
  27  
  28  Example:
  29  
  30      if len(slice) < 0 {
  31          fmt.Println("unreachable code")
  32      }`,
  33  		Since:    "2021.1",
  34  		Severity: lint.SeverityWarning,
  35  		MergeIf:  lint.MergeIfAny,
  36  	},
  37  })
  38  
  39  var Analyzer = SCAnalyzer.Analyzer
  40  
  41  var builtinLessThanZeroQ = pattern.MustParse(`
  42  	(Or
  43  		(BinaryExpr
  44  			(IntegerLiteral "0")
  45  			">"
  46  			(CallExpr builtin@(Builtin (Or "len" "cap")) _))
  47  		(BinaryExpr
  48  			(CallExpr builtin@(Builtin (Or "len" "cap")) _)
  49  			"<"
  50  			(IntegerLiteral "0")))
  51  `)
  52  
  53  func run(pass *analysis.Pass) (interface{}, error) {
  54  	fn := func(node ast.Node) {
  55  		matcher, ok := code.Match(pass, builtinLessThanZeroQ, node)
  56  		if !ok {
  57  			return
  58  		}
  59  
  60  		builtin := matcher.State["builtin"].(*ast.Ident)
  61  		report.Report(pass, node, fmt.Sprintf("builtin function %s does not return negative values", builtin.Name))
  62  	}
  63  	code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
  64  
  65  	return nil, nil
  66  }
  67