Source file src/cmd/vendor/golang.org/x/tools/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
     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  }
   102  

View as plain text