golang.org/x/tools@v0.21.0/go/analysis/passes/ifaceassert/ifaceassert.go (about) 1 // Copyright 2020 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 ifaceassert 6 7 import ( 8 _ "embed" 9 "go/ast" 10 "go/types" 11 12 "golang.org/x/tools/go/analysis" 13 "golang.org/x/tools/go/analysis/passes/inspect" 14 "golang.org/x/tools/go/analysis/passes/internal/analysisutil" 15 "golang.org/x/tools/go/ast/inspector" 16 "golang.org/x/tools/internal/typeparams" 17 ) 18 19 //go:embed doc.go 20 var doc string 21 22 var Analyzer = &analysis.Analyzer{ 23 Name: "ifaceassert", 24 Doc: analysisutil.MustExtractDoc(doc, "ifaceassert"), 25 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/ifaceassert", 26 Requires: []*analysis.Analyzer{inspect.Analyzer}, 27 Run: run, 28 } 29 30 // assertableTo checks whether interface v can be asserted into t. It returns 31 // nil on success, or the first conflicting method on failure. 32 func assertableTo(free *typeparams.Free, v, t types.Type) *types.Func { 33 if t == nil || v == nil { 34 // not assertable to, but there is no missing method 35 return nil 36 } 37 // ensure that v and t are interfaces 38 V, _ := v.Underlying().(*types.Interface) 39 T, _ := t.Underlying().(*types.Interface) 40 if V == nil || T == nil { 41 return nil 42 } 43 44 // Mitigations for interface comparisons and generics. 45 // TODO(https://github.com/golang/go/issues/50658): Support more precise conclusion. 46 if free.Has(V) || free.Has(T) { 47 return nil 48 } 49 if f, wrongType := types.MissingMethod(V, T, false); wrongType { 50 return f 51 } 52 return nil 53 } 54 55 func run(pass *analysis.Pass) (interface{}, error) { 56 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 57 nodeFilter := []ast.Node{ 58 (*ast.TypeAssertExpr)(nil), 59 (*ast.TypeSwitchStmt)(nil), 60 } 61 var free typeparams.Free 62 inspect.Preorder(nodeFilter, func(n ast.Node) { 63 var ( 64 assert *ast.TypeAssertExpr // v.(T) expression 65 targets []ast.Expr // interfaces T in v.(T) 66 ) 67 switch n := n.(type) { 68 case *ast.TypeAssertExpr: 69 // take care of v.(type) in *ast.TypeSwitchStmt 70 if n.Type == nil { 71 return 72 } 73 assert = n 74 targets = append(targets, n.Type) 75 case *ast.TypeSwitchStmt: 76 // retrieve type assertion from type switch's 'assign' field 77 switch t := n.Assign.(type) { 78 case *ast.ExprStmt: 79 assert = t.X.(*ast.TypeAssertExpr) 80 case *ast.AssignStmt: 81 assert = t.Rhs[0].(*ast.TypeAssertExpr) 82 } 83 // gather target types from case clauses 84 for _, c := range n.Body.List { 85 targets = append(targets, c.(*ast.CaseClause).List...) 86 } 87 } 88 V := pass.TypesInfo.TypeOf(assert.X) 89 for _, target := range targets { 90 T := pass.TypesInfo.TypeOf(target) 91 if f := assertableTo(&free, V, T); f != nil { 92 pass.Reportf( 93 target.Pos(), 94 "impossible type assertion: no type can implement both %v and %v (conflicting types for %v method)", 95 V, T, f.Name(), 96 ) 97 } 98 } 99 }) 100 return nil, nil 101 }