...
1
2
3
4
5 package reflectvaluecompare
6
7 import (
8 _ "embed"
9 "go/ast"
10 "go/token"
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/analysis/passes/internal/analysisutil"
16 "golang.org/x/tools/go/ast/inspector"
17 "golang.org/x/tools/go/types/typeutil"
18 )
19
20
21 var doc string
22
23 var Analyzer = &analysis.Analyzer{
24 Name: "reflectvaluecompare",
25 Doc: analysisutil.MustExtractDoc(doc, "reflectvaluecompare"),
26 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/reflectvaluecompare",
27 Requires: []*analysis.Analyzer{inspect.Analyzer},
28 Run: run,
29 }
30
31 func run(pass *analysis.Pass) (interface{}, error) {
32 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
33
34 nodeFilter := []ast.Node{
35 (*ast.BinaryExpr)(nil),
36 (*ast.CallExpr)(nil),
37 }
38 inspect.Preorder(nodeFilter, func(n ast.Node) {
39 switch n := n.(type) {
40 case *ast.BinaryExpr:
41 if n.Op != token.EQL && n.Op != token.NEQ {
42 return
43 }
44 if isReflectValue(pass, n.X) || isReflectValue(pass, n.Y) {
45 if n.Op == token.EQL {
46 pass.ReportRangef(n, "avoid using == with reflect.Value")
47 } else {
48 pass.ReportRangef(n, "avoid using != with reflect.Value")
49 }
50 }
51 case *ast.CallExpr:
52 fn, _ := typeutil.Callee(pass.TypesInfo, n).(*types.Func)
53 if analysisutil.IsFunctionNamed(fn, "reflect", "DeepEqual") && (isReflectValue(pass, n.Args[0]) || isReflectValue(pass, n.Args[1])) {
54 pass.ReportRangef(n, "avoid using reflect.DeepEqual with reflect.Value")
55 }
56 }
57 })
58 return nil, nil
59 }
60
61
62 func isReflectValue(pass *analysis.Pass, e ast.Expr) bool {
63 tv, ok := pass.TypesInfo.Types[e]
64 if !ok {
65 return false
66 }
67
68 if !analysisutil.IsNamedType(tv.Type, "reflect", "Value") {
69 return false
70 }
71 if _, ok := e.(*ast.CompositeLit); ok {
72
73
74 return false
75 }
76 return true
77 }
78
View as plain text