gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/interfacer/types.go (about)

     1  // Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
     2  // See LICENSE for licensing information
     3  
     4  package interfacer
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"go/types"
    10  	"sort"
    11  	"strings"
    12  )
    13  
    14  type methoder interface {
    15  	NumMethods() int
    16  	Method(int) *types.Func
    17  }
    18  
    19  func methoderFuncMap(m methoder, skip bool) map[string]string {
    20  	ifuncs := make(map[string]string, m.NumMethods())
    21  	for i := 0; i < m.NumMethods(); i++ {
    22  		f := m.Method(i)
    23  		if !f.Exported() {
    24  			if skip {
    25  				continue
    26  			}
    27  			return nil
    28  		}
    29  		sign := f.Type().(*types.Signature)
    30  		ifuncs[f.Name()] = signString(sign)
    31  	}
    32  	return ifuncs
    33  }
    34  
    35  func typeFuncMap(t types.Type) map[string]string {
    36  	switch x := t.(type) {
    37  	case *types.Pointer:
    38  		return typeFuncMap(x.Elem())
    39  	case *types.Named:
    40  		u := x.Underlying()
    41  		if types.IsInterface(u) {
    42  			return typeFuncMap(u)
    43  		}
    44  		return methoderFuncMap(x, true)
    45  	case *types.Interface:
    46  		return methoderFuncMap(x, false)
    47  	default:
    48  		return nil
    49  	}
    50  }
    51  
    52  func funcMapString(iface map[string]string) string {
    53  	fnames := make([]string, 0, len(iface))
    54  	for fname := range iface {
    55  		fnames = append(fnames, fname)
    56  	}
    57  	sort.Strings(fnames)
    58  	var b bytes.Buffer
    59  	for i, fname := range fnames {
    60  		if i > 0 {
    61  			fmt.Fprint(&b, "; ")
    62  		}
    63  		fmt.Fprint(&b, fname, iface[fname])
    64  	}
    65  	return b.String()
    66  }
    67  
    68  func tupleJoin(buf *bytes.Buffer, t *types.Tuple) {
    69  	buf.WriteByte('(')
    70  	for i := 0; i < t.Len(); i++ {
    71  		if i > 0 {
    72  			buf.WriteString(", ")
    73  		}
    74  		buf.WriteString(t.At(i).Type().String())
    75  	}
    76  	buf.WriteByte(')')
    77  }
    78  
    79  // signString is similar to Signature.String(), but it ignores
    80  // param/result names.
    81  func signString(sign *types.Signature) string {
    82  	var buf bytes.Buffer
    83  	tupleJoin(&buf, sign.Params())
    84  	tupleJoin(&buf, sign.Results())
    85  	return buf.String()
    86  }
    87  
    88  func interesting(t types.Type) bool {
    89  	switch x := t.(type) {
    90  	case *types.Interface:
    91  		return x.NumMethods() > 1
    92  	case *types.Named:
    93  		if u := x.Underlying(); types.IsInterface(u) {
    94  			return interesting(u)
    95  		}
    96  		return x.NumMethods() >= 1
    97  	case *types.Pointer:
    98  		return interesting(x.Elem())
    99  	default:
   100  		return false
   101  	}
   102  }
   103  
   104  func anyInteresting(params *types.Tuple) bool {
   105  	for i := 0; i < params.Len(); i++ {
   106  		t := params.At(i).Type()
   107  		if interesting(t) {
   108  			return true
   109  		}
   110  	}
   111  	return false
   112  }
   113  
   114  func fromScope(scope *types.Scope) (ifaces map[string]string, funcs map[string]bool) {
   115  	ifaces = make(map[string]string)
   116  	funcs = make(map[string]bool)
   117  	for _, name := range scope.Names() {
   118  		tn, ok := scope.Lookup(name).(*types.TypeName)
   119  		if !ok {
   120  			continue
   121  		}
   122  		switch x := tn.Type().Underlying().(type) {
   123  		case *types.Interface:
   124  			iface := methoderFuncMap(x, false)
   125  			if len(iface) == 0 {
   126  				continue
   127  			}
   128  			for i := 0; i < x.NumMethods(); i++ {
   129  				f := x.Method(i)
   130  				sign := f.Type().(*types.Signature)
   131  				if !anyInteresting(sign.Params()) {
   132  					continue
   133  				}
   134  				funcs[signString(sign)] = true
   135  			}
   136  			s := funcMapString(iface)
   137  			if _, e := ifaces[s]; !e {
   138  				ifaces[s] = tn.Name()
   139  			}
   140  		case *types.Signature:
   141  			if !anyInteresting(x.Params()) {
   142  				continue
   143  			}
   144  			funcs[signString(x)] = true
   145  		}
   146  	}
   147  	return ifaces, funcs
   148  }
   149  
   150  func mentionsName(fname, name string) bool {
   151  	if len(name) < 2 {
   152  		return false
   153  	}
   154  	capit := strings.ToUpper(name[:1]) + name[1:]
   155  	lower := strings.ToLower(name)
   156  	return strings.Contains(fname, capit) || strings.HasPrefix(fname, lower)
   157  }
   158  
   159  func typeNamed(t types.Type) *types.Named {
   160  	for {
   161  		switch x := t.(type) {
   162  		case *types.Named:
   163  			return x
   164  		case *types.Pointer:
   165  			t = x.Elem()
   166  		default:
   167  			return nil
   168  		}
   169  	}
   170  }