1
2
3
4
5 package modernize
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/types"
11 "strings"
12
13 "golang.org/x/tools/go/analysis"
14 "golang.org/x/tools/go/analysis/passes/inspect"
15 "golang.org/x/tools/go/ast/edge"
16 "golang.org/x/tools/internal/analysis/analyzerutil"
17 typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex"
18 "golang.org/x/tools/internal/astutil"
19 "golang.org/x/tools/internal/typesinternal/typeindex"
20 "golang.org/x/tools/internal/versions"
21 )
22
23 var FmtAppendfAnalyzer = &analysis.Analyzer{
24 Name: "fmtappendf",
25 Doc: analyzerutil.MustExtractDoc(doc, "fmtappendf"),
26 Requires: []*analysis.Analyzer{
27 inspect.Analyzer,
28 typeindexanalyzer.Analyzer,
29 },
30 Run: fmtappendf,
31 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#fmtappendf",
32 }
33
34
35
36 func fmtappendf(pass *analysis.Pass) (any, error) {
37 index := pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)
38 for _, fn := range []types.Object{
39 index.Object("fmt", "Sprintf"),
40 index.Object("fmt", "Sprintln"),
41 index.Object("fmt", "Sprint"),
42 } {
43 for curCall := range index.Calls(fn) {
44 call := curCall.Node().(*ast.CallExpr)
45 if ek, idx := curCall.ParentEdge(); ek == edge.CallExpr_Args && idx == 0 {
46
47 conv := curCall.Parent().Node().(*ast.CallExpr)
48 tv := pass.TypesInfo.Types[conv.Fun]
49 if tv.IsType() && types.Identical(tv.Type, byteSliceType) &&
50 analyzerutil.FileUsesGoVersion(pass, astutil.EnclosingFile(curCall), versions.Go1_19) {
51
52
53
54 var id *ast.Ident
55 switch e := ast.Unparen(call.Fun).(type) {
56 case *ast.SelectorExpr:
57 id = e.Sel
58 case *ast.Ident:
59 id = e
60 }
61
62 old, new := fn.Name(), strings.Replace(fn.Name(), "Sprint", "Append", 1)
63 edits := []analysis.TextEdit{
64 {
65
66 Pos: conv.Pos(),
67 End: conv.Lparen + 1,
68 },
69 {
70
71 Pos: conv.Rparen,
72 End: conv.Rparen + 1,
73 },
74 {
75 Pos: id.Pos(),
76 End: id.End(),
77 NewText: []byte(new),
78 },
79 {
80 Pos: call.Lparen + 1,
81 NewText: []byte("nil, "),
82 },
83 }
84 if len(conv.Args) == 1 {
85 arg := conv.Args[0]
86
87
88
89
90
91 if arg.End() < conv.Rparen {
92 edits = append(edits, analysis.TextEdit{
93 Pos: arg.End(),
94 End: conv.Rparen,
95 })
96 }
97 }
98 pass.Report(analysis.Diagnostic{
99 Pos: conv.Pos(),
100 End: conv.End(),
101 Message: fmt.Sprintf("Replace []byte(fmt.%s...) with fmt.%s", old, new),
102 SuggestedFixes: []analysis.SuggestedFix{{
103 Message: fmt.Sprintf("Replace []byte(fmt.%s...) with fmt.%s", old, new),
104 TextEdits: edits,
105 }},
106 })
107 }
108 }
109 }
110 }
111 return nil, nil
112 }
113
View as plain text