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
51
52
53
54 if info.Types[expr].IsNil() {
55 continue
56 }
57
58 if !typesinternal.NoEffects(info, expr) {
59 continue
60 }
61
62 t := info.TypeOf(expr)
63 var edits []analysis.TextEdit
64
65
66
67 if astutil.IsChildOf(curCall, edge.SelectorExpr_X) {
68 curSel := unparenEnclosing(curCall).Parent()
69 if astutil.IsChildOf(curSel, edge.CallExpr_Fun) {
70 call2 := unparenEnclosing(curSel).Parent().Node().(*ast.CallExpr)
71 obj := typeutil.Callee(info, call2)
72 if typesinternal.IsMethodNamed(obj, "reflect", "Type", "Elem") {
73 if ptr, ok := t.(*types.Pointer); ok {
74
75 t = ptr.Elem()
76
77
78
79 edits = []analysis.TextEdit{{
80 Pos: call.End(),
81 End: call2.End(),
82 }}
83 }
84 }
85 }
86 }
87
88
89
90
91 if types.IsInterface(t) && edits == nil {
92 continue
93 }
94
95 file := astutil.EnclosingFile(curCall)
96 if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_22) {
97 continue
98 }
99 tokFile := pass.Fset.File(file.Pos())
100
101
102
103
104
105 qual := typesinternal.FileQualifier(file, pass.Pkg)
106 tstr := types.TypeString(t, qual)
107
108 sel, ok := call.Fun.(*ast.SelectorExpr)
109 if !ok {
110 continue
111 }
112
113
114
115
116
117
118 curArg0 := curCall.ChildAt(edge.CallExpr_Args, 0)
119 edits = append(edits, refactor.DeleteUnusedVars(index, info, tokFile, curArg0)...)
120
121 pass.Report(analysis.Diagnostic{
122 Pos: call.Fun.Pos(),
123 End: call.Fun.End(),
124 Message: "reflect.TypeOf call can be simplified using TypeFor",
125 SuggestedFixes: []analysis.SuggestedFix{{
126
127
128
129 Message: "Replace TypeOf by TypeFor",
130 TextEdits: append([]analysis.TextEdit{
131 {
132 Pos: sel.Sel.Pos(),
133 End: sel.Sel.End(),
134 NewText: []byte("TypeFor[" + tstr + "]"),
135 },
136
137 {
138 Pos: call.Lparen + 1,
139 End: call.Rparen,
140 },
141 }, edits...),
142 }},
143 })
144 }
145
146 return nil, nil
147 }
148
View as plain text