...

Source file src/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go

Documentation: golang.org/x/tools/go/analysis/passes/ifaceassert

     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.
     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  // assertableTo checks whether interface v can be asserted into t. It returns
    42  // nil on success, or the first conflicting method on failure.
    43  func assertableTo(v, t 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 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 // 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(targets, n.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(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