1
2
3
4
5 package modernize
6
7
8
9 import (
10 "go/ast"
11 "go/types"
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/go/types/typeutil"
17 "golang.org/x/tools/internal/analysis/analyzerutil"
18 typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex"
19 "golang.org/x/tools/internal/astutil"
20 "golang.org/x/tools/internal/refactor"
21 "golang.org/x/tools/internal/typesinternal"
22 "golang.org/x/tools/internal/typesinternal/typeindex"
23 "golang.org/x/tools/internal/versions"
24 )
25
26 var ReflectTypeForAnalyzer = &analysis.Analyzer{
27 Name: "reflecttypefor",
28 Doc: analyzerutil.MustExtractDoc(doc, "reflecttypefor"),
29 Requires: []*analysis.Analyzer{
30 inspect.Analyzer,
31 typeindexanalyzer.Analyzer,
32 },
33 Run: reflecttypefor,
34 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#reflecttypefor",
35 }
36
37 func reflecttypefor(pass *analysis.Pass) (any, error) {
38 var (
39 index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)
40 info = pass.TypesInfo
41
42 reflectTypeOf = index.Object("reflect", "TypeOf")
43 )
44
45 for curCall := range index.Calls(reflectTypeOf) {
46 call := curCall.Node().(*ast.CallExpr)
47
48
49 expr := call.Args[0]
50 if !typesinternal.NoEffects(info, expr) {
51 continue
52 }
53
54 t := info.TypeOf(expr)
55 var edits []analysis.TextEdit
56
57
58
59 if astutil.IsChildOf(curCall, edge.SelectorExpr_X) {
60 curSel := unparenEnclosing(curCall).Parent()
61 if astutil.IsChildOf(curSel, edge.CallExpr_Fun) {
62 call2 := unparenEnclosing(curSel).Parent().Node().(*ast.CallExpr)
63 obj := typeutil.Callee(info, call2)
64 if typesinternal.IsMethodNamed(obj, "reflect", "Type", "Elem") {
65 if ptr, ok := t.(*types.Pointer); ok {
66
67 t = ptr.Elem()
68
69
70
71 edits = []analysis.TextEdit{{
72 Pos: call.End(),
73 End: call2.End(),
74 }}
75 }
76 }
77 }
78 }
79
80
81
82
83 if types.IsInterface(t) && edits == nil {
84 continue
85 }
86
87 file := astutil.EnclosingFile(curCall)
88 if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_22) {
89 continue
90 }
91 tokFile := pass.Fset.File(file.Pos())
92
93
94
95
96
97 qual := typesinternal.FileQualifier(file, pass.Pkg)
98 tstr := types.TypeString(t, qual)
99
100 sel, ok := call.Fun.(*ast.SelectorExpr)
101 if !ok {
102 continue
103 }
104
105
106
107
108
109
110 curArg0 := curCall.ChildAt(edge.CallExpr_Args, 0)
111 edits = append(edits, refactor.DeleteUnusedVars(index, info, tokFile, curArg0)...)
112
113 pass.Report(analysis.Diagnostic{
114 Pos: call.Fun.Pos(),
115 End: call.Fun.End(),
116 Message: "reflect.TypeOf call can be simplified using TypeFor",
117 SuggestedFixes: []analysis.SuggestedFix{{
118
119
120
121 Message: "Replace TypeOf by TypeFor",
122 TextEdits: append([]analysis.TextEdit{
123 {
124 Pos: sel.Sel.Pos(),
125 End: sel.Sel.End(),
126 NewText: []byte("TypeFor[" + tstr + "]"),
127 },
128
129 {
130 Pos: call.Lparen + 1,
131 End: call.Rparen,
132 },
133 }, edits...),
134 }},
135 })
136 }
137
138 return nil, nil
139 }
140
View as plain text