github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/plugin/checker/internal/asthelpers/helpers.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package asthelpers
     5  
     6  import (
     7  	"go/ast"
     8  	"go/types"
     9  
    10  	"github.com/pkg/errors"
    11  	"golang.org/x/tools/go/packages"
    12  )
    13  
    14  func GetPackage(pkgPath string) (*packages.Package, error) {
    15  	cfg := &packages.Config{
    16  		Mode: packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo,
    17  	}
    18  	pkgs, err := packages.Load(cfg, pkgPath)
    19  	if err != nil {
    20  		return nil, err
    21  	}
    22  
    23  	if len(pkgs) == 0 {
    24  		return nil, errors.Errorf("could not find package %s", pkgPath)
    25  	}
    26  	return pkgs[0], nil
    27  }
    28  
    29  func FindInterface(name string, files []*ast.File) (*ast.InterfaceType, error) {
    30  	iface, _, err := FindInterfaceWithIdent(name, files)
    31  	return iface, err
    32  }
    33  
    34  func FindInterfaceWithIdent(name string, files []*ast.File) (*ast.InterfaceType, *ast.Ident, error) {
    35  	var (
    36  		ident *ast.Ident
    37  		iface *ast.InterfaceType
    38  	)
    39  
    40  	for _, f := range files {
    41  		ast.Inspect(f, func(n ast.Node) bool {
    42  			if t, ok := n.(*ast.TypeSpec); ok {
    43  				if iface != nil {
    44  					return false
    45  				}
    46  
    47  				if i, ok := t.Type.(*ast.InterfaceType); ok && t.Name.Name == name {
    48  					ident = t.Name
    49  					iface = i
    50  					return false
    51  				}
    52  			}
    53  			return true
    54  		})
    55  
    56  		if iface != nil {
    57  			return iface, ident, nil
    58  		}
    59  	}
    60  	return nil, nil, errors.Errorf("could not find %s interface", name)
    61  }
    62  
    63  func FindMethodsCalledOnType(info *types.Info, typ types.Type, caller *ast.FuncDecl) []string {
    64  	var methods []string
    65  
    66  	ast.Inspect(caller, func(n ast.Node) bool {
    67  		if s, ok := n.(*ast.SelectorExpr); ok {
    68  
    69  			var receiver *ast.Ident
    70  			switch r := s.X.(type) {
    71  			case *ast.Ident:
    72  				// Left-hand side of the selector is an identifier, eg:
    73  				//
    74  				//   a := p.API
    75  				//   a.GetTeams()
    76  				//
    77  				receiver = r
    78  			case *ast.SelectorExpr:
    79  				// Left-hand side of the selector is a selector, eg:
    80  				//
    81  				//   p.API.GetTeams()
    82  				//
    83  				receiver = r.Sel
    84  			}
    85  
    86  			if receiver != nil {
    87  				obj := info.ObjectOf(receiver)
    88  				if obj != nil && types.Identical(obj.Type(), typ) {
    89  					methods = append(methods, s.Sel.Name)
    90  				}
    91  				return false
    92  			}
    93  
    94  		}
    95  		return true
    96  	})
    97  
    98  	return methods
    99  }
   100  
   101  func FindReceiverMethods(receiverName string, files []*ast.File) []*ast.FuncDecl {
   102  	var fns []*ast.FuncDecl
   103  	for _, f := range files {
   104  		ast.Inspect(f, func(n ast.Node) bool {
   105  			if fn, ok := n.(*ast.FuncDecl); ok {
   106  				r := extractReceiverTypeName(fn)
   107  				if r == receiverName {
   108  					fns = append(fns, fn)
   109  				}
   110  			}
   111  			return true
   112  		})
   113  	}
   114  	return fns
   115  }
   116  
   117  func extractReceiverTypeName(fn *ast.FuncDecl) string {
   118  	if fn.Recv != nil {
   119  		t := fn.Recv.List[0].Type
   120  		// Unwrap the pointer type (a star expression)
   121  		if se, ok := t.(*ast.StarExpr); ok {
   122  			t = se.X
   123  		}
   124  		if id, ok := t.(*ast.Ident); ok {
   125  			return id.Name
   126  		}
   127  	}
   128  	return ""
   129  }