...
1
2
3
4
5
6
7 package ifaceassert
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/inspector"
16 )
17
18 const Doc = `detect impossible interface-to-interface type assertions
19
20 This checker flags type assertions v.(T) and corresponding type-switch cases
21 in which the static type V of v is an interface that cannot possibly implement
22 the target interface T. This occurs when V and T contain methods with the same
23 name but different signatures. Example:
24
25 var v interface {
26 Read()
27 }
28 _ = v.(io.Reader)
29
30 The Read method in v has a different signature than the Read method in
31 io.Reader, so this assertion cannot succeed.
32 `
33
34 var Analyzer = &analysis.Analyzer{
35 Name: "ifaceassert",
36 Doc: Doc,
37 Requires: []*analysis.Analyzer{inspect.Analyzer},
38 Run: run,
39 }
40
41
42
43 func assertableTo(v, t types.Type) *types.Func {
44 if t == nil || v == nil {
45
46 return nil
47 }
48
49 V, _ := v.Underlying().(*types.Interface)
50 T, _ := t.Underlying().(*types.Interface)
51 if V == nil || T == nil {
52 return nil
53 }
54
55
56
57 if isParameterized(V) || isParameterized(T) {
58 return nil
59 }
60 if f, wrongType := types.MissingMethod(V, T, false); wrongType {
61 return f
62 }
63 return nil
64 }
65
66 func run(pass *analysis.Pass) (interface{}, error) {
67 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
68 nodeFilter := []ast.Node{
69 (*ast.TypeAssertExpr)(nil),
70 (*ast.TypeSwitchStmt)(nil),
71 }
72 inspect.Preorder(nodeFilter, func(n ast.Node) {
73 var (
74 assert *ast.TypeAssertExpr
75 targets []ast.Expr
76 )
77 switch n := n.(type) {
78 case *ast.TypeAssertExpr:
79
80 if n.Type == nil {
81 return
82 }
83 assert = n
84 targets = append(targets, n.Type)
85 case *ast.TypeSwitchStmt:
86
87 switch t := n.Assign.(type) {
88 case *ast.ExprStmt:
89 assert = t.X.(*ast.TypeAssertExpr)
90 case *ast.AssignStmt:
91 assert = t.Rhs[0].(*ast.TypeAssertExpr)
92 }
93
94 for _, c := range n.Body.List {
95 targets = append(targets, c.(*ast.CaseClause).List...)
96 }
97 }
98 V := pass.TypesInfo.TypeOf(assert.X)
99 for _, target := range targets {
100 T := pass.TypesInfo.TypeOf(target)
101 if f := assertableTo(V, T); f != nil {
102 pass.Reportf(
103 target.Pos(),
104 "impossible type assertion: no type can implement both %v and %v (conflicting types for %v method)",
105 V, T, f.Name(),
106 )
107 }
108 }
109 })
110 return nil, nil
111 }
112
View as plain text