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