This repository has been archived by the owner on Dec 30, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
dupArg_checker.go
115 lines (96 loc) · 3.38 KB
/
dupArg_checker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package checkers
import (
"go/ast"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
"github.com/go-toolsmith/astequal"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "dupArg"
info.Tags = []string{"diagnostic"}
info.Summary = "Detects suspicious duplicated arguments"
info.Before = `copy(dst, dst)`
info.After = `copy(dst, src)`
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
c := &dupArgChecker{ctx: ctx}
// newMatcherFunc returns a function that matches a call if
// args[xIndex] and args[yIndex] are equal.
newMatcherFunc := func(xIndex, yIndex int) func(*ast.CallExpr) bool {
return func(call *ast.CallExpr) bool {
x := call.Args[xIndex]
y := call.Args[yIndex]
return astequal.Expr(x, y)
}
}
// m maps pattern string to a matching function.
// String patterns are used for documentation purposes (readability).
m := map[string]func(*ast.CallExpr) bool{
"(x, x, ...)": newMatcherFunc(0, 1),
"(x, _, x, ...)": newMatcherFunc(0, 2),
"(_, x, x, ...)": newMatcherFunc(1, 2),
}
// TODO(quasilyte): handle x.Equal(x) cases.
// Example: *math/Big.Int.Cmp method.
// TODO(quasilyte): more perky mode that will also
// report things like io.Copy(x, x).
// Probably safe thing to do even without that option
// if `x` is not interface (requires type checks
// that are not incorporated into this checker yet).
c.matchers = map[string]func(*ast.CallExpr) bool{
"copy": m["(x, x, ...)"],
"reflect.Copy": m["(x, x, ...)"],
"reflect.DeepEqual": m["(x, x, ...)"],
"strings.Contains": m["(x, x, ...)"],
"strings.Compare": m["(x, x, ...)"],
"strings.EqualFold": m["(x, x, ...)"],
"strings.HasPrefix": m["(x, x, ...)"],
"strings.HasSuffix": m["(x, x, ...)"],
"strings.Index": m["(x, x, ...)"],
"strings.LastIndex": m["(x, x, ...)"],
"strings.Split": m["(x, x, ...)"],
"strings.SplitAfter": m["(x, x, ...)"],
"strings.SplitAfterN": m["(x, x, ...)"],
"strings.SplitN": m["(x, x, ...)"],
"strings.Replace": m["(_, x, x, ...)"],
"strings.ReplaceAll": m["(_, x, x, ...)"],
"bytes.Contains": m["(x, x, ...)"],
"bytes.Compare": m["(x, x, ...)"],
"bytes.Equal": m["(x, x, ...)"],
"bytes.EqualFold": m["(x, x, ...)"],
"bytes.HasPrefix": m["(x, x, ...)"],
"bytes.HasSuffix": m["(x, x, ...)"],
"bytes.Index": m["(x, x, ...)"],
"bytes.LastIndex": m["(x, x, ...)"],
"bytes.Split": m["(x, x, ...)"],
"bytes.SplitAfter": m["(x, x, ...)"],
"bytes.SplitAfterN": m["(x, x, ...)"],
"bytes.SplitN": m["(x, x, ...)"],
"bytes.Replace": m["(_, x, x, ...)"],
"bytes.ReplaceAll": m["(_, x, x, ...)"],
"types.Identical": m["(x, x, ...)"],
"types.IdenticalIgnoreTags": m["(x, x, ...)"],
"draw.Draw": m["(x, _, x, ...)"],
// TODO(quasilyte): more of these.
}
return astwalk.WalkerForExpr(c)
})
}
type dupArgChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
matchers map[string]func(*ast.CallExpr) bool
}
func (c *dupArgChecker) VisitExpr(expr ast.Expr) {
call, ok := expr.(*ast.CallExpr)
if !ok {
return
}
m := c.matchers[qualifiedName(call.Fun)]
if m != nil && m(call) {
c.warn(call)
}
}
func (c *dupArgChecker) warn(cause ast.Node) {
c.ctx.Warn(cause, "suspicious duplicated args in `%s`", cause)
}