github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/simple/s1019/s1019.go (about)

     1  package s1019
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/types"
     7  	"path/filepath"
     8  
     9  	"github.com/amarpal/go-tools/analysis/code"
    10  	"github.com/amarpal/go-tools/analysis/facts/generated"
    11  	"github.com/amarpal/go-tools/analysis/lint"
    12  	"github.com/amarpal/go-tools/analysis/report"
    13  	"github.com/amarpal/go-tools/go/types/typeutil"
    14  	"github.com/amarpal/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.Documentation{
    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  }