GoPLS Viewer

Home|gopls/go/analysis/passes/ifaceassert/ifaceassert.go
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 defines an Analyzer that flags
6// impossible interface-interface type assertions.
7package ifaceassert
8
9import (
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
18const Doc = `detect impossible interface-to-interface type assertions
19
20This checker flags type assertions v.(T) and corresponding type-switch cases
21in which the static type V of v is an interface that cannot possibly implement
22the target interface T. This occurs when V and T contain methods with the same
23name but different signatures. Example:
24
25    var v interface {
26        Read()
27    }
28    _ = v.(io.Reader)
29
30The Read method in v has a different signature than the Read method in
31io.Reader, so this assertion cannot succeed.
32`
33
34var Analyzer = &analysis.Analyzer{
35    Name:     "ifaceassert",
36    Doc:      Doc,
37    Requires: []*analysis.Analyzer{inspect.Analyzer},
38    Run:      run,
39}
40
41// assertableTo checks whether interface v can be asserted into t. It returns
42// nil on success, or the first conflicting method on failure.
43func assertableTo(vt types.Type) *types.Func {
44    if t == nil || v == nil {
45        // not assertable to, but there is no missing method
46        return nil
47    }
48    // ensure that v and t are interfaces
49    V_ := v.Underlying().(*types.Interface)
50    T_ := t.Underlying().(*types.Interface)
51    if V == nil || T == nil {
52        return nil
53    }
54
55    // Mitigations for interface comparisons and generics.
56    // TODO(https://github.com/golang/go/issues/50658): Support more precise conclusion.
57    if isParameterized(V) || isParameterized(T) {
58        return nil
59    }
60    if fwrongType := types.MissingMethod(VTfalse); wrongType {
61        return f
62    }
63    return nil
64}
65
66func 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 // v.(T) expression
75            targets []ast.Expr          // interfaces T in v.(T)
76        )
77        switch n := n.(type) {
78        case *ast.TypeAssertExpr:
79            // take care of v.(type) in *ast.TypeSwitchStmt
80            if n.Type == nil {
81                return
82            }
83            assert = n
84            targets = append(targetsn.Type)
85        case *ast.TypeSwitchStmt:
86            // retrieve type assertion from type switch's 'assign' field
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            // gather target types from case clauses
94            for _c := range n.Body.List {
95                targets = append(targetsc.(*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(VT); 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                    VTf.Name(),
106                )
107            }
108        }
109    })
110    return nilnil
111}
112
MembersX
types
inspect
run.BlockStmt.V
run.BlockStmt.RangeStmt_2885.BlockStmt.f
inspector
assertableTo
run
run.BlockStmt.BlockStmt.RangeStmt_2744.c
run.BlockStmt.RangeStmt_2885.target
analysis
assertableTo.v
assertableTo.t
assertableTo.wrongType
run.pass
run.nodeFilter
run.BlockStmt.assert
ast
Doc
assertableTo.f
run.BlockStmt.targets
run.BlockStmt.RangeStmt_2885.BlockStmt.T
Members
X