github.com/prysmaticlabs/prysm@v1.4.4/tools/analyzers/interfacechecker/analyzer.go (about) 1 // Package interfacechecker implements a static analyzer to prevent incorrect conditional checks on select interfaces. 2 package interfacechecker 3 4 import ( 5 "errors" 6 "go/ast" 7 "go/token" 8 "go/types" 9 "strings" 10 11 "golang.org/x/tools/go/analysis" 12 "golang.org/x/tools/go/analysis/passes/inspect" 13 "golang.org/x/tools/go/ast/inspector" 14 ) 15 16 // Doc explaining the tool. 17 const Doc = "Enforce usage of proper conditional check for interfaces" 18 19 // Analyzer runs static analysis. 20 var Analyzer = &analysis.Analyzer{ 21 Name: "interfacechecker", 22 Doc: Doc, 23 Requires: []*analysis.Analyzer{inspect.Analyzer}, 24 Run: run, 25 } 26 27 // These are the selected interfaces that we want to parse through and check nilness for. 28 var selectedInterfaces = []string{ 29 "interfaces.SignedBeaconBlock", 30 "interfaces.MetadataV0", 31 "interface.BeaconState", 32 "interface.ReadOnlyBeaconState", 33 "interface.WriteOnlyBeaconState", 34 } 35 36 func run(pass *analysis.Pass) (interface{}, error) { 37 inspection, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 38 if !ok { 39 return nil, errors.New("analyzer is not type *inspector.Inspector") 40 } 41 42 nodeFilter := []ast.Node{ 43 (*ast.IfStmt)(nil), 44 } 45 46 inspection.Preorder(nodeFilter, func(node ast.Node) { 47 stmt, ok := node.(*ast.IfStmt) 48 if !ok { 49 return 50 } 51 exp, ok := stmt.Cond.(*ast.BinaryExpr) 52 if !ok { 53 return 54 } 55 handleConditionalExpression(exp, pass) 56 }) 57 58 return nil, nil 59 } 60 61 func handleConditionalExpression(exp *ast.BinaryExpr, pass *analysis.Pass) { 62 identX, ok := exp.X.(*ast.Ident) 63 if !ok { 64 return 65 } 66 identY, ok := exp.Y.(*ast.Ident) 67 if !ok { 68 return 69 } 70 typeMap := pass.TypesInfo.Types 71 if _, ok := typeMap[identX].Type.(*types.Slice); ok { 72 return 73 } 74 if _, ok := typeMap[identY].Type.(*types.Slice); ok { 75 return 76 } 77 for _, iface := range selectedInterfaces { 78 xIsIface := strings.Contains(typeMap[identX].Type.String(), iface) 79 xIsNil := typeMap[identX].IsNil() 80 yisIface := strings.Contains(typeMap[identY].Type.String(), iface) 81 yIsNil := typeMap[identY].IsNil() 82 // Exit early if neither are of the desired interface 83 if !xIsIface && !yisIface { 84 continue 85 } 86 if xIsIface && yIsNil { 87 reportFailure(identX.Pos(), pass) 88 } 89 if yisIface && xIsNil { 90 reportFailure(identY.Pos(), pass) 91 } 92 } 93 } 94 95 func reportFailure(pos token.Pos, pass *analysis.Pass) { 96 pass.Reportf(pos, "A single nilness check is being performed on an interface"+ 97 ", this check needs another accompanying nilness check on the underlying object for the interface.") 98 }