Skip to content

Commit d8eac1b

Browse files
committed
S1008: added support for multi-arguments return statement.
1 parent b36ec99 commit d8eac1b

File tree

2 files changed

+111
-10
lines changed

2 files changed

+111
-10
lines changed

simple/s1008/s1008.go

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"go/ast"
66
"go/constant"
77
"go/token"
8+
"strings"
89

910
"honnef.co/go/tools/analysis/code"
1011
"honnef.co/go/tools/analysis/facts/generated"
@@ -38,8 +39,25 @@ return false`,
3839
var Analyzer = SCAnalyzer.Analyzer
3940

4041
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+
`)
4361
)
4462

4563
func run(pass *analysis.Pass) (interface{}, error) {
@@ -57,47 +75,95 @@ func run(pass *analysis.Pass) (interface{}, error) {
5775
return
5876
}
5977
}
60-
m1, ok := code.Match(pass, checkIfReturnQIf, n1)
78+
fm1, ok := code.Match(pass, checkIfReturnQIf, n1)
6179
if !ok {
6280
return
6381
}
64-
m2, ok := code.Match(pass, checkIfReturnQRet, n2)
82+
fm2, ok := code.Match(pass, checkIfReturnQRet, n2)
6583
if !ok {
6684
return
6785
}
6886

69-
if op, ok := m1.State["cond"].(*ast.BinaryExpr); ok {
87+
if op, ok := fm1.State["cond"].(*ast.BinaryExpr); ok {
7088
switch op.Op {
7189
case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ:
7290
default:
7391
return
7492
}
7593
}
7694

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+
}
79117

80118
if ret1.Name == ret2.Name {
81119
// we want the function to return true and false, not the
82120
// same value both times.
83121
return
84122
}
85123

86-
cond := m1.State["cond"].(ast.Expr)
124+
cond := fm1.State["cond"].(ast.Expr)
87125
origCond := cond
88126
if ret1.Name == "false" {
89127
cond = negate(pass, cond)
90128
}
91129
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'",
93132
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+
),
95143
report.FilterGenerated())
96144
}
97145
code.Preorder(pass, fn, (*ast.BlockStmt)(nil))
98146
return nil, nil
99147
}
100148

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+
101167
func negate(pass *analysis.Pass, expr ast.Expr) ast.Expr {
102168
switch expr := expr.(type) {
103169
case *ast.BinaryExpr:
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package pkg
2+
3+
func multireturn_fn1(x string) (bool, error) {
4+
if len(x) > 0 { //@ diag(`should use 'return len(x) > 0, nil' instead of 'if len(x) > 0 { return true, nil }; return false, nil'`)
5+
return true, nil
6+
}
7+
return false, nil
8+
}
9+
10+
func multireturn_fn2(x string) (bool, error) {
11+
if len(x) > 0 {
12+
}
13+
return false, nil
14+
}
15+
16+
func multireturn_fn3(x string) (bool, error, error) {
17+
if len(x) > 0 { //@ diag(`should use 'return len(x) > 0, nil, nil' instead of 'if len(x) > 0 { return true, nil, nil }; return false, nil, nil'`)
18+
return true, nil, nil
19+
}
20+
return false, nil, nil
21+
}
22+
23+
func multireturn_fn4(x string) (bool, int, error) {
24+
if len(x) > 0 { //@ diag(`should use 'return len(x) == 0, 0, nil' instead of 'if len(x) > 0 { return false, 0, nil }; return true, 0, nil'`)
25+
return false, 0, nil
26+
}
27+
return true, 0, nil
28+
}
29+
30+
func multireturn_fn5(x string) (bool, int, error) {
31+
if len(x) > 0 {
32+
return false, 20, nil
33+
}
34+
return true, 30, nil
35+
}

0 commit comments

Comments
 (0)