gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/mvdan.cc/interfacer/check/types.go (about) 1 // Copyright (c) 2015, Daniel Martà <mvdan@mvdan.cc> 2 // See LICENSE for licensing information 3 4 package check 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 }