golang.org/x/tools@v0.21.0/go/analysis/passes/reflectvaluecompare/reflectvaluecompare.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     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  //go:embed doc.go
    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  // isReflectValue reports whether the type of e is reflect.Value.
    62  func isReflectValue(pass *analysis.Pass, e ast.Expr) bool {
    63  	tv, ok := pass.TypesInfo.Types[e]
    64  	if !ok { // no type info, something else is wrong
    65  		return false
    66  	}
    67  	// See if the type is reflect.Value
    68  	if !analysisutil.IsNamedType(tv.Type, "reflect", "Value") {
    69  		return false
    70  	}
    71  	if _, ok := e.(*ast.CompositeLit); ok {
    72  		// This is reflect.Value{}. Don't treat that as an error.
    73  		// Users should probably use x.IsValid() rather than x == reflect.Value{}, but the latter isn't wrong.
    74  		return false
    75  	}
    76  	return true
    77  }