Skip to content

Commit 488f065

Browse files
committed
feat: suggested fixes for os.CreateTemp
1 parent 28655ca commit 488f065

File tree

4 files changed

+486
-8
lines changed

4 files changed

+486
-8
lines changed

report.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package usetesting
22

33
import (
4+
"bytes"
45
"fmt"
56
"go/ast"
7+
"go/printer"
68
"go/token"
79
"slices"
810
"strings"
@@ -34,10 +36,7 @@ func (a *analyzer) reportCallExpr(pass *analysis.Pass, ce *ast.CallExpr, fnInfo
3436
}
3537

3638
if expr.Name == osPkgName && isFirstArgEmptyString(ce) {
37-
pass.Reportf(ce.Pos(),
38-
`%s.%s("", ...) could be replaced by %[1]s.%[2]s(%s.%s(), ...) in %s`,
39-
osPkgName, createTempName, fnInfo.ArgName, tempDirName, fnInfo.Name,
40-
)
39+
pass.Report(diagnosticOSCreateTemp(ce, fnInfo))
4140

4241
return true
4342
}
@@ -50,10 +49,7 @@ func (a *analyzer) reportCallExpr(pass *analysis.Pass, ce *ast.CallExpr, fnInfo
5049
pkgName := getPkgNameFromType(pass, fun)
5150

5251
if pkgName == osPkgName && isFirstArgEmptyString(ce) {
53-
pass.Reportf(ce.Pos(),
54-
`%s.%s("", ...) could be replaced by %[1]s.%[2]s(%s.%s(), ...) in %s`,
55-
osPkgName, createTempName, fnInfo.ArgName, tempDirName, fnInfo.Name,
56-
)
52+
pass.Report(diagnosticOSCreateTemp(ce, fnInfo))
5753

5854
return true
5955
}
@@ -62,6 +58,50 @@ func (a *analyzer) reportCallExpr(pass *analysis.Pass, ce *ast.CallExpr, fnInfo
6258
return false
6359
}
6460

61+
func diagnosticOSCreateTemp(ce *ast.CallExpr, fnInfo *FuncInfo) analysis.Diagnostic {
62+
diagnostic := analysis.Diagnostic{
63+
Pos: ce.Pos(),
64+
Message: fmt.Sprintf(
65+
`%s.%s("", ...) could be replaced by %[1]s.%[2]s(%s.%s(), ...) in %s`,
66+
osPkgName, createTempName, fnInfo.ArgName, tempDirName, fnInfo.Name,
67+
),
68+
}
69+
70+
// Skip `<t/b>` arg names.
71+
if !strings.Contains(fnInfo.ArgName, "<") {
72+
g := &ast.CallExpr{
73+
Fun: ce.Fun,
74+
Args: []ast.Expr{
75+
&ast.CallExpr{
76+
Fun: &ast.SelectorExpr{
77+
X: &ast.Ident{Name: fnInfo.ArgName},
78+
Sel: &ast.Ident{Name: tempDirName},
79+
},
80+
},
81+
ce.Args[1],
82+
},
83+
}
84+
85+
buf := bytes.NewBuffer(nil)
86+
87+
err := printer.Fprint(buf, token.NewFileSet(), g)
88+
if err != nil {
89+
diagnostic.Message = fmt.Sprintf("Suggested fix error: %v", err)
90+
return diagnostic
91+
}
92+
93+
diagnostic.SuggestedFixes = append(diagnostic.SuggestedFixes, analysis.SuggestedFix{
94+
TextEdits: []analysis.TextEdit{{
95+
Pos: ce.Pos(),
96+
End: ce.End(),
97+
NewText: buf.Bytes(),
98+
}},
99+
})
100+
}
101+
102+
return diagnostic
103+
}
104+
65105
func (a *analyzer) reportSelector(pass *analysis.Pass, se *ast.SelectorExpr, fnInfo *FuncInfo) {
66106
if se.Sel == nil || !se.Sel.IsExported() {
67107
return
@@ -120,6 +160,8 @@ func report(pass *analysis.Pass, rg analysis.Range, origPkgName, origName, expec
120160
),
121161
}
122162

163+
// Skip `<t/b>` arg names.
164+
// Only applies on `context.XXX` because the nb of return parameters is the same as the replacement.
123165
if !strings.Contains(fnInfo.ArgName, "<") && origPkgName == contextPkgName {
124166
diagnostic.SuggestedFixes = append(diagnostic.SuggestedFixes, analysis.SuggestedFix{
125167
TextEdits: []analysis.TextEdit{{
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package basic
2+
3+
import (
4+
"os"
5+
"runtime"
6+
"strconv"
7+
"testing"
8+
)
9+
10+
func Test_NoName(_ *testing.T) {
11+
os.CreateTemp("", "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(<t/b>\.TempDir\(\), \.\.\.\) in .+`
12+
}
13+
14+
func Benchmark_ExprStmt(b *testing.B) {
15+
os.CreateTemp(b.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(b\.TempDir\(\), \.\.\.\) in .+`
16+
}
17+
18+
func Test_ExprStmt(t *testing.T) {
19+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
20+
os.CreateTemp(t.TempDir(), "xx") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
21+
os.CreateTemp(os.TempDir(), "xx")
22+
os.CreateTemp(t.TempDir(), "xx")
23+
}
24+
25+
func Test_AssignStmt(t *testing.T) {
26+
f, err := os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
27+
_ = err
28+
_ = f
29+
}
30+
31+
func Test_AssignStmt_ignore_return(t *testing.T) {
32+
_, _ = os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
33+
}
34+
35+
func Test_IfStmt(t *testing.T) {
36+
if _, err := os.CreateTemp(t.TempDir(), ""); err != nil { // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
37+
// foo
38+
}
39+
}
40+
41+
func TestName_RangeStmt(t *testing.T) {
42+
for i := range 5 {
43+
os.CreateTemp(t.TempDir(), strconv.Itoa(i)) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
44+
}
45+
}
46+
47+
func Test_ForStmt(t *testing.T) {
48+
for i := 0; i < 3; i++ {
49+
os.CreateTemp(t.TempDir(), strconv.Itoa(i)) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
50+
}
51+
}
52+
53+
func Test_DeferStmt(t *testing.T) {
54+
defer os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
55+
}
56+
57+
func Test_CallExpr(t *testing.T) {
58+
t.Log(os.CreateTemp(t.TempDir(), "")) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
59+
}
60+
61+
func Test_GoStmt(t *testing.T) {
62+
go func() {
63+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
64+
}()
65+
}
66+
67+
func Test_GoStmt_arg(t *testing.T) {
68+
go func(v *os.File, err error) {}(os.CreateTemp(t.TempDir(), "")) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
69+
}
70+
71+
func Test_FuncLit_ExprStmt(t *testing.T) {
72+
testCases := []struct {
73+
desc string
74+
}{
75+
{desc: ""},
76+
}
77+
78+
for _, test := range testCases {
79+
t.Run(test.desc, func(t *testing.T) {
80+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
81+
})
82+
}
83+
}
84+
85+
func Test_SwitchStmt(t *testing.T) {
86+
switch {
87+
case runtime.GOOS == "linux":
88+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
89+
}
90+
}
91+
92+
func Test_DeclStmt(t *testing.T) {
93+
var f, err any = os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
94+
_ = err
95+
_ = f
96+
}
97+
98+
func Test_SelectStmt(t *testing.T) {
99+
doneCh := make(chan struct{})
100+
101+
go func() {
102+
for {
103+
select {
104+
case <-doneCh:
105+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
106+
}
107+
}
108+
}()
109+
}
110+
111+
func Test_DeferStmt_wrap(t *testing.T) {
112+
defer func() {
113+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
114+
}()
115+
}
116+
117+
func Test_SelectStmt_anon_func(t *testing.T) {
118+
doneCh := make(chan struct{})
119+
120+
go func() {
121+
for {
122+
select {
123+
case <-doneCh:
124+
func() {
125+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
126+
}()
127+
}
128+
}
129+
}()
130+
}
131+
132+
func Test_BlockStmt(t *testing.T) {
133+
{
134+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
135+
}
136+
}
137+
138+
func Test_TypeSwitchStmt(t *testing.T) {
139+
os.CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
140+
}
141+
142+
func foobar() {
143+
os.CreateTemp("", "")
144+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package dot
2+
3+
import (
4+
. "os"
5+
"runtime"
6+
"strconv"
7+
"testing"
8+
)
9+
10+
func Test_NoName(_ *testing.T) {
11+
CreateTemp("", "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(<t/b>\.TempDir\(\), \.\.\.\) in .+`
12+
}
13+
14+
func Benchmark_ExprStmt(b *testing.B) {
15+
CreateTemp(b.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(b\.TempDir\(\), \.\.\.\) in .+`
16+
}
17+
18+
func Test_ExprStmt(t *testing.T) {
19+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
20+
CreateTemp(t.TempDir(), "xx") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
21+
CreateTemp(TempDir(), "xx")
22+
CreateTemp(t.TempDir(), "xx")
23+
}
24+
25+
func Test_AssignStmt(t *testing.T) {
26+
f, err := CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
27+
_ = err
28+
_ = f
29+
}
30+
31+
func Test_AssignStmt_ignore_return(t *testing.T) {
32+
_, _ = CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
33+
}
34+
35+
func Test_IfStmt(t *testing.T) {
36+
if _, err := CreateTemp(t.TempDir(), ""); err != nil { // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
37+
// foo
38+
}
39+
}
40+
41+
func TestName_RangeStmt(t *testing.T) {
42+
for i := range 5 {
43+
CreateTemp(t.TempDir(), strconv.Itoa(i)) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
44+
}
45+
}
46+
47+
func Test_ForStmt(t *testing.T) {
48+
for i := 0; i < 3; i++ {
49+
CreateTemp(t.TempDir(), strconv.Itoa(i)) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
50+
}
51+
}
52+
53+
func Test_DeferStmt(t *testing.T) {
54+
defer CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
55+
}
56+
57+
func Test_CallExpr(t *testing.T) {
58+
t.Log(CreateTemp(t.TempDir(), "")) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
59+
}
60+
61+
func Test_GoStmt(t *testing.T) {
62+
go func() {
63+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
64+
}()
65+
}
66+
67+
func Test_GoStmt_arg(t *testing.T) {
68+
go func(v *File, err error) {}(CreateTemp(t.TempDir(), "")) // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
69+
}
70+
71+
func Test_FuncLit_ExprStmt(t *testing.T) {
72+
testCases := []struct {
73+
desc string
74+
}{
75+
{desc: ""},
76+
}
77+
78+
for _, test := range testCases {
79+
t.Run(test.desc, func(t *testing.T) {
80+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
81+
})
82+
}
83+
}
84+
85+
func Test_SwitchStmt(t *testing.T) {
86+
switch {
87+
case runtime.GOOS == "linux":
88+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
89+
}
90+
}
91+
92+
func Test_DeclStmt(t *testing.T) {
93+
var f, err any = CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
94+
_ = err
95+
_ = f
96+
}
97+
98+
func Test_SelectStmt(t *testing.T) {
99+
doneCh := make(chan struct{})
100+
101+
go func() {
102+
for {
103+
select {
104+
case <-doneCh:
105+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
106+
}
107+
}
108+
}()
109+
}
110+
111+
func Test_DeferStmt_wrap(t *testing.T) {
112+
defer func() {
113+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
114+
}()
115+
}
116+
117+
func Test_SelectStmt_anon_func(t *testing.T) {
118+
doneCh := make(chan struct{})
119+
120+
go func() {
121+
for {
122+
select {
123+
case <-doneCh:
124+
func() {
125+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
126+
}()
127+
}
128+
}
129+
}()
130+
}
131+
132+
func Test_BlockStmt(t *testing.T) {
133+
{
134+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
135+
}
136+
}
137+
138+
func Test_TypeSwitchStmt(t *testing.T) {
139+
CreateTemp(t.TempDir(), "") // want `os\.CreateTemp\("", \.\.\.\) could be replaced by os\.CreateTemp\(t\.TempDir\(\), \.\.\.\) in .+`
140+
}
141+
142+
func foobar() {
143+
CreateTemp("", "")
144+
}

0 commit comments

Comments
 (0)