sa4001.go raw
1 package sa4001
2
3 import (
4 "go/ast"
5 "regexp"
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: "SA4001",
19 Run: run,
20 Requires: []*analysis.Analyzer{inspect.Analyzer},
21 },
22 Doc: &lint.RawDocumentation{
23 Title: `\'&*x\' gets simplified to \'x\', it does not copy \'x\'`,
24 Since: "2017.1",
25 Severity: lint.SeverityWarning,
26 MergeIf: lint.MergeIfAny,
27 },
28 })
29
30 var Analyzer = SCAnalyzer.Analyzer
31
32 var (
33 // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't
34 // want to flag.
35 cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`)
36 checkIneffectiveCopyQ1 = pattern.MustParse(`(UnaryExpr "&" (StarExpr obj))`)
37 checkIneffectiveCopyQ2 = pattern.MustParse(`(StarExpr (UnaryExpr "&" _))`)
38 )
39
40 func run(pass *analysis.Pass) (interface{}, error) {
41 fn := func(node ast.Node) {
42 if m, ok := code.Match(pass, checkIneffectiveCopyQ1, node); ok {
43 if ident, ok := m.State["obj"].(*ast.Ident); !ok || !cgoIdent.MatchString(ident.Name) {
44 report.Report(pass, node, "&*x will be simplified to x. It will not copy x.")
45 }
46 } else if _, ok := code.Match(pass, checkIneffectiveCopyQ2, node); ok {
47 report.Report(pass, node, "*&x will be simplified to x. It will not copy x.")
48 }
49 }
50 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil))
51 return nil, nil
52 }
53