5
5
"go/ast"
6
6
"go/constant"
7
7
"go/token"
8
+ "strings"
8
9
9
10
"honnef.co/go/tools/analysis/code"
10
11
"honnef.co/go/tools/analysis/facts/generated"
@@ -38,8 +39,25 @@ return false`,
38
39
var Analyzer = SCAnalyzer .Analyzer
39
40
40
41
var (
41
- checkIfReturnQIf = pattern .MustParse (`(IfStmt nil cond [(ReturnStmt [ret@(Builtin (Or "true" "false"))])] nil)` )
42
- checkIfReturnQRet = pattern .MustParse (`(ReturnStmt [ret@(Builtin (Or "true" "false"))])` )
42
+ checkIfReturnQIf = pattern .MustParse (`
43
+ (IfStmt
44
+ nil
45
+ cond
46
+ [fret@(ReturnStmt _)]
47
+ nil)
48
+ ` )
49
+ checkIfReturnQRet = pattern .MustParse (`
50
+ (Binding "fret" (ReturnStmt _))
51
+ ` )
52
+ checkReturnValue = pattern .MustParse (`
53
+ (Or
54
+ (ReturnStmt
55
+ (List
56
+ ret@(Builtin (Or "true" "false"))
57
+ tail@(Any)))
58
+ (ReturnStmt
59
+ [ret@(Builtin (Or "true" "false"))]))
60
+ ` )
43
61
)
44
62
45
63
func run (pass * analysis.Pass ) (interface {}, error ) {
@@ -57,47 +75,95 @@ func run(pass *analysis.Pass) (interface{}, error) {
57
75
return
58
76
}
59
77
}
60
- m1 , ok := code .Match (pass , checkIfReturnQIf , n1 )
78
+ fm1 , ok := code .Match (pass , checkIfReturnQIf , n1 )
61
79
if ! ok {
62
80
return
63
81
}
64
- m2 , ok := code .Match (pass , checkIfReturnQRet , n2 )
82
+ fm2 , ok := code .Match (pass , checkIfReturnQRet , n2 )
65
83
if ! ok {
66
84
return
67
85
}
68
86
69
- if op , ok := m1 .State ["cond" ].(* ast.BinaryExpr ); ok {
87
+ if op , ok := fm1 .State ["cond" ].(* ast.BinaryExpr ); ok {
70
88
switch op .Op {
71
89
case token .EQL , token .LSS , token .GTR , token .NEQ , token .LEQ , token .GEQ :
72
90
default :
73
91
return
74
92
}
75
93
}
76
94
77
- ret1 := m1 .State ["ret" ].(* ast.Ident )
78
- ret2 := m2 .State ["ret" ].(* ast.Ident )
95
+ fret1 := fm1 .State ["fret" ].(* ast.ReturnStmt )
96
+ m1 , ok := code .Match (pass , checkReturnValue , fret1 )
97
+ if ! ok {
98
+ return
99
+ }
100
+
101
+ fret2 := fm2 .State ["fret" ].(* ast.ReturnStmt )
102
+ m2 , ok := code .Match (pass , checkReturnValue , fret2 )
103
+ if ! ok {
104
+ return
105
+ }
106
+
107
+ ret1 , tail1 := getRetAndTail (m1 )
108
+ tail1String := renderTailString (pass , tail1 )
109
+
110
+ ret2 , tail2 := getRetAndTail (m2 )
111
+ tail2String := renderTailString (pass , tail2 )
112
+
113
+ if tail1String != tail2String {
114
+ // we want to process only return with the same values
115
+ return
116
+ }
79
117
80
118
if ret1 .Name == ret2 .Name {
81
119
// we want the function to return true and false, not the
82
120
// same value both times.
83
121
return
84
122
}
85
123
86
- cond := m1 .State ["cond" ].(ast.Expr )
124
+ cond := fm1 .State ["cond" ].(ast.Expr )
87
125
origCond := cond
88
126
if ret1 .Name == "false" {
89
127
cond = negate (pass , cond )
90
128
}
91
129
report .Report (pass , n1 ,
92
- fmt .Sprintf ("should use 'return %s' instead of 'if %s { return %s }; return %s'" ,
130
+ fmt .Sprintf (
131
+ "should use 'return %s%s' instead of 'if %s { return %s%s }; return %s%s'" ,
93
132
report .Render (pass , cond ),
94
- report .Render (pass , origCond ), report .Render (pass , ret1 ), report .Render (pass , ret2 )),
133
+ tail1String ,
134
+ report .Render (
135
+ pass ,
136
+ origCond ,
137
+ ),
138
+ report .Render (pass , ret1 ),
139
+ tail1String ,
140
+ report .Render (pass , ret2 ),
141
+ tail2String ,
142
+ ),
95
143
report .FilterGenerated ())
96
144
}
97
145
code .Preorder (pass , fn , (* ast .BlockStmt )(nil ))
98
146
return nil , nil
99
147
}
100
148
149
+ func getRetAndTail (m1 * pattern.Matcher ) (* ast.Ident , []ast.Expr ) {
150
+ ret1 := m1 .State ["ret" ].(* ast.Ident )
151
+ var tail1 []ast.Expr
152
+ if tail , ok := m1 .State ["tail" ]; ok {
153
+ tail1 , _ = tail .([]ast.Expr )
154
+ }
155
+ return ret1 , tail1
156
+ }
157
+
158
+ func renderTailString (pass * analysis.Pass , tail []ast.Expr ) string {
159
+ var tail1StringBuilder strings.Builder
160
+ if len (tail ) != 0 {
161
+ tail1StringBuilder .WriteString (", " )
162
+ tail1StringBuilder .WriteString (report .RenderArgs (pass , tail ))
163
+ }
164
+ return tail1StringBuilder .String ()
165
+ }
166
+
101
167
func negate (pass * analysis.Pass , expr ast.Expr ) ast.Expr {
102
168
switch expr := expr .(type ) {
103
169
case * ast.BinaryExpr :
0 commit comments