...

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

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

View as plain text