s1019.go raw

   1  package s1019
   2  
   3  import (
   4  	"fmt"
   5  	"go/ast"
   6  	"go/types"
   7  	"path/filepath"
   8  
   9  	"honnef.co/go/tools/analysis/code"
  10  	"honnef.co/go/tools/analysis/facts/generated"
  11  	"honnef.co/go/tools/analysis/lint"
  12  	"honnef.co/go/tools/analysis/report"
  13  	"honnef.co/go/tools/go/types/typeutil"
  14  	"honnef.co/go/tools/pattern"
  15  
  16  	"golang.org/x/tools/go/analysis"
  17  	"golang.org/x/tools/go/analysis/passes/inspect"
  18  )
  19  
  20  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  21  	Analyzer: &analysis.Analyzer{
  22  		Name:     "S1019",
  23  		Run:      run,
  24  		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
  25  	},
  26  	Doc: &lint.RawDocumentation{
  27  		Title: `Simplify \"make\" call by omitting redundant arguments`,
  28  		Text: `The \"make\" function has default values for the length and capacity
  29  arguments. For channels, the length defaults to zero, and for slices,
  30  the capacity defaults to the length.`,
  31  		Since: "2017.1",
  32  		// MergeIfAll because the type might be different under different build tags.
  33  		// You shouldn't write code like that…
  34  		MergeIf: lint.MergeIfAll,
  35  	},
  36  })
  37  
  38  var Analyzer = SCAnalyzer.Analyzer
  39  
  40  var (
  41  	checkMakeLenCapQ1 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size@(IntegerLiteral "0")])`)
  42  	checkMakeLenCapQ2 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size size])`)
  43  )
  44  
  45  func run(pass *analysis.Pass) (interface{}, error) {
  46  	fn := func(node ast.Node) {
  47  		if pass.Pkg.Path() == "runtime_test" && filepath.Base(pass.Fset.Position(node.Pos()).Filename) == "map_test.go" {
  48  			// special case of runtime tests testing map creation
  49  			return
  50  		}
  51  		if m, ok := code.Match(pass, checkMakeLenCapQ1, node); ok {
  52  			T := m.State["typ"].(ast.Expr)
  53  			size := m.State["size"].(ast.Node)
  54  
  55  			if _, ok := typeutil.CoreType(pass.TypesInfo.TypeOf(T)).Underlying().(*types.Chan); ok {
  56  				report.Report(pass, size, fmt.Sprintf("should use make(%s) instead", report.Render(pass, T)), report.FilterGenerated())
  57  			}
  58  
  59  		} else if m, ok := code.Match(pass, checkMakeLenCapQ2, node); ok {
  60  			// TODO(dh): don't consider sizes identical if they're
  61  			// dynamic. for example: make(T, <-ch, <-ch).
  62  			T := m.State["typ"].(ast.Expr)
  63  			size := m.State["size"].(ast.Node)
  64  			report.Report(pass, size,
  65  				fmt.Sprintf("should use make(%s, %s) instead", report.Render(pass, T), report.Render(pass, size)),
  66  				report.FilterGenerated())
  67  		}
  68  	}
  69  	code.Preorder(pass, fn, (*ast.CallExpr)(nil))
  70  	return nil, nil
  71  }
  72