This repository has been archived by the owner on Aug 25, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmethod_rule.go
144 lines (129 loc) · 3.24 KB
/
method_rule.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package main
import (
"fmt"
"go/ast"
"go/token"
"regexp"
"strconv"
"strings"
)
// MethodRule handles selector expressions (method calls)
type MethodRule struct {
comment string
call string
callMatch []*regexp.Regexp
argument int
// generic
avoid bool
match []*regexp.Regexp
// int action specific
greaterThan int
lessThan int
equals int
ignoreGreaterThan bool
ignoreLessThan bool
ignoreEquals bool
}
func (rule *MethodRule) String() string {
return fmt.Sprintf("method rule for %v (%v): %v", rule.call, rule.argument, rule.greaterThan)
}
func (rule *MethodRule) LintMessage(fs *token.FileSet, node ast.Node) string {
position := fs.Position(node.Pos())
return fmt.Sprintf(
"%s:%d:%d:%s",
position.Filename,
position.Line,
position.Column,
rule.comment,
)
}
func (rule *MethodRule) ProcessMethodCall(methodCall string, fs *token.FileSet, node ast.Node, ce *ast.CallExpr) {
// digs into the ast node from found function arguments
var handleArgument func(ast.Node)
handleArgument = func(arg ast.Node) {
switch a := arg.(type) {
case *ast.BasicLit:
bl := a
switch bl.Kind {
case token.STRING:
strValue := strings.Replace(bl.Value, "\"", "", -1)
for _, cm := range rule.match {
match := cm.FindString(strValue)
if match != "" {
fmt.Println(rule.LintMessage(fs, bl))
}
}
case token.INT:
argInt, err := strconv.Atoi(bl.Value)
if err == nil {
// this is trickier than I thought it would be...
if argInt == rule.equals && !rule.ignoreEquals {
fmt.Println(rule.LintMessage(fs, bl))
return
}
if argInt < rule.lessThan && !rule.ignoreLessThan {
fmt.Println(rule.LintMessage(fs, bl))
return
}
if argInt > rule.greaterThan && !rule.ignoreGreaterThan {
fmt.Println(rule.LintMessage(fs, bl))
return
}
}
default:
// fmt.Println(a)
}
case *ast.Ident: // initial hint there's a variable being used
switch v := a.Obj.Decl.(type) {
case *ast.ValueSpec:
handleArgument(v)
case *ast.AssignStmt:
for _, r := range v.Rhs {
handleArgument(r)
}
default:
// fmt.Println(v)
}
case *ast.ValueSpec: // handle variables
for _, v := range a.Values {
handleArgument(v)
}
case *ast.BinaryExpr:
handleArgument(a.X)
handleArgument(a.Y)
case *ast.CallExpr:
for _, ar := range a.Args {
handleArgument(ar)
}
default:
// fmt.Println(a)
}
}
if methodCall == rule.call || matchAny(methodCall, rule.callMatch) {
if rule.avoid {
fmt.Println(rule.LintMessage(fs, node))
return
}
if rule.argument == -1 { // all arguments
for _, arg := range ce.Args {
handleArgument(arg)
}
} else { // a specific argument
arg := ce.Args[rule.argument]
handleArgument(arg)
}
} else if matchAny(methodCall, rule.match) { // TODO: consider removal because call_match
fmt.Println(rule.LintMessage(fs, node))
}
}
// Action is required to establish a Rule
func (rule *MethodRule) Action(fs *token.FileSet, node ast.Node) {
ce, ok := node.(*ast.CallExpr)
if ok {
se, ok := ce.Fun.(*ast.SelectorExpr)
if ok {
methodCall := fmt.Sprintf("%v.%v", se.X, se.Sel.Name)
rule.ProcessMethodCall(methodCall, fs, node, ce)
}
}
}