├── .gitignore ├── LICENSE ├── README.md ├── README_cn.md ├── builtinfunc.go ├── builtinfunc_string.go ├── chandir.go ├── chandir_string.go ├── check.go ├── check_test.go ├── cmd └── pkgimport │ ├── README.md │ └── main.go ├── comment_test.go ├── constant.go ├── go.mod ├── importer.go ├── info.go ├── kind.go ├── kind_string.go ├── misc.go ├── option.go ├── other_test.go ├── parser.go ├── parser_types.go ├── testdata ├── kind │ └── a.go ├── pkg │ ├── a.go │ └── b.go ├── type │ ├── a.go │ └── b.go └── value │ ├── a.go │ └── b.go ├── types.go ├── types_alias.go ├── types_array.go ├── types_builtin.go ├── types_chan.go ├── types_comment_locator.go ├── types_declaration.go ├── types_func.go ├── types_importer.go ├── types_interface.go ├── types_map.go ├── types_named.go ├── types_origin.go ├── types_ptr.go ├── types_scope.go ├── types_selector.go ├── types_slice.go ├── types_struct.go ├── types_struct_field.go ├── types_tuple.go ├── types_value_bind.go ├── types_value_pair.go └── utils_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | .vscode 17 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-NOW wzshiming 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gotype 2 | 3 | Golang source code parsing, usage like reflect package 4 | 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/wzshiming/gotype)](https://goreportcard.com/report/github.com/wzshiming/gotype) 6 | [![GoDoc](https://godoc.org/github.com/wzshiming/gotype?status.svg)](https://godoc.org/github.com/wzshiming/gotype) 7 | [![GitHub license](https://img.shields.io/github/license/wzshiming/gotype.svg)](https://github.com/wzshiming/gotype/blob/master/LICENSE) 8 | 9 | - [English](https://github.com/wzshiming/gotype/blob/master/README.md) 10 | - [简体中文](https://github.com/wzshiming/gotype/blob/master/README_cn.md) 11 | 12 | ## Usage 13 | 14 | [API Documentation](https://godoc.org/github.com/wzshiming/gotype) 15 | 16 | [Examples](https://github.com/wzshiming/gotype/blob/master/cmd/pkgimport/main.go) 17 | 18 | ## TODO 19 | 20 | - Supports generics 21 | 22 | ## License 23 | 24 | Licensed under the MIT License. See [LICENSE](https://github.com/wzshiming/gotype/blob/master/LICENSE) for the full license text. 25 | -------------------------------------------------------------------------------- /README_cn.md: -------------------------------------------------------------------------------- 1 | # gotype 2 | 3 | Golang 源代码解析,像反射包一样使用 4 | 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/wzshiming/gotype)](https://goreportcard.com/report/github.com/wzshiming/gotype) 6 | [![GoDoc](https://godoc.org/github.com/wzshiming/gotype?status.svg)](https://godoc.org/github.com/wzshiming/gotype) 7 | [![GitHub license](https://img.shields.io/github/license/wzshiming/gotype.svg)](https://github.com/wzshiming/gotype/blob/master/LICENSE) 8 | 9 | - [English](https://github.com/wzshiming/gotype/blob/master/README.md) 10 | - [简体中文](https://github.com/wzshiming/gotype/blob/master/README_cn.md) 11 | 12 | ## 用法 13 | 14 | [API 文档](https://godoc.org/github.com/wzshiming/gotype) 15 | 16 | [示例](https://github.com/wzshiming/gotype/blob/master/cmd/pkgimport/main.go) 17 | 18 | ## TODO 19 | 20 | - 支持泛型 21 | 22 | ## 许可证 23 | 24 | 软包根据MIT License。有关完整的许可证文本,请参阅[LICENSE](https://github.com/wzshiming/gotype/blob/master/LICENSE)。 25 | -------------------------------------------------------------------------------- /builtinfunc.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | //go:generate stringer -type builtinfunc builtinfunc.go 4 | 5 | type builtinfunc uint8 6 | 7 | // Built-in function results 8 | const ( 9 | _ builtinfunc = iota 10 | builtinfuncInt // Returns int 11 | builtinfuncPtrItem // Returns the type pointer of the first parameter 12 | builtinfuncItem // Returns the first parameter 13 | builtinfuncInterface // Returns interface 14 | builtinfuncVoid // Returns void 15 | ) 16 | -------------------------------------------------------------------------------- /builtinfunc_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type builtinfunc builtinfunc.go"; DO NOT EDIT. 2 | 3 | package gotype 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[builtinfuncInt-1] 12 | _ = x[builtinfuncPtrItem-2] 13 | _ = x[builtinfuncItem-3] 14 | _ = x[builtinfuncInterface-4] 15 | _ = x[builtinfuncVoid-5] 16 | } 17 | 18 | const _builtinfunc_name = "builtinfuncIntbuiltinfuncPtrItembuiltinfuncItembuiltinfuncInterfacebuiltinfuncVoid" 19 | 20 | var _builtinfunc_index = [...]uint8{0, 14, 32, 47, 67, 82} 21 | 22 | func (i builtinfunc) String() string { 23 | i -= 1 24 | if i >= builtinfunc(len(_builtinfunc_index)-1) { 25 | return "builtinfunc(" + strconv.FormatInt(int64(i+1), 10) + ")" 26 | } 27 | return _builtinfunc_name[_builtinfunc_index[i]:_builtinfunc_index[i+1]] 28 | } 29 | -------------------------------------------------------------------------------- /chandir.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | //go:generate stringer -type ChanDir chandir.go 4 | 5 | // ChanDir represents a channel type's direction. 6 | type ChanDir int 7 | 8 | // Define channel direction 9 | const ( 10 | RecvDir ChanDir = 1 << iota // chan<- 11 | SendDir // <-chan 12 | BothDir ChanDir = RecvDir | SendDir // chan 13 | ) 14 | -------------------------------------------------------------------------------- /chandir_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type ChanDir chandir.go"; DO NOT EDIT. 2 | 3 | package gotype 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[RecvDir-1] 12 | _ = x[SendDir-2] 13 | _ = x[BothDir-3] 14 | } 15 | 16 | const _ChanDir_name = "RecvDirSendDirBothDir" 17 | 18 | var _ChanDir_index = [...]uint8{0, 7, 14, 21} 19 | 20 | func (i ChanDir) String() string { 21 | i -= 1 22 | if i < 0 || i >= ChanDir(len(_ChanDir_index)-1) { 23 | return "ChanDir(" + strconv.FormatInt(int64(i+1), 10) + ")" 24 | } 25 | return _ChanDir_name[_ChanDir_index[i]:_ChanDir_index[i+1]] 26 | } 27 | -------------------------------------------------------------------------------- /check.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Equal Reports whether the type is equal 8 | // Deprecated: should use Identical instead. 9 | func Equal(t0 Type, t1 Type) bool { 10 | return Identical(t0, t1) 11 | } 12 | 13 | // Implements reports whether type inter implements interface t. 14 | func Implements(t Type, inter Type) bool { 15 | if inter.Kind() == Declaration { 16 | inter = inter.Declaration() 17 | } 18 | 19 | if inter.Kind() != Interface { 20 | return false 21 | } 22 | 23 | if t.Kind() == Declaration { 24 | t = t.Declaration() 25 | } 26 | 27 | lenMet0 := inter.NumField() 28 | for i := 0; i != lenMet0; i++ { 29 | m0 := inter.Field(i) 30 | name := m0.Name() 31 | m := m0.Declaration() 32 | switch m.Kind() { 33 | case Func: 34 | m1, ok := t.MethodByName(name) 35 | if !ok { 36 | return false 37 | } 38 | 39 | if !Identical(m0, m1) { 40 | return false 41 | } 42 | case Interface: 43 | if !Implements(t, m0) { 44 | return false 45 | } 46 | } 47 | } 48 | return true 49 | } 50 | 51 | // Identical reports whether t0 and t1 are identical types. 52 | func Identical(t0, t1 Type) bool { 53 | return identical(t0, t1, true) 54 | } 55 | 56 | // IdenticalIgnoreTags reports whether t0 and t1 are identical types if tags are ignored. 57 | func IdenticalIgnoreTags(t0, t1 Type) bool { 58 | return identical(t0, t1, false) 59 | } 60 | 61 | func identical(t0, t1 Type, cmpTags bool) bool { 62 | if t0 == t1 { 63 | return true 64 | } 65 | if t0.PkgPath() == t1.PkgPath() && t0.Name() == t1.Name() { 66 | return true 67 | } 68 | k0 := t0.Kind() 69 | k1 := t1.Kind() 70 | if k0 != k1 { 71 | return false 72 | } 73 | 74 | switch k0 { 75 | case Bool, 76 | Int, Int8, Int16, Int32, Int64, 77 | Uint, Uint8, Uint16, Uint32, Uint64, 78 | Uintptr, 79 | Float32, Float64, 80 | Complex64, Complex128, 81 | String, Byte, Rune, Error: 82 | return true 83 | case Slice, Ptr: 84 | return identical(t0.Elem(), t1.Elem(), cmpTags) 85 | case Array: 86 | return t0.Len() == t1.Len() && identical(t0.Elem(), t1.Elem(), cmpTags) 87 | case Chan: 88 | return t0.ChanDir() == t1.ChanDir() && identical(t0.Elem(), t1.Elem(), cmpTags) 89 | case Map: 90 | return identical(t0.Key(), t1.Key(), cmpTags) && identical(t0.Elem(), t1.Elem(), cmpTags) 91 | case Field: 92 | if cmpTags && t0.Tag() != t1.Tag() { 93 | return false 94 | } 95 | return identical(t0.Elem(), t1.Elem(), cmpTags) 96 | case Scope: 97 | return t0.PkgPath() == t1.PkgPath() 98 | case Declaration: 99 | return identical(t0.Declaration(), t1.Declaration(), cmpTags) 100 | case Func: 101 | numIn0 := t0.NumIn() 102 | numIn1 := t1.NumIn() 103 | if numIn0 != numIn1 { 104 | return false 105 | } 106 | for i := 0; i != numIn0; i++ { 107 | if !identical(t0.In(i), t1.In(i), cmpTags) { 108 | return false 109 | } 110 | } 111 | 112 | numOut0 := t0.NumOut() 113 | numOut1 := t1.NumOut() 114 | if numOut0 != numOut1 { 115 | return false 116 | } 117 | for i := 0; i != numOut0; i++ { 118 | if !identical(t0.Out(i), t1.Out(i), cmpTags) { 119 | return false 120 | } 121 | } 122 | return true 123 | case Interface: 124 | numMethod0 := t0.NumMethod() 125 | numMethod1 := t1.NumMethod() 126 | if numMethod0 != numMethod1 { 127 | return false 128 | } 129 | for i := 0; i != numMethod0; i++ { 130 | if !identical(t0.Method(i), t1.Method(i), cmpTags) { 131 | return false 132 | } 133 | } 134 | return true 135 | case Struct: 136 | numField0 := t0.NumField() 137 | numField1 := t1.NumField() 138 | if numField0 != numField1 { 139 | return false 140 | } 141 | for i := 0; i != numField0; i++ { 142 | if !identical(t0.Field(i), t1.Field(i), cmpTags) { 143 | return false 144 | } 145 | } 146 | return true 147 | } 148 | return false 149 | } 150 | 151 | // IsBuiltin return it is a built-in base type 152 | func IsBuiltin(t Type) bool { 153 | kind := t.Kind() 154 | if kind <= predeclaredTypesBeg || kind >= predeclaredTypesEnd { 155 | return false 156 | } 157 | return t.Name() == strings.ToLower(kind.String()) 158 | } 159 | -------------------------------------------------------------------------------- /check_test.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestImplements(t *testing.T) { 8 | 9 | const src = ` 10 | package a 11 | 12 | import "time" 13 | var now = time.Now() 14 | ` 15 | scope := Parse(t, src) 16 | 17 | typ, ok := scope.ChildByName("now") 18 | if !ok { 19 | t.Fail() 20 | } 21 | 22 | scopeTime := Import(t, "time") 23 | val, ok := scopeTime.ChildByName("Time") 24 | if !ok { 25 | t.Fail() 26 | return 27 | } 28 | 29 | if !Identical(typ.Declaration().Declaration(), val) { 30 | t.Fail() 31 | } 32 | 33 | scopeEncodin := Import(t, "encoding") 34 | inter, ok := scopeEncodin.ChildByName("TextMarshaler") 35 | if !ok { 36 | t.Fail() 37 | return 38 | } 39 | 40 | if !Implements(val, inter) { 41 | t.Fail() 42 | } 43 | } 44 | 45 | func TestIdentical(t *testing.T) { 46 | var src = `package a 47 | import time "time" 48 | 49 | type A struct { 50 | String string 51 | Int int 52 | Array [8]byte 53 | Channel chan int 54 | Interface interface{} 55 | Map map[string]string 56 | Time time.Time 57 | } 58 | type B struct { 59 | String string 60 | Int int 61 | Array [8]byte 62 | Channel chan int 63 | Interface interface{} 64 | Map map[string]string 65 | Time time.Time 66 | } 67 | ` 68 | 69 | scopeSrc := Parse(t, src) 70 | a, _ := scopeSrc.ChildByName("A") 71 | b, _ := scopeSrc.ChildByName("B") 72 | if !Identical(a, b) { 73 | t.Fail() 74 | return 75 | } 76 | } 77 | 78 | func TestEqual(t *testing.T) { 79 | scopeTime := Import(t, "time") 80 | 81 | val, ok := scopeTime.ChildByName("Time") 82 | if !ok { 83 | t.Fail() 84 | return 85 | } 86 | 87 | now, ok := scopeTime.ChildByName("Now") 88 | if !ok { 89 | t.Fail() 90 | return 91 | } 92 | 93 | if !Equal(val, now.Declaration().Out(0).Declaration()) { 94 | t.Fail() 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /cmd/pkgimport/README.md: -------------------------------------------------------------------------------- 1 | # pkgimport 2 | 3 | ## Requirements 4 | 5 | Go version >= 1.9. 6 | 7 | ## Download and install 8 | 9 | ``` shell 10 | go get -u -v github.com/wzshiming/gotype/cmd/pkgimport 11 | ``` 12 | 13 | ## Commands 14 | 15 | import "mime" 16 | 17 | ``` shell 18 | pkgimport -p mime -i mime -o mime.go 19 | ``` 20 | 21 | or 22 | 23 | ``` shell 24 | go generate 25 | ``` 26 | 27 | ``` golang 28 | // Code generated by "pkgimport -p mime -i mime -o mime.go"; DO NOT EDIT. 29 | // Install by "go get -u -v github.com/wzshiming/gotype/cmd/pkgimport"; 30 | //go:generate pkgimport -p mime -i mime -o mime.go 31 | 32 | package mime 33 | 34 | import ( 35 | origin "mime" 36 | ) 37 | 38 | // Value 39 | var ( 40 | 41 | // QEncoding represents the Q-encoding scheme as defined by RFC 2047. 42 | QEncoding = origin.QEncoding 43 | 44 | // ErrInvalidMediaParameter is returned by ParseMediaType if 45 | // the media type value was found but there was an error parsing 46 | // the optional parameters 47 | ErrInvalidMediaParameter = origin.ErrInvalidMediaParameter 48 | 49 | // BEncoding represents Base64 encoding scheme as defined by RFC 2045. 50 | BEncoding = origin.BEncoding 51 | ) 52 | 53 | // Type 54 | type ( 55 | 56 | // A WordEncoder is an RFC 2047 encoded-word encoder. 57 | WordEncoder = origin.WordEncoder 58 | 59 | // A WordDecoder decodes MIME headers containing RFC 2047 encoded-words. 60 | WordDecoder = origin.WordDecoder 61 | ) 62 | 63 | // Function 64 | var ( 65 | 66 | // TypeByExtension returns the MIME type associated with the file extension ext. 67 | // The extension ext should begin with a leading dot, as in ".html". 68 | // When ext has no associated type, TypeByExtension returns "". 69 | // 70 | // Extensions are looked up first case-sensitively, then case-insensitively. 71 | // 72 | // The built-in table is small but on unix it is augmented by the local 73 | // system's mime.types file(s) if available under one or more of these 74 | // names: 75 | // 76 | // /etc/mime.types 77 | // /etc/apache2/mime.types 78 | // /etc/apache/mime.types 79 | // 80 | // On Windows, MIME types are extracted from the registry. 81 | // 82 | // Text types have the charset parameter set to "utf-8" by default. 83 | TypeByExtension = origin.TypeByExtension 84 | 85 | // ParseMediaType parses a media type value and any optional 86 | // parameters, per RFC 1521. Media types are the values in 87 | // Content-Type and Content-Disposition headers (RFC 2183). 88 | // On success, ParseMediaType returns the media type converted 89 | // to lowercase and trimmed of white space and a non-nil map. 90 | // If there is an error parsing the optional parameter, 91 | // the media type will be returned along with the error 92 | // ErrInvalidMediaParameter. 93 | // The returned map, params, maps from the lowercase 94 | // attribute to the attribute value with its case preserved. 95 | ParseMediaType = origin.ParseMediaType 96 | 97 | // FormatMediaType serializes mediatype t and the parameters 98 | // param as a media type conforming to RFC 2045 and RFC 2616. 99 | // The type and parameter names are written in lower-case. 100 | // When any of the arguments result in a standard violation then 101 | // FormatMediaType returns the empty string. 102 | FormatMediaType = origin.FormatMediaType 103 | 104 | // ExtensionsByType returns the extensions known to be associated with the MIME 105 | // type typ. The returned extensions will each begin with a leading dot, as in 106 | // ".html". When typ has no associated extensions, ExtensionsByType returns an 107 | // nil slice. 108 | ExtensionsByType = origin.ExtensionsByType 109 | 110 | // AddExtensionType sets the MIME type associated with 111 | // the extension ext to typ. The extension should begin with 112 | // a leading dot, as in ".html". 113 | AddExtensionType = origin.AddExtensionType 114 | ) 115 | ``` 116 | 117 | ## License 118 | 119 | Pouch is licensed under the MIT License. See [LICENSE](https://github.com/wzshiming/gotype/blob/master/LICENSE) for the full license text. 120 | -------------------------------------------------------------------------------- /cmd/pkgimport/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "go/ast" 8 | "go/format" 9 | "io/ioutil" 10 | "strings" 11 | "text/template" 12 | 13 | "github.com/wzshiming/gotype" 14 | ) 15 | 16 | type m struct { 17 | Name string 18 | Doc string 19 | Comment string 20 | } 21 | 22 | var ( 23 | i = flag.String("i", "", "Input package path") 24 | p = flag.String("p", "", "Package name") 25 | o = flag.String("o", "", "Out file") 26 | ) 27 | 28 | func init() { 29 | flag.Parse() 30 | } 31 | 32 | func main() { 33 | imp := gotype.NewImporter() 34 | n, err := imp.Import(*i, "") 35 | if err != nil { 36 | fmt.Println(err) 37 | return 38 | } 39 | 40 | ts := []m{} 41 | vs := []m{} 42 | fs := []m{} 43 | 44 | nc := n.NumChild() 45 | for i := 0; i != nc; i++ { 46 | v := n.Child(i) 47 | if ast.IsExported(v.Name()) { 48 | switch v.Kind() { 49 | case gotype.Declaration: 50 | decl := v.Declaration() 51 | 52 | switch decl.Kind() { 53 | default: 54 | vs = append(vs, m{ 55 | Name: v.Name(), 56 | Doc: comment(v.Doc().Text()), 57 | Comment: comment(v.Comment().Text()), 58 | }) 59 | case gotype.Func: 60 | fs = append(fs, m{ 61 | Name: v.Name(), 62 | Doc: comment(v.Doc().Text()), 63 | Comment: comment(v.Comment().Text()), 64 | }) 65 | } 66 | 67 | case gotype.Scope, gotype.Invalid: 68 | // No action 69 | default: 70 | doc := v 71 | ts = append(ts, m{ 72 | Name: v.Name(), 73 | Doc: comment(doc.Doc().Text()), 74 | Comment: comment(doc.Comment().Text()), 75 | }) 76 | } 77 | } 78 | } 79 | 80 | buf := bytes.NewBuffer(nil) 81 | 82 | if *p == "" { 83 | *p = n.Name() 84 | } 85 | fn := *o 86 | if fn == "" { 87 | fn = (*p) 88 | } 89 | 90 | templateFile.ExecuteTemplate(buf, "temp", map[string]interface{}{ 91 | "PackageName": p, 92 | "Origin": i, 93 | "TypeSpec": ts, 94 | "ValueSpec": vs, 95 | "FuncSpec": fs, 96 | "Filename": fn, 97 | }) 98 | 99 | d, err := format.Source([]byte(buf.String())) 100 | if err != nil { 101 | fmt.Println(buf.String(), err) 102 | return 103 | } 104 | 105 | if *o == "" { 106 | fmt.Print(string(d)) 107 | } else { 108 | ioutil.WriteFile(*o, d, 0666) 109 | } 110 | } 111 | 112 | func comment(s string) string { 113 | ss := []string{} 114 | dd := strings.Split(s, "\n") 115 | for i, v := range dd { 116 | if v == "" && i == len(dd)-1 { 117 | continue 118 | } 119 | v = strings.TrimSpace(v) 120 | if strings.Index(v, "//") != 0 { 121 | v = "// " + v 122 | } 123 | ss = append(ss, v) 124 | } 125 | 126 | return strings.Join(ss, "\n") 127 | } 128 | 129 | var templateFile = template.New("temp") 130 | 131 | func init() { 132 | templateFile.Parse(temp) 133 | } 134 | 135 | var temp = `// Code generated by "pkgimport -p {{.PackageName}} -i {{.Origin}} -o {{.Filename}}"; DO NOT EDIT. 136 | // Install by "go get -u -v github.com/wzshiming/gotype/cmd/pkgimport"; 137 | //go:generate pkgimport -p {{.PackageName}} -i {{.Origin}} -o {{.Filename}} 138 | 139 | package {{.PackageName}} 140 | 141 | import ( 142 | origin "{{.Origin}}" 143 | ) 144 | 145 | {{if .ValueSpec}} 146 | // Value 147 | var ( 148 | {{range .ValueSpec}} 149 | {{.Doc}} 150 | {{.Name}} = origin.{{.Name}} {{.Comment}} 151 | {{end}} 152 | ) 153 | {{end}} 154 | 155 | {{if .TypeSpec}} 156 | // Type 157 | type ( 158 | {{range .TypeSpec}} 159 | {{.Doc}} 160 | {{.Name}} = origin.{{.Name}} {{.Comment}} 161 | {{end}} 162 | ) 163 | {{end}} 164 | 165 | {{if .FuncSpec}} 166 | // Function 167 | var ( 168 | {{range .FuncSpec}} 169 | {{.Doc}} 170 | {{.Name}} = origin.{{.Name}} {{.Comment}} 171 | {{end}} 172 | ) 173 | {{end}} 174 | 175 | ` 176 | -------------------------------------------------------------------------------- /comment_test.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestFuncParameterComment(t *testing.T) { 9 | var testdata = []string{ 10 | `package a 11 | func Func (A string /* A */, 12 | B int, // B 13 | ) ( 14 | C int, // C 15 | ) {} 16 | `, 17 | } 18 | for _, src := range testdata { 19 | testFuncParameterComment(t, Parse(t, src)) 20 | } 21 | } 22 | 23 | func testFuncParameterComment(t *testing.T, scope Type) { 24 | 25 | typ, ok := scope.ChildByName("Func") 26 | if !ok { 27 | t.Fail() 28 | } 29 | typ = typ.Declaration() 30 | 31 | { 32 | num := typ.NumIn() 33 | for i := 0; i != num; i++ { 34 | v := typ.In(i) 35 | name := v.Name() 36 | comment := strings.TrimSpace(v.Comment().Text()) 37 | if name != comment { 38 | t.Fatal("Error func parameter comment:", name, comment) 39 | } 40 | } 41 | } 42 | 43 | { 44 | num := typ.NumOut() 45 | for i := 0; i != num; i++ { 46 | v := typ.Out(i) 47 | name := v.Name() 48 | comment := strings.TrimSpace(v.Comment().Text()) 49 | if name != comment { 50 | t.Fatal("Error func parameter comment:", name, comment) 51 | } 52 | } 53 | } 54 | } 55 | 56 | func TestFieldMedhodDoc(t *testing.T) { 57 | var testdata = []string{ 58 | `package a 59 | 60 | type Type struct {} 61 | 62 | // Medhod1 63 | func(Type) Medhod1(){} 64 | 65 | // Medhod2 66 | func(Type) Medhod2(string)string{} 67 | `, 68 | } 69 | for _, src := range testdata { 70 | testFieldMedhodDoc(t, Parse(t, src)) 71 | } 72 | } 73 | 74 | func testFieldMedhodDoc(t *testing.T, scope Type) { 75 | 76 | typ, ok := scope.ChildByName("Type") 77 | if !ok { 78 | t.Fail() 79 | } 80 | 81 | num := typ.NumMethod() 82 | for i := 0; i != num; i++ { 83 | v := typ.Method(i) 84 | name := v.Name() 85 | doc := strings.TrimSpace(v.Doc().Text()) 86 | if name != doc { 87 | t.Fatal("Error method doc:", name, doc) 88 | } 89 | } 90 | } 91 | 92 | func TestInterfaceMedhodComment(t *testing.T) { 93 | var testdata = []string{ 94 | `package a 95 | 96 | type Type interface { 97 | Medhod1() // Medhod1 98 | Medhod2(string) string // Medhod2 99 | } 100 | `, 101 | } 102 | for _, src := range testdata { 103 | testInterfaceMedhodComment(t, Parse(t, src)) 104 | } 105 | } 106 | 107 | func testInterfaceMedhodComment(t *testing.T, scope Type) { 108 | 109 | typ, ok := scope.ChildByName("Type") 110 | if !ok { 111 | t.Fail() 112 | } 113 | 114 | num := typ.NumMethod() 115 | for i := 0; i != num; i++ { 116 | v := typ.Method(i) 117 | name := v.Name() 118 | doc := strings.TrimSpace(v.Comment().Text()) 119 | if name != doc { 120 | t.Fatal("Error field comment:", name, doc) 121 | } 122 | } 123 | } 124 | 125 | func TestInterfaceMedhodDoc(t *testing.T) { 126 | var testdata = []string{ 127 | `package a 128 | 129 | type Type interface { 130 | // Medhod1 131 | Medhod1() 132 | // Medhod2 133 | Medhod2(string) string 134 | } 135 | `, 136 | } 137 | for _, src := range testdata { 138 | testInterfaceMedhodDoc(t, Parse(t, src)) 139 | } 140 | } 141 | 142 | func testInterfaceMedhodDoc(t *testing.T, scope Type) { 143 | 144 | typ, ok := scope.ChildByName("Type") 145 | if !ok { 146 | t.Fail() 147 | } 148 | 149 | num := typ.NumMethod() 150 | for i := 0; i != num; i++ { 151 | v := typ.Method(i) 152 | name := v.Name() 153 | doc := strings.TrimSpace(v.Doc().Text()) 154 | if name != doc { 155 | t.Fatal("Error field doc:", name, doc) 156 | } 157 | } 158 | } 159 | 160 | func TestFieldComment(t *testing.T) { 161 | var testdata = []string{ 162 | `package a 163 | 164 | type Type struct { 165 | Field1 string // Field1 166 | 167 | Field2 struct{ 168 | 169 | } // Field2 170 | } 171 | `, 172 | } 173 | for _, src := range testdata { 174 | testFieldComment(t, Parse(t, src)) 175 | } 176 | } 177 | 178 | func testFieldComment(t *testing.T, scope Type) { 179 | 180 | typ, ok := scope.ChildByName("Type") 181 | if !ok { 182 | t.Fail() 183 | } 184 | 185 | num := typ.NumField() 186 | for i := 0; i != num; i++ { 187 | v := typ.Field(i) 188 | name := v.Name() 189 | doc := strings.TrimSpace(v.Comment().Text()) 190 | if name != doc { 191 | t.Fatal("Error field comment:", name, doc) 192 | } 193 | } 194 | } 195 | 196 | func TestFieldDoc(t *testing.T) { 197 | var testdata = []string{ 198 | `package a 199 | 200 | type Type struct { 201 | // Field1 202 | Field1 string 203 | 204 | // Field2 205 | Field2 struct{ 206 | 207 | } 208 | } 209 | `, 210 | } 211 | for _, src := range testdata { 212 | testFieldDoc(t, Parse(t, src)) 213 | } 214 | } 215 | 216 | func testFieldDoc(t *testing.T, scope Type) { 217 | 218 | typ, ok := scope.ChildByName("Type") 219 | if !ok { 220 | t.Fail() 221 | } 222 | 223 | num := typ.NumField() 224 | for i := 0; i != num; i++ { 225 | v := typ.Field(i) 226 | name := v.Name() 227 | doc := strings.TrimSpace(v.Doc().Text()) 228 | if name != doc { 229 | t.Fatal("Error field doc:", name, doc) 230 | } 231 | } 232 | } 233 | 234 | func TestDoc(t *testing.T) { 235 | var testdata = []string{ 236 | `package a 237 | 238 | // Const 239 | const Const = "" 240 | 241 | var ( 242 | // Var 243 | Var = 0 244 | ) 245 | 246 | // Type 247 | type Type struct {} 248 | 249 | // Func 250 | func Func() { 251 | } 252 | 253 | `, 254 | } 255 | for _, src := range testdata { 256 | testDoc(t, Parse(t, src)) 257 | } 258 | } 259 | 260 | func testDoc(t *testing.T, scope Type) { 261 | 262 | num := scope.NumChild() 263 | for i := 0; i != num; i++ { 264 | v := scope.Child(i) 265 | name := v.Name() 266 | doc := strings.TrimSpace(v.Doc().Text()) 267 | if name != doc { 268 | t.Fatal("Error doc:", name, doc) 269 | } 270 | } 271 | } 272 | 273 | func TestComment(t *testing.T) { 274 | var testdata = []string{ 275 | `package a 276 | 277 | const Const = "" // Const 278 | 279 | var Var = 0 // Var 280 | 281 | type Type struct { 282 | 283 | } // Type 284 | 285 | type Type2 struct {} // Type2 286 | 287 | `, 288 | } 289 | for _, src := range testdata { 290 | testComment(t, Parse(t, src)) 291 | } 292 | } 293 | 294 | func testComment(t *testing.T, scope Type) { 295 | 296 | num := scope.NumChild() 297 | for i := 0; i != num; i++ { 298 | v := scope.Child(i) 299 | name := v.Name() 300 | comment := strings.TrimSpace(v.Comment().Text()) 301 | if name != comment { 302 | t.Fatal("Error comment:", name, comment) 303 | } 304 | } 305 | } 306 | 307 | func TestInversion(t *testing.T) { 308 | var testdata = `package p 309 | 310 | // S 311 | type S struct { 312 | t T 313 | } 314 | 315 | // T t 316 | type T struct { 317 | } 318 | 319 | ` 320 | 321 | scope := Parse(t, testdata) 322 | s, ok := scope.ChildByName("S") 323 | if !ok { 324 | t.Log(ok) 325 | return 326 | } 327 | t0 := s.Field(0).Elem() 328 | d0 := t0.Doc().Text() 329 | 330 | t1, ok := scope.ChildByName("T") 331 | if !ok { 332 | t.Log(ok) 333 | return 334 | } 335 | d1 := t1.Doc().Text() 336 | if d0 != d1 { 337 | t.Fail() 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /constant.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/constant" 7 | "go/token" 8 | "strconv" 9 | ) 10 | 11 | func constantEval(expr ast.Node, iota int64, info *infoFile) (r constant.Value, err error) { 12 | switch t := expr.(type) { 13 | case *ast.BasicLit: 14 | switch t.Kind { 15 | case token.STRING: 16 | r = constant.MakeString(t.Value[1 : len(t.Value)-1]) 17 | return r, nil 18 | case token.INT: 19 | i, err := strconv.ParseInt(t.Value, 0, 0) 20 | if err != nil { 21 | return nil, err 22 | } 23 | r = constant.MakeInt64(i) 24 | return r, nil 25 | case token.FLOAT: 26 | i, err := strconv.ParseFloat(t.Value, 0) 27 | if err != nil { 28 | return nil, err 29 | } 30 | r = constant.MakeFloat64(i) 31 | return r, nil 32 | } 33 | case *ast.UnaryExpr: 34 | x, err := constantEval(t.X, iota, info) 35 | if err != nil { 36 | return nil, err 37 | } 38 | r, err = constantUnaryOp(t.Op, x) 39 | if err != nil { 40 | return nil, err 41 | } 42 | return r, nil 43 | case *ast.BinaryExpr: 44 | x, err := constantEval(t.X, iota, info) 45 | if err != nil { 46 | return nil, err 47 | } 48 | y, err := constantEval(t.Y, iota, info) 49 | if err != nil { 50 | return nil, err 51 | } 52 | r, err = constantBinaryOp(t.Op, x, y) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return r, nil 57 | case *ast.Ident: 58 | if t.Name == "iota" { 59 | r = constant.MakeInt64(iota) 60 | return r, nil 61 | } else if val, ok := info.GetPkgOrType(t.Name); ok && val.Kind() == Declaration { 62 | val = val.Declaration() 63 | switch val.Kind() { 64 | case Int, Int8, Int16, Int32, Int64: 65 | i, err := strconv.ParseInt(val.Value(), 0, 0) 66 | if err != nil { 67 | return nil, err 68 | } 69 | r = constant.MakeInt64(i) 70 | return r, nil 71 | case Uint, Uint8, Uint16, Uint32, Uint64: 72 | i, err := strconv.ParseUint(val.Value(), 0, 0) 73 | if err != nil { 74 | return nil, err 75 | } 76 | r = constant.MakeUint64(i) 77 | return r, nil 78 | case Float32, Float64: 79 | i, err := strconv.ParseFloat(val.Value(), 0) 80 | if err != nil { 81 | return nil, err 82 | } 83 | r = constant.MakeFloat64(i) 84 | return r, nil 85 | case String: 86 | str := val.Value() 87 | r = constant.MakeString(str[1 : len(str)-1]) 88 | return r, nil 89 | default: 90 | // iota 91 | i, err := strconv.ParseInt(val.Value(), 0, 0) 92 | if err == nil { 93 | r = constant.MakeInt64(i) 94 | return r, nil 95 | } 96 | } 97 | } else { 98 | return nil, fmt.Errorf("undefined ident") 99 | } 100 | case *ast.ParenExpr: 101 | r, err = constantEval(t.X, iota, info) 102 | if err != nil { 103 | return nil, err 104 | } 105 | return r, nil 106 | case *ast.CallExpr: 107 | if len(t.Args) == 0 { 108 | return nil, fmt.Errorf("undefined call") 109 | } 110 | r, err = constantEval(t.Args[0], iota, info) 111 | if err != nil { 112 | return nil, err 113 | } 114 | return r, nil 115 | default: 116 | return nil, fmt.Errorf("undefined expr") 117 | } 118 | return constant.MakeUnknown(), nil 119 | } 120 | 121 | func constantUnaryOp(op token.Token, y constant.Value) (r constant.Value, err error) { 122 | defer func() { 123 | if x := recover(); x != nil { 124 | err = fmt.Errorf("%v", x) 125 | } 126 | }() 127 | 128 | r = constant.UnaryOp(op, y, 0) 129 | return r, nil 130 | } 131 | 132 | func constantBinaryOp(op token.Token, x, y constant.Value) (r constant.Value, err error) { 133 | defer func() { 134 | if x := recover(); x != nil { 135 | err = fmt.Errorf("%v", x) 136 | } 137 | }() 138 | switch op { 139 | case token.SHL, token.SHR: 140 | n, _ := constant.Uint64Val(y) 141 | r = constant.Shift(x, op, uint(n)) 142 | case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: 143 | r = constant.MakeBool(constant.Compare(x, op, y)) 144 | default: 145 | r = constant.BinaryOp(x, op, y) 146 | } 147 | return r, nil 148 | } 149 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wzshiming/gotype 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /importer.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | "go/build" 6 | goparser "go/parser" 7 | "go/token" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | // Importer Go source type analyzer 13 | type Importer struct { 14 | fset *token.FileSet 15 | mode goparser.Mode 16 | bufType map[string]Type 17 | bufBuild map[string]*build.Package 18 | errorHandler func(error) 19 | importHandler func(path, src, dir string) 20 | isCommentLocator bool 21 | ctx build.Context 22 | gopath []string 23 | } 24 | 25 | // NewImporter creates a new importer 26 | func NewImporter(options ...Option) *Importer { 27 | i := &Importer{ 28 | fset: token.NewFileSet(), 29 | mode: goparser.ParseComments, 30 | bufType: map[string]Type{}, 31 | bufBuild: map[string]*build.Package{}, 32 | errorHandler: func(err error) { 33 | return 34 | }, 35 | ctx: build.Default, 36 | gopath: filepath.SplitList(build.Default.GOPATH), 37 | } 38 | for _, v := range options { 39 | v(i) 40 | } 41 | return i 42 | } 43 | 44 | // ImportPackage returns go package scope 45 | func (i *Importer) ImportPackage(path string, pkg *ast.Package) (Type, error) { 46 | t, ok := i.bufType[path] 47 | if ok { 48 | return t, nil 49 | } 50 | np := newParser(i, i.isCommentLocator, "", path, false) 51 | t = np.ParsePackage(pkg) 52 | i.bufType[path] = t 53 | return t, nil 54 | } 55 | 56 | // ImportFile returns go package scope 57 | func (i *Importer) ImportFile(path string, f *ast.File) (Type, error) { 58 | t, ok := i.bufType[path] 59 | if ok { 60 | return t, nil 61 | } 62 | np := newParser(i, i.isCommentLocator, "", path, false) 63 | t = np.ParseFile(f) 64 | i.bufType[path] = t 65 | return t, nil 66 | } 67 | 68 | // ImportSource returns go package scope 69 | func (i *Importer) ImportSource(path string, src []byte) (Type, error) { 70 | f, err := goparser.ParseFile(i.fset, path, src, i.mode) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return i.ImportFile(path, f) 75 | } 76 | 77 | // FileSet returns the FileSet 78 | func (i *Importer) FileSet() *token.FileSet { 79 | return i.fset 80 | } 81 | 82 | // ImportBuild returns details about the Go package named by the import path. 83 | func (i *Importer) ImportBuild(path string, src string) (*build.Package, error) { 84 | if !filepath.IsAbs(src) { 85 | abs, err := filepath.Abs(src) 86 | if err != nil { 87 | return nil, err 88 | } 89 | src = abs 90 | } 91 | k := path + " " + src 92 | if v, ok := i.bufBuild[k]; ok { 93 | return v, nil 94 | } 95 | imp, err := i.ctx.Import(path, src, 0) 96 | if err != nil { 97 | i.appendError(err) 98 | return nil, err 99 | } 100 | i.bufBuild[k] = imp 101 | return imp, nil 102 | } 103 | 104 | // appendError append error 105 | func (i *Importer) appendError(err error) { 106 | if i.errorHandler != nil { 107 | i.errorHandler(err) 108 | } 109 | } 110 | 111 | // ImportName returns go package name 112 | func (i *Importer) ImportName(path string, src string) (name string, goroot bool) { 113 | imp, err := i.ImportBuild(path, src) 114 | if err != nil { 115 | return "", false 116 | } 117 | return imp.Name, imp.Goroot 118 | } 119 | 120 | // Import returns go package scope 121 | func (i *Importer) Import(path string, src string) (Type, error) { 122 | imp, err := i.ImportBuild(path, src) 123 | if err != nil { 124 | return nil, err 125 | } 126 | dir := imp.Dir 127 | 128 | tt, ok := i.bufType[dir] 129 | if ok { 130 | return tt, nil 131 | } 132 | if i.importHandler != nil { 133 | i.importHandler(path, src, dir) 134 | } 135 | 136 | m := map[string]bool{} 137 | for _, v := range imp.GoFiles { 138 | m[v] = true 139 | } 140 | 141 | for _, v := range imp.CgoFiles { 142 | m[v] = true 143 | } 144 | 145 | p, err := goparser.ParseDir(i.fset, dir, func(fi os.FileInfo) bool { 146 | return m[fi.Name()] 147 | }, i.mode) 148 | 149 | if err != nil { 150 | i.appendError(err) 151 | return nil, err 152 | } 153 | 154 | for _, v := range p { 155 | np := newParser(i, i.isCommentLocator, dir, imp.ImportPath, imp.Goroot) 156 | t := np.ParsePackage(v) 157 | i.bufType[dir] = t 158 | return t, nil 159 | } 160 | err = &NoGoError{path} 161 | i.appendError(err) 162 | return nil, err 163 | } 164 | 165 | // NoGoError is the error used by Import to describe a directory 166 | // containing no Go source files. 167 | type NoGoError struct { 168 | Dir string 169 | } 170 | 171 | func (e *NoGoError) Error() string { 172 | return "no Go source code files in " + e.Dir 173 | } 174 | -------------------------------------------------------------------------------- /info.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | // info holds result type information. 4 | // Only the information for which a map is provided is collected. 5 | // If the package has type errors, the collected information may be incomplete. 6 | type info struct { 7 | PkgNamed map[string]types // map[file]packgae 8 | Named types // var, func, type, 9 | Methods map[string]types // map[type]method 10 | Src string 11 | PkgPath string 12 | Goroot bool 13 | } 14 | 15 | type infoFile struct { 16 | *info 17 | filename string 18 | } 19 | 20 | func newInfo(src string, pkg string, goroot bool) *info { 21 | return &info{ 22 | Src: src, 23 | PkgPath: pkg, 24 | Goroot: goroot, 25 | Methods: map[string]types{}, 26 | PkgNamed: map[string]types{}, 27 | } 28 | } 29 | 30 | func (i *info) File(filename string) *infoFile { 31 | return &infoFile{ 32 | info: i, 33 | filename: filename, 34 | } 35 | } 36 | 37 | func (i *infoFile) GetPkgOrType(name string) (Type, bool) { 38 | if pkg, ok := i.PkgNamed[i.filename]; ok { 39 | t, ok := pkg.Search(name) 40 | if ok { 41 | return t, ok 42 | } 43 | } 44 | if i.filename == "" { 45 | for _, pkg := range i.PkgNamed { 46 | t, ok := pkg.Search(name) 47 | if ok { 48 | return t, ok 49 | } 50 | } 51 | } 52 | return i.Named.Search(name) 53 | } 54 | 55 | func (i *infoFile) AddPkg(t Type) { 56 | pkg, _ := i.PkgNamed[i.filename] 57 | pkg.Add(t) 58 | i.PkgNamed[i.filename] = pkg 59 | } 60 | 61 | func (i *infoFile) AddType(t Type) { 62 | i.Named.Add(t) 63 | } 64 | 65 | func (i *infoFile) AddMethod(name string, t Type) { 66 | methods := i.Methods[name] 67 | methods.Add(t) 68 | i.Methods[name] = methods 69 | } 70 | -------------------------------------------------------------------------------- /kind.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | //go:generate stringer -type Kind kind.go 4 | 5 | // Kind represents the specific kind of type that a Type represents. 6 | // The zero Kind is not a valid kind. 7 | type Kind uint8 8 | 9 | // Define kind 10 | const ( 11 | Invalid Kind = iota 12 | 13 | // Built-in base type 14 | 15 | predeclaredTypesBeg 16 | Bool 17 | Int 18 | Int8 19 | Int16 20 | Int32 21 | Int64 22 | Uint 23 | Uint8 24 | Uint16 25 | Uint32 26 | Uint64 27 | Uintptr 28 | Float32 29 | Float64 30 | Complex64 31 | Complex128 32 | String 33 | Byte 34 | Rune 35 | Error 36 | predeclaredTypesEnd 37 | 38 | // Built-in combination 39 | 40 | Array 41 | Chan 42 | Func 43 | Interface 44 | Map 45 | Ptr 46 | Slice 47 | Struct 48 | 49 | // Special is different from other Kinds 50 | 51 | Field // a Struct Field 52 | Scope // package or func body 53 | Declaration // a top-level function, variable, or constant. 54 | ) 55 | -------------------------------------------------------------------------------- /kind_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type Kind kind.go"; DO NOT EDIT. 2 | 3 | package gotype 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Invalid-0] 12 | _ = x[predeclaredTypesBeg-1] 13 | _ = x[Bool-2] 14 | _ = x[Int-3] 15 | _ = x[Int8-4] 16 | _ = x[Int16-5] 17 | _ = x[Int32-6] 18 | _ = x[Int64-7] 19 | _ = x[Uint-8] 20 | _ = x[Uint8-9] 21 | _ = x[Uint16-10] 22 | _ = x[Uint32-11] 23 | _ = x[Uint64-12] 24 | _ = x[Uintptr-13] 25 | _ = x[Float32-14] 26 | _ = x[Float64-15] 27 | _ = x[Complex64-16] 28 | _ = x[Complex128-17] 29 | _ = x[String-18] 30 | _ = x[Byte-19] 31 | _ = x[Rune-20] 32 | _ = x[Error-21] 33 | _ = x[predeclaredTypesEnd-22] 34 | _ = x[Array-23] 35 | _ = x[Chan-24] 36 | _ = x[Func-25] 37 | _ = x[Interface-26] 38 | _ = x[Map-27] 39 | _ = x[Ptr-28] 40 | _ = x[Slice-29] 41 | _ = x[Struct-30] 42 | _ = x[Field-31] 43 | _ = x[Scope-32] 44 | _ = x[Declaration-33] 45 | } 46 | 47 | const _Kind_name = "InvalidpredeclaredTypesBegBoolIntInt8Int16Int32Int64UintUint8Uint16Uint32Uint64UintptrFloat32Float64Complex64Complex128StringByteRuneErrorpredeclaredTypesEndArrayChanFuncInterfaceMapPtrSliceStructFieldScopeDeclaration" 48 | 49 | var _Kind_index = [...]uint8{0, 7, 26, 30, 33, 37, 42, 47, 52, 56, 61, 67, 73, 79, 86, 93, 100, 109, 119, 125, 129, 133, 138, 157, 162, 166, 170, 179, 182, 185, 190, 196, 201, 206, 217} 50 | 51 | func (i Kind) String() string { 52 | if i >= Kind(len(_Kind_index)-1) { 53 | return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" 54 | } 55 | return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] 56 | } 57 | -------------------------------------------------------------------------------- /misc.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "strings" 7 | ) 8 | 9 | type importer interface { 10 | appendError(err error) 11 | Import(path string, src string) (Type, error) 12 | ImportName(path string, src string) (name string, goroot bool) 13 | } 14 | 15 | // typeName Parses the expression to get the type name and whether it is imported 16 | func typeName(x ast.Expr) (name string, imported bool) { 17 | switch t := x.(type) { 18 | case *ast.Ident: // Defined by the current package 19 | return t.Name, false 20 | case *ast.SelectorExpr: // Defined by the imported 21 | if _, ok := t.X.(*ast.Ident); ok { 22 | return t.Sel.Name, true 23 | } 24 | case *ast.StarExpr: 25 | return typeName(t.X) 26 | } 27 | return 28 | } 29 | 30 | func init() { 31 | for i := predeclaredTypesBeg + 1; i != predeclaredTypesEnd; i++ { 32 | k := strings.ToLower(i.String()) 33 | predeclaredTypes[k] = i 34 | } 35 | } 36 | 37 | var predeclaredTypes = map[string]Kind{} 38 | 39 | var tokenTypes = map[token.Token]Kind{ 40 | token.INT: Int, 41 | token.FLOAT: Float64, 42 | token.IMAG: Complex128, 43 | token.CHAR: Int32, 44 | token.STRING: String, 45 | } 46 | 47 | var builtinFunc = map[string]builtinfunc{ 48 | "append": builtinfuncItem, 49 | "close": builtinfuncVoid, 50 | "delete": builtinfuncVoid, 51 | "panic": builtinfuncVoid, 52 | "recover": builtinfuncInterface, 53 | "imag": builtinfuncInt, 54 | "real": builtinfuncInt, 55 | "make": builtinfuncItem, 56 | "new": builtinfuncPtrItem, 57 | "cap": builtinfuncInt, 58 | "copy": builtinfuncInt, 59 | "len": builtinfuncInt, 60 | } 61 | -------------------------------------------------------------------------------- /option.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | // Option some basic options 4 | type Option func(i *Importer) 5 | 6 | // ErrorHandler returns the error handler option 7 | func ErrorHandler(f func(error)) Option { 8 | return func(i *Importer) { 9 | i.errorHandler = f 10 | } 11 | } 12 | 13 | // WithCommentLocator sets comment locator 14 | func WithCommentLocator() Option { 15 | return func(i *Importer) { 16 | i.isCommentLocator = true 17 | } 18 | } 19 | 20 | // ImportHandler returns the import handler option 21 | func ImportHandler(f func(path, src, dir string)) Option { 22 | return func(i *Importer) { 23 | i.importHandler = f 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /other_test.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "fmt" 5 | "go/token" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestOther(t *testing.T) { 13 | var testpath = []string{ 14 | "github.com/wzshiming/gotype/testdata/value", 15 | "github.com/wzshiming/gotype/testdata/kind", 16 | "github.com/wzshiming/gotype/testdata/type", 17 | "github.com/wzshiming/gotype/testdata/pkg", 18 | "./testdata/value", 19 | "./testdata/kind", 20 | "./testdata/type", 21 | "./testdata/pkg", 22 | } 23 | for _, src := range testpath { 24 | testAll(t, src) 25 | } 26 | } 27 | 28 | func testAll(t *testing.T, src string) { 29 | imp := getImporter(t) 30 | scope, err := imp.Import(src, "") 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | fset := imp.FileSet() 35 | num := scope.NumChild() 36 | for i := 0; i != num; i++ { 37 | v := scope.Child(i) 38 | testType(t, fset, v) 39 | } 40 | } 41 | 42 | func testType(t *testing.T, fset *token.FileSet, v Type) { 43 | 44 | for _, line := range strings.Split(v.Doc().Text(), "\n") { 45 | if line == "" { 46 | continue 47 | } 48 | v := v 49 | 50 | pos := fset.Position(v.Origin().Pos()) 51 | 52 | tag := reflect.StructTag(line) 53 | if data, ok := tag.Lookup("To"); ok { 54 | st := strings.Split(data, ",") 55 | for _, to := range st { 56 | method := strings.Split(to, ":") 57 | switch method[0] { 58 | case "Elem": 59 | v = v.Elem() 60 | case "Declaration": 61 | v = v.Declaration() 62 | case "Key": 63 | v = v.Key() 64 | case "In": 65 | if len(method) < 2 { 66 | t.Fatal(pos, "Error In num: ", to) 67 | } 68 | i, _ := strconv.ParseInt(method[1], 10, 64) 69 | v = v.In(int(i)) 70 | case "Out": 71 | if len(method) < 2 { 72 | t.Fatal(pos, "Error Out num: ", to) 73 | } 74 | i, _ := strconv.ParseInt(method[1], 10, 64) 75 | v = v.Out(int(i)) 76 | case "Field": 77 | if len(method) < 2 { 78 | t.Fatal(pos, "Error Field num: ", to) 79 | } 80 | i, err := strconv.ParseInt(method[1], 10, 64) 81 | if err != nil { 82 | t.Fatal(pos, "Error Field: ", err) 83 | } 84 | if v.NumField() <= int(i) { 85 | t.Fatal(pos, "Error Out of index range: ", to) 86 | } 87 | v = v.Field(int(i)) 88 | case "Method": 89 | if len(method) < 2 { 90 | t.Fatal(pos, "Error Method num: ", to) 91 | } 92 | i, err := strconv.ParseInt(method[1], 10, 64) 93 | if err != nil { 94 | t.Fatal(pos, "Error Method: ", err) 95 | } 96 | if v.NumMethod() <= int(i) { 97 | t.Fatal(pos, "Error Out of index range: ", to) 98 | } 99 | v = v.Method(int(i)) 100 | case "Child": 101 | if len(method) < 2 { 102 | t.Fatal(pos, "Error Child num: ", to) 103 | } 104 | i, err := strconv.ParseInt(method[1], 10, 64) 105 | if err != nil { 106 | t.Fatal(pos, "Error Child: ", err) 107 | } 108 | if v.NumMethod() <= int(i) { 109 | t.Fatal(pos, "Error Out of index range: ", to) 110 | } 111 | v = v.Child(int(i)) 112 | case "FieldByName": 113 | if len(method) < 2 { 114 | t.Fatal(pos, "Error FieldByName num: ", to) 115 | } 116 | v, ok = v.FieldByName(method[1]) 117 | if !ok { 118 | t.Fatal(pos, "Error not found: ", to) 119 | } 120 | case "MethodByName": 121 | if len(method) < 2 { 122 | t.Fatal(pos, "Error MethodByName num: ", to) 123 | } 124 | v, ok = v.MethodByName(method[1]) 125 | if !ok { 126 | t.Fatal(pos, "Error not found: ", to) 127 | } 128 | case "ChildByName": 129 | if len(method) < 2 { 130 | t.Fatal(pos, "Error ChildByName num: ", to) 131 | } 132 | v, ok = v.ChildByName(method[1]) 133 | if !ok { 134 | t.Fatal(pos, "Error not found: ", to) 135 | } 136 | default: 137 | t.Fatal(pos, "Error to: ", to) 138 | } 139 | } 140 | } 141 | if data, ok := tag.Lookup("Value"); ok { 142 | val := v.Value() 143 | if data != val { 144 | t.Fatal(pos, "Error value:", val, ":", data) 145 | } 146 | } 147 | if data, ok := tag.Lookup("Name"); ok { 148 | name := v.Name() 149 | if data != name { 150 | t.Fatal(pos, "Error name:", name, ":", data) 151 | } 152 | } 153 | if data, ok := tag.Lookup("String"); ok { 154 | str := v.String() 155 | if data != str { 156 | t.Fatal(pos, "Error string:", str, ":", data) 157 | } 158 | } 159 | if data, ok := tag.Lookup("Kind"); ok { 160 | kind := v.Kind().String() 161 | if data != kind { 162 | t.Fatal(pos, "Error kind:", kind, ":", data) 163 | } 164 | } 165 | if data, ok := tag.Lookup("Len"); ok { 166 | l := fmt.Sprint(v.Len()) 167 | if data != l { 168 | t.Fatal(pos, "Error len:", l, ":", data) 169 | } 170 | } 171 | if data, ok := tag.Lookup("NumChild"); ok { 172 | num := fmt.Sprint(v.NumChild()) 173 | if data != num { 174 | t.Fatal(pos, "Error num child:", num, ":", data) 175 | } 176 | } 177 | if data, ok := tag.Lookup("NumMethod"); ok { 178 | num := fmt.Sprint(v.NumMethod()) 179 | if data != num { 180 | t.Fatal(pos, "Error num method:", num, ":", data) 181 | } 182 | } 183 | if data, ok := tag.Lookup("NumIn"); ok { 184 | num := fmt.Sprint(v.NumIn()) 185 | if data != num { 186 | t.Fatal(pos, "Error num in:", num, ":", data) 187 | } 188 | } 189 | if data, ok := tag.Lookup("NumOut"); ok { 190 | num := fmt.Sprint(v.NumOut()) 191 | if data != num { 192 | t.Fatal(pos, "Error num out:", num, ":", data) 193 | } 194 | } 195 | if data, ok := tag.Lookup("NumField"); ok { 196 | num := fmt.Sprint(v.NumField()) 197 | if data != num { 198 | t.Fatal(pos, "Error num field:", num, ":", data) 199 | } 200 | } 201 | if data, ok := tag.Lookup("Tag"); ok { 202 | num := string(v.Tag()) 203 | if data != num { 204 | t.Fatal(pos, "Error tag:", num, ":", data) 205 | } 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "strconv" 7 | ) 8 | 9 | type parser struct { 10 | importer importer 11 | isCommentLocator bool 12 | info *info 13 | comments []*ast.CommentGroup 14 | } 15 | 16 | // NewParser 17 | func newParser(i importer, c bool, src string, pkg string, goroot bool) *parser { 18 | r := &parser{ 19 | importer: i, 20 | isCommentLocator: c, 21 | info: newInfo(src, pkg, goroot), 22 | } 23 | return r 24 | } 25 | 26 | // ParsePackage parse package 27 | func (r *parser) ParsePackage(pkg *ast.Package) Type { 28 | for filename, file := range pkg.Files { 29 | r.parseFile(r.info.File(filename), file) 30 | } 31 | tt := newTypeScope(pkg.Name, r.info) 32 | tt = newTypeOrigin(tt, pkg, r.info, nil, nil) 33 | return tt 34 | } 35 | 36 | // ParseFile parse file 37 | func (r *parser) ParseFile(file *ast.File) Type { 38 | r.parseFile(r.info.File(""), file) 39 | tt := newTypeScope(file.Name.String(), r.info) 40 | tt = newTypeOrigin(tt, file, r.info, nil, nil) 41 | return tt 42 | } 43 | 44 | // parseFile parse file 45 | func (r *parser) parseFile(info *infoFile, file *ast.File) { 46 | r.comments = file.Comments 47 | for _, decl := range file.Decls { 48 | r.parseDecl(info, decl) 49 | } 50 | r.comments = nil 51 | } 52 | 53 | // parseDecl parse declaration 54 | func (r *parser) parseDecl(info *infoFile, decl ast.Decl) { 55 | switch d := decl.(type) { 56 | case *ast.FuncDecl: 57 | r.parseFunc(info, d) 58 | case *ast.GenDecl: 59 | switch d.Tok { 60 | case token.CONST, token.VAR: 61 | r.parseValue(info, d) 62 | case token.IMPORT: 63 | r.parseImport(info, d) 64 | case token.TYPE: 65 | r.parseType(info, d) 66 | } 67 | } 68 | } 69 | 70 | // parseFunc parse function 71 | func (r *parser) parseFunc(info *infoFile, decl *ast.FuncDecl) { 72 | doc := decl.Doc 73 | t := r.evalType(info, decl.Type) 74 | t = newDeclaration(decl.Name.Name, t) 75 | t = newTypeOrigin(t, decl, r.info, doc, nil) 76 | 77 | if decl.Recv != nil { 78 | name, ok := typeName(decl.Recv.List[0].Type) 79 | if ok { 80 | return 81 | } 82 | info.AddMethod(name, t) 83 | return 84 | } 85 | info.AddType(t) 86 | return 87 | } 88 | 89 | // parseType parse type 90 | func (r *parser) parseType(info *infoFile, decl *ast.GenDecl) { 91 | for _, spec := range decl.Specs { 92 | s, ok := spec.(*ast.TypeSpec) 93 | if !ok { 94 | continue 95 | } 96 | 97 | doc := decl.Doc 98 | if s.Doc != nil { 99 | doc = s.Doc 100 | } 101 | comment := s.Comment 102 | 103 | tt := r.evalType(info, s.Type) 104 | if s.Assign == 0 && tt.Kind() != Interface { 105 | tt = newTypeNamed(s.Name.Name, tt, info) 106 | } else { 107 | tt = newTypeAlias(s.Name.Name, tt) 108 | } 109 | 110 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 111 | info.AddType(tt) 112 | } 113 | } 114 | 115 | // parserImport parser import 116 | func (r *parser) parseImport(info *infoFile, decl *ast.GenDecl) { 117 | for _, spec := range decl.Specs { 118 | s, ok := spec.(*ast.ImportSpec) 119 | if !ok { 120 | continue 121 | } 122 | 123 | doc := decl.Doc 124 | if s.Doc != nil { 125 | doc = s.Doc 126 | } 127 | comment := s.Comment 128 | 129 | path, err := strconv.Unquote(s.Path.Value) 130 | if err != nil { 131 | continue 132 | } 133 | 134 | if r.importer == nil { 135 | continue 136 | } 137 | 138 | if s.Name == nil { 139 | tt := newTypeImport("", path, r.info.Src, r.importer) 140 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 141 | info.AddPkg(tt) 142 | } else { 143 | switch s.Name.Name { 144 | case "_": 145 | case ".": 146 | p, err := r.importer.Import(path, r.info.PkgPath) 147 | if err != nil { 148 | r.importer.appendError(err) 149 | continue 150 | } 151 | l := p.NumChild() 152 | for i := 0; i != l; i++ { 153 | tt := p.Child(i) 154 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 155 | info.AddPkg(tt) 156 | } 157 | default: 158 | tt := newTypeImport(s.Name.Name, path, r.info.Src, r.importer) 159 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 160 | info.AddPkg(tt) 161 | } 162 | } 163 | } 164 | } 165 | 166 | // parseValue parse value 167 | func (r *parser) parseValue(info *infoFile, decl *ast.GenDecl) { 168 | var prevVal ast.Expr 169 | var prevTyp ast.Expr 170 | 171 | loop: 172 | for index, spec := range decl.Specs { 173 | s, ok := spec.(*ast.ValueSpec) 174 | if !ok { 175 | continue 176 | } 177 | 178 | doc := decl.Doc 179 | if s.Doc != nil { 180 | doc = s.Doc 181 | } 182 | comment := s.Comment 183 | 184 | ctyp := s.Type 185 | values := s.Values 186 | 187 | if decl.Tok == token.CONST && s.Type == nil && len(values) == 0 { 188 | values = append(values, prevVal) 189 | ctyp = prevTyp 190 | } 191 | 192 | var typ Type 193 | if ctyp != nil { // Type definition 194 | typ = r.evalType(info, ctyp) 195 | if decl.Tok == token.CONST { 196 | prevTyp = ctyp 197 | } 198 | } 199 | 200 | var val Type 201 | 202 | switch l := len(values); l { 203 | case 0: 204 | case 1: 205 | val = r.evalType(info, values[0]) 206 | if decl.Tok == token.CONST { 207 | prevVal = values[0] 208 | val = newEvalBind(val, int64(index), info) 209 | } 210 | if tup, ok := val.(*typeTuple); ok { 211 | l := tup.all.Len() 212 | for i, v := range s.Names { 213 | if v.Name == "" { 214 | continue 215 | } 216 | if i == l { 217 | break 218 | } 219 | val = tup.all.Index(i) 220 | tt := newDeclaration(v.Name, val) 221 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 222 | info.AddType(tt) 223 | } 224 | continue loop 225 | } 226 | default: 227 | l := len(values) 228 | for i, v := range s.Names { 229 | if v.Name == "" { 230 | continue 231 | } 232 | if i == l { 233 | break 234 | } 235 | val = r.evalType(info, values[i]) 236 | if decl.Tok == token.CONST { 237 | val = newEvalBind(val, int64(index), info) 238 | } 239 | tt := newDeclaration(v.Name, val) 240 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 241 | info.AddType(tt) 242 | } 243 | continue loop 244 | } 245 | 246 | for _, v := range s.Names { 247 | if v.Name == "" { 248 | continue 249 | } 250 | 251 | if typ == nil { 252 | if val != nil { 253 | tt := newDeclaration(v.Name, val) 254 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 255 | info.AddType(tt) 256 | } else { 257 | // No action 258 | } 259 | } else { 260 | if val == nil { 261 | tt := newDeclaration(v.Name, typ) 262 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 263 | info.AddType(tt) 264 | } else { 265 | tt := newDeclaration(v.Name, typ) 266 | tt = newTypeOrigin(tt, s, r.info, doc, comment) 267 | tt = newTypeValueBind(tt, val, r.info) 268 | info.AddType(tt) 269 | } 270 | } 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /parser_types.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func (r *parser) EvalType(expr ast.Expr) (ret Type) { 12 | return r.evalType(r.info.File(""), expr) 13 | } 14 | 15 | func (r *parser) evalType(info *infoFile, expr ast.Expr) (ret Type) { 16 | defer func() { 17 | if ret != nil { 18 | ret = newTypeOrigin(ret, expr, r.info, nil, nil) 19 | } 20 | }() 21 | switch t := expr.(type) { 22 | case *ast.BadExpr: 23 | return nil 24 | case *ast.Ident: 25 | if k := predeclaredTypes[t.Name]; k != 0 { 26 | s := newTypeBuiltin(k, "") 27 | return s 28 | } 29 | s := newTypeNamed(t.Name, nil, info) 30 | return s 31 | case *ast.BasicLit: 32 | if k := tokenTypes[t.Kind]; k != 0 { 33 | s := newTypeBuiltin(k, t.Value) 34 | return s 35 | } 36 | return nil 37 | case *ast.FuncLit: 38 | return r.evalType(info, t.Type) 39 | case *ast.CompositeLit: 40 | typ := r.evalType(info, t.Type) 41 | if typ == nil { 42 | return nil 43 | } 44 | 45 | if typ.Kind() != Struct { 46 | return typ 47 | } 48 | 49 | pairs := &typeValuePairs{} 50 | for _, v := range t.Elts { 51 | d := r.evalType(info, v) 52 | pairs.li.Add(d) 53 | } 54 | return newTypeValueBind(typ, pairs, r.info) 55 | case *ast.ParenExpr: 56 | return r.evalType(info, t.X) 57 | case *ast.SelectorExpr: 58 | s := r.evalType(info, t.X) 59 | name := t.Sel.Name 60 | return newSelector(s, name) 61 | case *ast.IndexExpr: 62 | typ := r.evalType(info, t.X) 63 | if typ == nil { 64 | return nil 65 | } 66 | 67 | switch typ.Kind() { 68 | case Array, Chan, Map, Ptr, Slice: 69 | return typ.Elem() 70 | } 71 | return nil 72 | case *ast.SliceExpr: 73 | return r.evalType(info, t.X) 74 | case *ast.TypeAssertExpr: 75 | return r.evalType(info, t.Type) 76 | case *ast.CallExpr: 77 | switch b := t.Fun.(type) { 78 | case *ast.Ident: 79 | if bf, ok := builtinFunc[b.Name]; ok { 80 | switch bf { 81 | case builtinfuncInt: 82 | return newTypeBuiltin(Int, "") 83 | case builtinfuncPtrItem: 84 | return newTypePtr(r.evalType(info, t.Args[0])) 85 | case builtinfuncItem: 86 | return r.evalType(info, t.Args[0]) 87 | case builtinfuncInterface: 88 | return newTypeBuiltin(Interface, "") 89 | case builtinfuncVoid: 90 | return newTypeBuiltin(Invalid, "") 91 | } 92 | } 93 | } 94 | 95 | b := r.evalType(info, t.Fun) 96 | if b == nil { 97 | return nil 98 | } 99 | for b.Kind() == Declaration { 100 | b = b.Declaration() 101 | } 102 | if b.Kind() == Func { 103 | l := b.NumOut() 104 | ts := make(types, 0, l) 105 | for i := 0; i != l; i++ { 106 | ts = append(ts, b.Out(i)) 107 | } 108 | return newTypeTuple(ts) 109 | } 110 | return b 111 | case *ast.StarExpr: 112 | return newTypePtr(r.evalType(info, t.X)) 113 | case *ast.UnaryExpr: 114 | if t.Op == token.AND { 115 | return newTypePtr(r.evalType(info, t.X)) 116 | } 117 | return r.evalType(info, t.X) 118 | case *ast.BinaryExpr: 119 | return r.evalType(info, t.X) 120 | case *ast.KeyValueExpr: 121 | k := r.evalType(info, t.Key) 122 | v := r.evalType(info, t.Value) 123 | return newTypeValuePair(k, v) 124 | case *ast.ArrayType: 125 | if t.Len == nil { 126 | return newTypeSlice(r.evalType(info, t.Elt)) 127 | } 128 | 129 | if ell, ok := t.Len.(*ast.Ellipsis); ok && ell.Ellipsis != 0 { 130 | return newTypeArray(r.evalType(info, t.Elt), 0) 131 | } 132 | 133 | length := newEvalBind(r.evalType(info, t.Len), 0, info) 134 | i, _ := strconv.ParseInt(length.Value(), 0, 0) 135 | return newTypeArray(r.evalType(info, t.Elt), int(i)) 136 | case *ast.StructType: 137 | s := &typeStruct{} 138 | 139 | if t.Fields == nil { 140 | return s 141 | } 142 | for _, v := range t.Fields.List { 143 | ty := r.evalType(info, v.Type) 144 | var tag reflect.StructTag 145 | if v.Tag != nil { 146 | tv := v.Tag.Value 147 | tv = strings.Trim(tv, "`") 148 | tag = reflect.StructTag(tv) 149 | } 150 | 151 | if ty == nil { 152 | continue 153 | } 154 | 155 | if v.Names == nil { 156 | t := &typeStructField{ 157 | name: ty.Name(), 158 | elem: ty, 159 | tag: tag, 160 | anonymous: true, 161 | } 162 | tt := newTypeOrigin(t, v, r.info, v.Doc, v.Comment) 163 | s.fields = append(s.fields, tt) 164 | continue 165 | } 166 | for _, name := range v.Names { 167 | n := name.Name 168 | if n == "" { 169 | n = "_" 170 | } 171 | t := &typeStructField{ 172 | name: n, 173 | elem: ty, 174 | tag: tag, 175 | } 176 | tt := newTypeOrigin(t, v, r.info, v.Doc, v.Comment) 177 | s.fields = append(s.fields, tt) 178 | } 179 | } 180 | return s 181 | case *ast.FuncType: 182 | s := &typeFunc{} 183 | if t.Params != nil { 184 | list := t.Params.List 185 | for pk, v := range list { 186 | if _, ok := v.Type.(*ast.Ellipsis); ok { 187 | s.variadic = true 188 | } 189 | ty := r.evalType(info, v.Type) 190 | if ty == nil { 191 | continue 192 | } 193 | 194 | if v.Names == nil { 195 | t := newDeclaration("_", ty) 196 | s.params = append(s.params, t) 197 | continue 198 | } 199 | for nk, name := range v.Names { 200 | tv := newDeclaration(name.Name, ty) 201 | tt := newTypeOrigin(tv, v, r.info, v.Doc, v.Comment) 202 | 203 | // Parameter comment, Because the default criteria are not handled. 204 | if r.isCommentLocator { 205 | var beg, end token.Pos 206 | if nk != len(v.Names)-1 { 207 | beg = name.End() 208 | } else { 209 | beg = v.Type.End() 210 | } 211 | 212 | if nk != len(v.Names)-1 { 213 | end = v.Names[nk+1].Pos() 214 | } else if pk != len(list)-1 { 215 | end = list[pk+1].Pos() 216 | } else { 217 | end = t.Params.End() 218 | } 219 | tt = newTypeCommentLocatorComment(tt, beg, end, r.comments) 220 | } 221 | 222 | s.params = append(s.params, tt) 223 | } 224 | } 225 | } 226 | if t.Results != nil { 227 | list := t.Results.List 228 | for pk, v := range list { 229 | ty := r.evalType(info, v.Type) 230 | if ty == nil { 231 | continue 232 | } 233 | 234 | if v.Names == nil { 235 | t := newDeclaration("_", ty) 236 | s.results = append(s.results, t) 237 | continue 238 | } 239 | for nk, name := range v.Names { 240 | tv := newDeclaration(name.Name, ty) 241 | tt := newTypeOrigin(tv, v, r.info, v.Doc, v.Comment) 242 | 243 | // Parameter comment, Because the default criteria are not handled. 244 | if r.isCommentLocator { 245 | var beg, end token.Pos 246 | if nk != len(v.Names)-1 { 247 | beg = name.End() 248 | } else { 249 | beg = v.Type.End() 250 | } 251 | if nk != len(v.Names)-1 { 252 | end = v.Names[nk+1].Pos() 253 | } else if pk != len(list)-1 { 254 | end = list[pk+1].Pos() 255 | } else { 256 | end = t.Results.End() 257 | } 258 | tt = newTypeCommentLocatorComment(tt, beg, end, r.comments) 259 | } 260 | 261 | s.results = append(s.results, tt) 262 | } 263 | } 264 | } 265 | 266 | return s 267 | case *ast.InterfaceType: 268 | s := &typeInterface{} 269 | if t.Methods == nil { 270 | return s 271 | } 272 | 273 | for _, v := range t.Methods.List { 274 | ty := r.evalType(info, v.Type) 275 | if ty == nil { 276 | continue 277 | } 278 | 279 | if v.Names == nil { 280 | t := newDeclaration(ty.Name(), ty) 281 | tt := newTypeOrigin(t, v, r.info, v.Doc, v.Comment) 282 | s.all.Add(tt) 283 | s.anonymo.Add(tt) 284 | } else { 285 | for _, name := range v.Names { 286 | t := newDeclaration(name.Name, ty) 287 | tt := newTypeOrigin(t, v, r.info, v.Doc, v.Comment) 288 | s.all.Add(tt) 289 | s.method.Add(tt) 290 | } 291 | } 292 | } 293 | return s 294 | case *ast.MapType: 295 | k := r.evalType(info, t.Key) 296 | v := r.evalType(info, t.Value) 297 | s := newTypeMap(k, v) 298 | return s 299 | case *ast.ChanType: 300 | v := r.evalType(info, t.Value) 301 | s := newTypeChan(v, ChanDir(t.Dir)) 302 | return s 303 | case *ast.Ellipsis: 304 | v := r.evalType(info, t.Elt) 305 | s := newTypeSlice(v) 306 | return s 307 | default: 308 | } 309 | return nil 310 | } 311 | -------------------------------------------------------------------------------- /testdata/kind/a.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import ( 4 | . "time" 5 | ) 6 | 7 | // Kind:"Bool" Name:"TBool" String:"TBool" 8 | type TBool bool 9 | 10 | // Kind:"Int" Name:"TInt" String:"TInt" 11 | type TInt int 12 | 13 | // Kind:"Int8" Name:"TInt8" String:"TInt8" 14 | type TInt8 int8 15 | 16 | // Kind:"Int16" Name:"TInt16" String:"TInt16" 17 | type TInt16 int16 18 | 19 | // Kind:"Int32" Name:"TInt32" String:"TInt32" 20 | type TInt32 int32 21 | 22 | // Kind:"Int64" Name:"TInt64" String:"TInt64" 23 | type TInt64 int64 24 | 25 | // Kind:"Uint" Name:"TUint" String:"TUint" 26 | type TUint uint 27 | 28 | // Kind:"Uint8" Name:"TUint8" String:"TUint8" 29 | type TUint8 uint8 30 | 31 | // Kind:"Uint16" Name:"TUint16" String:"TUint16" 32 | type TUint16 uint16 33 | 34 | // Kind:"Uint32" Name:"TUint32" String:"TUint32" 35 | type TUint32 uint32 36 | 37 | // Kind:"Uint64" Name:"TUint64" String:"TUint64" 38 | type TUint64 uint64 39 | 40 | // Kind:"Uintptr" Name:"TUintptr" String:"TUintptr" 41 | type TUintptr uintptr 42 | 43 | // Kind:"Float32" Name:"TFloat32" String:"TFloat32" 44 | type TFloat32 float32 45 | 46 | // Kind:"Float64" Name:"TFloat64" String:"TFloat64" 47 | type TFloat64 float64 48 | 49 | // Kind:"Complex64" Name:"TComplex64" String:"TComplex64" 50 | type TComplex64 complex64 51 | 52 | // Kind:"Complex128" Name:"TComplex128" String:"TComplex128" 53 | type TComplex128 complex128 54 | 55 | // Kind:"String" Name:"TString" String:"TString" 56 | type TString string 57 | 58 | // Kind:"Byte" Name:"TByte" String:"TByte" 59 | type TByte byte 60 | 61 | // Kind:"Rune" Name:"TRune" String:"TRune" 62 | type TRune rune 63 | 64 | // Kind:"Error" Name:"TError" String:"TError" 65 | type TError error 66 | 67 | // Kind:"Array" Name:"TArray" String:"TArray" 68 | type TArray [1]struct{} 69 | 70 | // Kind:"Chan" Name:"TChan" String:"TChan" 71 | type TChan chan struct{} 72 | 73 | // Kind:"Func" Name:"TFunc" String:"TFunc" 74 | type TFunc func() 75 | 76 | // Kind:"Interface" Name:"TInterface" String:"TInterface" 77 | type TInterface interface{} 78 | 79 | // Kind:"Map" Name:"TMap" String:"TMap" 80 | type TMap map[string]struct{} 81 | 82 | // Kind:"Ptr" Name:"TPtr" String:"TPtr" 83 | type TPtr *int 84 | 85 | // Kind:"Slice" Name:"TSlice" String:"TSlice" 86 | type TSlice []struct{} 87 | 88 | // Kind:"Struct" Name:"MyTime" 89 | type MyTime Time 90 | -------------------------------------------------------------------------------- /testdata/pkg/a.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import "crypto/rand" 4 | 5 | // Name:"cryptoReader" 6 | // To:"Declaration" String:"rand.Int" 7 | // To:"Declaration,Declaration" String:"func(rand, max) (n, err)" 8 | var cryptoReader = rand.Int 9 | -------------------------------------------------------------------------------- /testdata/pkg/b.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import "math/rand" 4 | 5 | // Name:"mathReader" 6 | // To:"Declaration" String:"rand.Rand" 7 | // To:"Declaration,MethodByName:Seed" String:"Seed" 8 | var mathReader rand.Rand 9 | -------------------------------------------------------------------------------- /testdata/type/a.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | // String:"Struct" NumField:"3" NumMethod:"1" 4 | // To:"FieldByName:Name" Tag:"json:\"name\"" 5 | // To:"Field:0" Tag:"json:\"name\"" 6 | // To:"Field:0,Elem" Name:"string" 7 | // To:"FieldByName:Age" Tag:"json:\"age\"" 8 | // To:"FieldByName:Msg,Elem,FieldByName:Msg2" Tag:"json:\"msg\"" 9 | // To:"FieldByName:Msg2" Tag:"json:\"msg\"" 10 | // To:"Field:1" Tag:"json:\"age\"" 11 | // To:"Field:1,Elem" Name:"int" 12 | // To:"MethodByName:String" Name:"String" 13 | // To:"Method:0" Name:"String" 14 | // To:"MethodByName:Message1" Name:"Message1" 15 | // To:"MethodByName:Message2" Name:"Message2" 16 | type Struct struct { 17 | Name string `json:"name"` 18 | Age int `json:"age"` 19 | Msg 20 | } 21 | 22 | func (s *Struct) String() string { 23 | return "" 24 | } 25 | 26 | type Msg struct { 27 | Msg2 string `json:"msg"` 28 | } 29 | 30 | func (Msg) Message1() string { 31 | return "" 32 | } 33 | func (Msg) Message2() string { 34 | return "" 35 | } 36 | -------------------------------------------------------------------------------- /testdata/type/b.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "path/filepath" 8 | "time" 9 | ) 10 | 11 | // Name:"MyStringer" 12 | // Kind:"Interface" 13 | type MyStringer fmt.Stringer 14 | 15 | // Name:"a" Value:"" 16 | // To:"Declaration" Kind:"Interface" NumMethod:"1" NumField:"2" String:"interface{Stringer; MyString}" 17 | // To:"Declaration,MethodByName:String,Declaration" String:"func() (_)" Value:"" 18 | var a = (interface { 19 | fmt.Stringer 20 | MyString() string 21 | })(nil) 22 | 23 | // Name:"b" Value:"" 24 | // To:"Declaration" Kind:"Struct" String:"struct{fmt.Stringer; String string `s:\"\"`}" 25 | // To:"Declaration,MethodByName:String,Declaration" String:"func() (_)" Value:"" 26 | var b = struct { 27 | fmt.Stringer 28 | String string `s:""` 29 | }{} 30 | 31 | // Name:"c" Value:"" 32 | // To:"Declaration" Kind:"Map" Key:"String" Elme:"Int" String:"map[string]int" Value:"" 33 | var c = map[string]int{ 34 | "S": 1, 35 | } 36 | 37 | // Name:"d" Value:"" 38 | // To:"Declaration" Kind:"Slice" Elme:"Int" String:"[]int" Value:"" 39 | var d = make([]int, 10) 40 | 41 | // Name:"e" Value:"" 42 | // To:"Declaration" Kind:"Array" Elme:"Byte" Len:"8" String:"[8]byte" Value:"" 43 | var e = [8]byte{} 44 | 45 | // Name:"s" Value:"" 46 | // Kind:"Interface" NumMethod:"1" String:"s" Value:"" 47 | type s fmt.Stringer 48 | 49 | // Name:"t" Value:"" 50 | // Kind:"Struct" NumField:"3" String:"t" Value:"" 51 | type t time.Time 52 | 53 | // Name:"v" Value:"" 54 | // Kind:"Map" String:"v" Key:"string" Elme:"[]string" Value:"" 55 | type v url.Values 56 | 57 | // Name:"l" Value:"" 58 | // String:"l" Elme:"byte" Kind:"Slice" Value:"" 59 | type l json.RawMessage 60 | 61 | // Name:"w" Value:"" 62 | // String:"w" Kind:"Func" Value:"" 63 | type w filepath.WalkFunc 64 | -------------------------------------------------------------------------------- /testdata/value/a.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | const ( 4 | // Value:"1" Name:"U1" 5 | U1 uint = 1 << iota 6 | // Value:"2" Name:"U2" 7 | U2 8 | // Value:"4" Name:"U3" 9 | U3 10 | // Value:"3" Name:"U4" 11 | U4 = U1 | U2 12 | ) 13 | 14 | const ( 15 | // Value:"0" Name:"I1" 16 | I1 int = iota + 0 17 | // Value:"1" Name:"I2" 18 | I2 19 | // Value:"2" Name:"I3" 20 | I3 21 | // Value:"3" Name:"I4" 22 | I4 = I2 + I3 23 | // Value:"4" Name:"I5" 24 | I5 = -I2 + I3 + I4 25 | ) 26 | 27 | const ( 28 | // Value:"0" Name:"F1" 29 | F1 = float64(iota / 10) 30 | // Value:"1/10" Name:"F2" 31 | F2 32 | // Value:"1/5" Name:"F3" 33 | F3 34 | // Value:"3" Name:"F4" 35 | F4 float64 = iota 36 | ) 37 | 38 | const ( 39 | // Value:"\"A\"" Name:"StrA" 40 | StrA = "A" 41 | // Value:"\"CAB\"" Name:"StrB" 42 | StrB = StrC + "B" 43 | // Value:"\"CA\"" Name:"StrC" 44 | StrC = "C" + StrA 45 | ) 46 | 47 | var ( 48 | // Name:"Dir1" 49 | // To:"Declaration" String:"chan int" 50 | Dir1 chan int 51 | 52 | // Name:"Dir2" 53 | // To:"Declaration" String:"chan<- int" 54 | Dir2 chan<- int 55 | 56 | // Name:"Dir3" 57 | // To:"Declaration" String:"<-chan int" 58 | Dir3 <-chan int 59 | ) 60 | 61 | var ( 62 | // To:"Declaration" String:"[3]int" 63 | Arr1 [I4]int 64 | ) 65 | -------------------------------------------------------------------------------- /testdata/value/b.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | t "time" 7 | ) 8 | 9 | var ( 10 | 11 | // To:"Declaration" Kind:"String" 12 | S0, S1 = "", "" 13 | 14 | // Name:"TNow" 15 | // To:"Declaration,Declaration" Name:"Time" Kind:"Struct" 16 | TNow = t.Now() 17 | 18 | // Name:"MyNow" 19 | // To:"Declaration" String:"t.Now" 20 | // To:"Declaration,Declaration" String:"func() (_)" 21 | MyNow = t.Now 22 | 23 | // Name:"MyPrintf" 24 | // To:"Declaration" String:"fmt.Printf" 25 | // To:"Declaration,Declaration" Kind:"Func" String:"func(format, a...) (n, err)" 26 | MyPrintf = fmt.Printf 27 | 28 | // Name:"ts" Value:"" 29 | // To:"Declaration" String:"[]time.Time" 30 | ts []time.Time 31 | ) 32 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | "reflect" 6 | "sort" 7 | ) 8 | 9 | // Type is the representation of a Go type. 10 | // 11 | // Not all methods apply to all kinds of types. Restrictions, 12 | // if any, are noted in the documentation for each method. 13 | // Use the Kind method to find out the kind of type before 14 | // calling kind-specific methods. Calling a method 15 | // inappropriate to the kind of type causes a run-time panic. 16 | // 17 | // Type values are comparable, such as with the == operator, 18 | // so they can be used as map keys. 19 | // Two Type values are equal if they represent identical types. 20 | type Type interface { 21 | 22 | // String returns a string representation of the type. 23 | // The string representation may use shortened package names 24 | // (e.g., base64 instead of "encoding/base64") and is not 25 | // guaranteed to be unique among types. To test for type identity, 26 | // compare the Types directly. 27 | String() string 28 | 29 | // PkgPath returns a named type's package path, that is, the import path 30 | // that uniquely identifies the package, such as "encoding/base64". 31 | // If the type was predeclared (string, error) or unnamed (*T, struct{}, []int), 32 | // the package path will be the empty string. 33 | PkgPath() string 34 | 35 | // IsGoroot returns package is found in Go root 36 | IsGoroot() bool 37 | 38 | // Name returns the type's name within its package. 39 | // It returns an empty string for unnamed types. 40 | Name() string 41 | 42 | // Kind returns the specific kind of this type. 43 | Kind() Kind 44 | 45 | // Key returns a map type's key type. 46 | // It panics if the type's Kind is not Map. 47 | Key() Type 48 | 49 | // Elem returns a type's element type. 50 | // It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice. 51 | Elem() Type 52 | 53 | // Declaration returns a type's declaration. 54 | // It panics if the type's Kind is not declaration. 55 | Declaration() Type 56 | 57 | // Tag returns a field type's tag. 58 | // It panics if the type's Kind is not Field. 59 | Tag() reflect.StructTag 60 | 61 | // Len returns an array type's length. 62 | // It panics if the type's Kind is not Array. 63 | Len() int 64 | 65 | // Value returns a type's value. 66 | // Only get constants 67 | Value() string 68 | 69 | // ChanDir returns a channel type's direction. 70 | // It panics if the type's Kind is not Chan. 71 | ChanDir() ChanDir 72 | 73 | // Out returns the type of a function type's i'th output parameter. 74 | // It panics if the type's Kind is not Func. 75 | // It panics if i is not in the range [0, NumOut()). 76 | Out(int) Type 77 | 78 | // NumOut returns a function type's output parameter count. 79 | // It panics if the type's Kind is not Func. 80 | NumOut() int 81 | 82 | // In returns the type of a function type's i'th input parameter. 83 | // It panics if the type's Kind is not Func. 84 | // It panics if i is not in the range [0, NumIn()). 85 | In(int) Type 86 | 87 | // NumIn returns a function type's input parameter count. 88 | // It panics if the type's Kind is not Func. 89 | NumIn() int 90 | 91 | // IsVariadic reports whether a function type's final input parameter 92 | // is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's 93 | // implicit actual type []T. 94 | // 95 | // For concreteness, if t represents func(x int, y ... float64), then 96 | // 97 | // t.NumIn() == 2 98 | // t.In(0) is the reflect.Type for "int" 99 | // t.In(1) is the reflect.Type for "[]float64" 100 | // t.IsVariadic() == true 101 | // 102 | // IsVariadic panics if the type's Kind is not Func. 103 | IsVariadic() bool 104 | 105 | // Field returns a struct type's i'th field. 106 | // It panics if the type's Kind is not Struct. 107 | // It panics if i is not in the range [0, NumField()). 108 | Field(int) Type 109 | 110 | // FieldByName returns the struct field with the given name 111 | // and a boolean indicating if the field was found. 112 | FieldByName(string) (Type, bool) 113 | 114 | // NumField returns a struct type's field count. 115 | // It panics if the type's Kind is not Struct. 116 | NumField() int 117 | 118 | // IsAnonymous returns is an embedded field 119 | // It panics if the type's Kind is not Field. 120 | IsAnonymous() bool 121 | 122 | // Method returns the i'th method in the type's method set. 123 | // Not contain anonymo 124 | // It panics if i is not in the range [0, NumMethod()). 125 | // 126 | // For a non-interface type T or *T, the returned Method's Type and Func 127 | // fields describe a function whose first argument is the receiver. 128 | // 129 | // For an interface type, the returned Method's Type field gives the 130 | // method signature, without a receiver, and the Func field is nil. 131 | Method(int) Type 132 | 133 | // MethodByName returns the method with that name in the type's 134 | // method set and a boolean indicating if the method was found. 135 | // 136 | // For a non-interface type T or *T, the returned Method's Type and Func 137 | // fields describe a function whose first argument is the receiver. 138 | // 139 | // For an interface type, the returned Method's Type field gives the 140 | // method signature, without a receiver, and the Func field is nil. 141 | MethodByName(string) (Type, bool) 142 | 143 | // NumMethod returns the number of exported methods in the type's method set. 144 | // Not contain anonymo 145 | NumMethod() int 146 | 147 | // Child returns a scope type's i'th child. 148 | // It panics if i is not in the range [0, NumChild()). 149 | Child(int) Type 150 | 151 | // ChildByName returns the scope with the given name 152 | // and a boolean indicating if the child was found. 153 | ChildByName(string) (Type, bool) 154 | 155 | // NumChild returns a scope type's child count. 156 | NumChild() int 157 | 158 | // Origin returns the type's origin data within its package. 159 | Origin() ast.Node 160 | 161 | // Doc returns the type's doc within its package. 162 | Doc() *ast.CommentGroup 163 | 164 | // Comment returns the type's comment within its package. 165 | Comment() *ast.CommentGroup 166 | } 167 | 168 | type types []Type 169 | 170 | func (t *types) add(i int, n Type) { 171 | *t = append(*t, n) 172 | l := len(*t) 173 | copy((*t)[i+1:l], (*t)[i:l-1]) 174 | (*t)[i] = n 175 | } 176 | 177 | func (t *types) Add(n Type) { 178 | if n == nil { 179 | return 180 | } 181 | name := n.Name() 182 | i := t.SearchIndex(name) 183 | t.add(i, n) 184 | } 185 | 186 | func (t *types) AddNoRepeat(n Type) { 187 | if n == nil { 188 | return 189 | } 190 | name := n.Name() 191 | i := t.SearchIndex(name) 192 | tt := t.Index(i) 193 | if tt == nil || tt.Name() != name { 194 | t.add(i, n) 195 | } 196 | return 197 | } 198 | 199 | func (t *types) Search(name string) (Type, bool) { 200 | i := t.SearchIndex(name) 201 | tt := t.Index(i) 202 | if tt == nil || tt.Name() != name { 203 | return nil, false 204 | } 205 | return tt, true 206 | } 207 | 208 | func (t *types) SearchIndex(name string) int { 209 | return sort.Search(t.Len(), func(i int) bool { 210 | return t.Index(i).Name() <= name 211 | }) 212 | } 213 | 214 | func (t *types) Index(i int) Type { 215 | if i >= t.Len() { 216 | return nil 217 | } 218 | return (*t)[i] 219 | } 220 | 221 | func (t *types) Len() int { 222 | return len(*t) 223 | } 224 | 225 | type typeBase struct{} 226 | 227 | func (t *typeBase) IsGoroot() bool { 228 | return false 229 | } 230 | 231 | func (t *typeBase) PkgPath() string { 232 | return "" 233 | } 234 | 235 | func (t *typeBase) Name() string { 236 | return "" 237 | } 238 | 239 | func (t *typeBase) Kind() Kind { 240 | return Invalid 241 | } 242 | 243 | func (t *typeBase) Key() Type { 244 | panic("Key of non-map type") 245 | } 246 | 247 | func (t *typeBase) Elem() Type { 248 | panic("Elem of invalid type") 249 | } 250 | 251 | func (t *typeBase) Declaration() Type { 252 | panic("Declaration of non-declaration type") 253 | } 254 | 255 | func (t *typeBase) Tag() reflect.StructTag { 256 | panic("Tag of non-field type") 257 | } 258 | 259 | func (t *typeBase) Len() int { 260 | panic("Len of non-array type") 261 | } 262 | 263 | func (t *typeBase) Value() string { 264 | return "" 265 | } 266 | 267 | func (t *typeBase) ChanDir() ChanDir { 268 | panic("ChanDir of non-chan type") 269 | } 270 | 271 | func (t *typeBase) Out(int) Type { 272 | panic("Out of non-func type") 273 | } 274 | 275 | func (t *typeBase) NumOut() int { 276 | panic("NumOut of non-func type") 277 | } 278 | 279 | func (t *typeBase) In(int) Type { 280 | panic("In of non-func type") 281 | } 282 | 283 | func (t *typeBase) NumIn() int { 284 | panic("NumIn of non-func type") 285 | } 286 | 287 | func (t *typeBase) IsVariadic() bool { 288 | panic("IsVariadic of non-func type") 289 | } 290 | 291 | func (t *typeBase) Field(int) Type { 292 | panic("Field of non-struct type") 293 | } 294 | 295 | func (t *typeBase) FieldByName(string) (Type, bool) { 296 | panic("FieldByName of non-struct type") 297 | } 298 | 299 | func (t *typeBase) NumField() int { 300 | panic("NumField of non-struct type") 301 | } 302 | 303 | func (t *typeBase) IsAnonymous() bool { 304 | panic("IsAnonymous of non-field type") 305 | } 306 | 307 | func (t *typeBase) Method(int) Type { 308 | //panic("Method of invalid type") 309 | return nil 310 | } 311 | 312 | func (t *typeBase) MethodByName(string) (Type, bool) { 313 | //panic("MethodByName of invalid type") 314 | return nil, false 315 | } 316 | 317 | func (t *typeBase) NumMethod() int { 318 | //panic("NumMethod of invalid type") 319 | return 0 320 | } 321 | 322 | func (t *typeBase) Child(int) Type { 323 | //panic("Child of invalid type") 324 | return nil 325 | } 326 | 327 | func (t *typeBase) ChildByName(string) (Type, bool) { 328 | //panic("ChildByName of invalid type") 329 | return nil, false 330 | } 331 | 332 | func (t *typeBase) NumChild() int { 333 | //panic("NumChild of invalid type") 334 | return 0 335 | } 336 | 337 | func (t *typeBase) Origin() ast.Node { 338 | return nil 339 | } 340 | 341 | func (t *typeBase) Doc() *ast.CommentGroup { 342 | return nil 343 | } 344 | 345 | func (t *typeBase) Comment() *ast.CommentGroup { 346 | return nil 347 | } 348 | -------------------------------------------------------------------------------- /types_alias.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | func newTypeAlias(name string, typ Type) Type { 4 | return &typeAlias{ 5 | name: name, 6 | Type: typ, 7 | } 8 | } 9 | 10 | type typeAlias struct { 11 | name string 12 | Type 13 | } 14 | 15 | func (t *typeAlias) Name() string { 16 | return t.name 17 | } 18 | 19 | func (t *typeAlias) String() string { 20 | return t.name 21 | } 22 | -------------------------------------------------------------------------------- /types_array.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import "fmt" 4 | 5 | func newTypeArray(v Type, l int) Type { 6 | return &typeArray{ 7 | elem: v, 8 | le: l, 9 | } 10 | } 11 | 12 | type typeArray struct { 13 | typeBase 14 | le int 15 | elem Type 16 | } 17 | 18 | func (t *typeArray) String() string { 19 | return fmt.Sprintf("[%v]%v", t.le, t.elem) 20 | } 21 | 22 | func (t *typeArray) Kind() Kind { 23 | return Array 24 | } 25 | 26 | func (t *typeArray) Elem() Type { 27 | return t.elem 28 | } 29 | 30 | func (t *typeArray) Len() int { 31 | return t.le 32 | } 33 | -------------------------------------------------------------------------------- /types_builtin.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func newTypeBuiltin(kind Kind, value string) Type { 8 | return &typeBuiltin{ 9 | kind: kind, 10 | name: strings.ToLower(kind.String()), 11 | value: value, 12 | } 13 | } 14 | 15 | type typeBuiltin struct { 16 | typeBase 17 | kind Kind 18 | name string 19 | value string 20 | } 21 | 22 | func (t *typeBuiltin) String() string { 23 | return t.name 24 | } 25 | 26 | func (t *typeBuiltin) Name() string { 27 | return t.name 28 | } 29 | 30 | func (t *typeBuiltin) Kind() Kind { 31 | return t.kind 32 | } 33 | 34 | func (t *typeBuiltin) Value() string { 35 | return t.value 36 | } 37 | -------------------------------------------------------------------------------- /types_chan.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func newTypeChan(v Type, dir ChanDir) Type { 8 | return &typeChan{ 9 | elem: v, 10 | dir: dir, 11 | } 12 | } 13 | 14 | type typeChan struct { 15 | typeBase 16 | dir ChanDir 17 | elem Type 18 | } 19 | 20 | func (t *typeChan) String() string { 21 | switch t.dir { 22 | case RecvDir: 23 | return fmt.Sprintf("chan<- %v", t.elem) 24 | case SendDir: 25 | return fmt.Sprintf("<-chan %v", t.elem) 26 | case BothDir: 27 | } 28 | return fmt.Sprintf("chan %v", t.elem) 29 | } 30 | 31 | func (t *typeChan) Kind() Kind { 32 | return Chan 33 | } 34 | 35 | func (t *typeChan) Elem() Type { 36 | return t.elem 37 | } 38 | 39 | func (t *typeChan) ChanDir() ChanDir { 40 | return t.dir 41 | } 42 | -------------------------------------------------------------------------------- /types_comment_locator.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "sort" 7 | ) 8 | 9 | func newTypeCommentLocatorComment(typ Type, pos, end token.Pos, comments []*ast.CommentGroup) Type { 10 | return &typeCommentLocatorComment{ 11 | Type: typ, 12 | pos: pos, 13 | end: end, 14 | comments: comments, 15 | } 16 | } 17 | 18 | type typeCommentLocatorComment struct { 19 | Type 20 | pos token.Pos 21 | end token.Pos 22 | comments []*ast.CommentGroup 23 | comment *ast.CommentGroup 24 | } 25 | 26 | func (t *typeCommentLocatorComment) Comment() *ast.CommentGroup { 27 | if t.comment == nil { 28 | t.comment = commentLocator(t.pos, t.end, t.comments) 29 | } 30 | return t.comment 31 | } 32 | 33 | func commentLocator(pos, end token.Pos, comments []*ast.CommentGroup) *ast.CommentGroup { 34 | i := sort.Search(len(comments), func(i int) bool { 35 | return pos < comments[i].Pos() 36 | }) 37 | if i == -1 || i >= len(comments) { 38 | return nil 39 | } 40 | if comments[i].End() < end { 41 | return comments[i] 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /types_declaration.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | func newDeclaration(name string, typ Type) Type { 4 | return &typeDeclaration{ 5 | name: name, 6 | declaration: typ, 7 | } 8 | } 9 | 10 | type typeDeclaration struct { 11 | typeBase 12 | declaration Type 13 | name string 14 | } 15 | 16 | func (t *typeDeclaration) String() string { 17 | return t.name 18 | } 19 | 20 | func (t *typeDeclaration) Name() string { 21 | return t.name 22 | } 23 | 24 | func (t *typeDeclaration) Kind() Kind { 25 | return Declaration 26 | } 27 | 28 | func (t *typeDeclaration) Declaration() Type { 29 | return t.declaration 30 | } 31 | 32 | func (t *typeDeclaration) Value() string { 33 | return t.declaration.Value() 34 | } 35 | -------------------------------------------------------------------------------- /types_func.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type typeFunc struct { 8 | typeBase 9 | variadic bool 10 | params types 11 | results types 12 | } 13 | 14 | func (t *typeFunc) String() string { 15 | buf := bytes.NewBuffer(nil) 16 | buf.WriteString("func(") 17 | for i, v := range t.params { 18 | if i != 0 { 19 | buf.WriteString(", ") 20 | } 21 | buf.WriteString(v.String()) 22 | } 23 | if t.variadic { 24 | buf.WriteString("...") 25 | } 26 | buf.WriteString(") (") 27 | 28 | for i, v := range t.results { 29 | if i != 0 { 30 | buf.WriteString(", ") 31 | } 32 | buf.WriteString(v.String()) 33 | } 34 | buf.WriteString(")") 35 | return buf.String() 36 | } 37 | 38 | func (t *typeFunc) Kind() Kind { 39 | return Func 40 | } 41 | 42 | func (t *typeFunc) NumOut() int { 43 | return t.results.Len() 44 | } 45 | 46 | func (t *typeFunc) Out(i int) Type { 47 | return t.results.Index(i) 48 | } 49 | 50 | func (t *typeFunc) NumIn() int { 51 | return t.params.Len() 52 | } 53 | 54 | func (t *typeFunc) In(i int) Type { 55 | return t.params.Index(i) 56 | } 57 | 58 | func (t *typeFunc) IsVariadic() bool { 59 | return t.variadic 60 | } 61 | -------------------------------------------------------------------------------- /types_importer.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | func newTypeImport(name, path string, src string, imp importer) Type { 4 | return &typeImport{ 5 | name: name, 6 | path: path, 7 | src: src, 8 | imp: imp, 9 | } 10 | } 11 | 12 | type typeImport struct { 13 | typeBase 14 | name string 15 | path string 16 | src string 17 | imp importer 18 | scope Type 19 | } 20 | 21 | func (t *typeImport) check() { 22 | if t.scope != nil { 23 | return 24 | } 25 | 26 | s, err := t.imp.Import(t.path, t.src) 27 | if err != nil { 28 | t.imp.appendError(err) 29 | } 30 | t.scope = s 31 | } 32 | 33 | func (t *typeImport) PkgPath() string { 34 | return t.path 35 | } 36 | 37 | func (t *typeImport) IsGoroot() bool { 38 | _, ok := t.imp.ImportName(t.path, t.src) 39 | return ok 40 | } 41 | 42 | func (t *typeImport) String() string { 43 | return t.path 44 | } 45 | 46 | func (t *typeImport) Name() string { 47 | if t.name != "" { 48 | return t.name 49 | } 50 | 51 | t.name, _ = t.imp.ImportName(t.path, t.src) 52 | return t.name 53 | } 54 | 55 | func (t *typeImport) Kind() Kind { 56 | return Scope 57 | } 58 | 59 | func (t *typeImport) ChildByName(name string) (Type, bool) { 60 | t.check() 61 | if t.scope == nil { 62 | return nil, false 63 | } 64 | return t.scope.ChildByName(name) 65 | } 66 | 67 | func (t *typeImport) Child(i int) Type { 68 | t.check() 69 | if t.scope == nil { 70 | return nil 71 | } 72 | return t.scope.Child(i) 73 | } 74 | 75 | func (t *typeImport) NumChild() int { 76 | t.check() 77 | if t.scope == nil { 78 | return 0 79 | } 80 | return t.scope.NumChild() 81 | } 82 | -------------------------------------------------------------------------------- /types_interface.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type typeInterface struct { 8 | typeBase 9 | all types 10 | anonymo types 11 | method types 12 | } 13 | 14 | func (t *typeInterface) String() string { 15 | buf := bytes.NewBuffer(nil) 16 | buf.WriteString("interface{") 17 | for i, v := range t.all { 18 | if i != 0 { 19 | buf.WriteString("; ") 20 | } 21 | buf.WriteString(v.String()) 22 | } 23 | buf.WriteByte('}') 24 | return buf.String() 25 | } 26 | 27 | func (t *typeInterface) Kind() Kind { 28 | return Interface 29 | } 30 | 31 | func (t *typeInterface) NumMethod() int { 32 | return t.method.Len() 33 | } 34 | 35 | func (t *typeInterface) Method(i int) Type { 36 | return t.method.Index(i) 37 | } 38 | 39 | func (t *typeInterface) MethodByName(name string) (Type, bool) { 40 | b, ok := t.method.Search(name) 41 | if ok { 42 | return b, true 43 | } 44 | for _, v := range t.anonymo { 45 | v := v.Declaration() 46 | b, ok := v.MethodByName(name) 47 | if ok { 48 | return b, true 49 | } 50 | } 51 | return nil, false 52 | } 53 | 54 | func (t *typeInterface) NumField() int { 55 | return t.all.Len() 56 | } 57 | 58 | func (t *typeInterface) Field(i int) Type { 59 | return t.all.Index(i) 60 | } 61 | 62 | func (t *typeInterface) FieldByName(name string) (Type, bool) { 63 | b, ok := t.all.Search(name) 64 | if ok { 65 | return b, true 66 | } 67 | return nil, false 68 | } 69 | -------------------------------------------------------------------------------- /types_map.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import "fmt" 4 | 5 | func newTypeMap(k, v Type) Type { 6 | return &typeMap{ 7 | key: k, 8 | elem: v, 9 | } 10 | } 11 | 12 | type typeMap struct { 13 | typeBase 14 | key, elem Type 15 | } 16 | 17 | func (t *typeMap) String() string { 18 | return fmt.Sprintf("map[%v]%v", t.key, t.elem) 19 | } 20 | 21 | func (t *typeMap) Kind() Kind { 22 | return Map 23 | } 24 | 25 | func (t *typeMap) Key() Type { 26 | return t.key 27 | } 28 | 29 | func (t *typeMap) Elem() Type { 30 | return t.elem 31 | } 32 | -------------------------------------------------------------------------------- /types_named.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | "reflect" 6 | ) 7 | 8 | func newTypeNamed(name string, typ Type, info *infoFile) Type { 9 | return &typeNamed{ 10 | name: name, 11 | typ: typ, 12 | info: info, 13 | } 14 | } 15 | 16 | type typeNamed struct { 17 | name string 18 | info *infoFile 19 | typ Type 20 | } 21 | 22 | func (t *typeNamed) ToChild() (Type, bool) { 23 | if t.typ == nil { 24 | var ok bool 25 | t.typ, ok = t.info.GetPkgOrType(t.Name()) 26 | return t.typ, ok 27 | } 28 | return t.typ, true 29 | } 30 | 31 | func (t *typeNamed) PkgPath() string { 32 | child, ok := t.ToChild() 33 | if !ok { 34 | return "" 35 | } 36 | return child.PkgPath() 37 | } 38 | 39 | func (t *typeNamed) IsGoroot() bool { 40 | child, ok := t.ToChild() 41 | if !ok { 42 | return false 43 | } 44 | return child.IsGoroot() 45 | } 46 | 47 | func (t *typeNamed) Name() string { 48 | return t.name 49 | } 50 | 51 | func (t *typeNamed) String() string { 52 | return t.name 53 | } 54 | 55 | func (t *typeNamed) Kind() Kind { 56 | child, ok := t.ToChild() 57 | if !ok { 58 | return Invalid 59 | } 60 | return child.Kind() 61 | } 62 | 63 | func (t *typeNamed) Key() Type { 64 | child, ok := t.ToChild() 65 | if !ok { 66 | return nil 67 | } 68 | return child.Key() 69 | } 70 | 71 | func (t *typeNamed) Elem() Type { 72 | child, ok := t.ToChild() 73 | if !ok { 74 | return nil 75 | } 76 | return child.Elem() 77 | } 78 | 79 | func (t *typeNamed) Declaration() Type { 80 | child, ok := t.ToChild() 81 | if !ok { 82 | return nil 83 | } 84 | return child.Declaration() 85 | } 86 | 87 | func (t *typeNamed) NumField() int { 88 | child, ok := t.ToChild() 89 | if !ok { 90 | return 0 91 | } 92 | return child.NumField() 93 | } 94 | 95 | func (t *typeNamed) Field(i int) Type { 96 | child, ok := t.ToChild() 97 | if !ok { 98 | return nil 99 | } 100 | return child.Field(i) 101 | } 102 | 103 | func (t *typeNamed) FieldByName(name string) (Type, bool) { 104 | child, ok := t.ToChild() 105 | if !ok { 106 | return nil, false 107 | } 108 | return child.FieldByName(name) 109 | } 110 | 111 | func (t *typeNamed) IsAnonymous() bool { 112 | child, ok := t.ToChild() 113 | if !ok { 114 | return false 115 | } 116 | return child.IsAnonymous() 117 | } 118 | 119 | func (t *typeNamed) Tag() reflect.StructTag { 120 | child, ok := t.ToChild() 121 | if !ok { 122 | return "" 123 | } 124 | return child.Tag() 125 | } 126 | 127 | func (t *typeNamed) Len() int { 128 | child, ok := t.ToChild() 129 | if !ok { 130 | return 0 131 | } 132 | return child.Len() 133 | } 134 | 135 | func (t *typeNamed) Value() string { 136 | child, ok := t.ToChild() 137 | if !ok { 138 | return "" 139 | } 140 | return child.Value() 141 | } 142 | 143 | func (t *typeNamed) ChanDir() ChanDir { 144 | child, ok := t.ToChild() 145 | if !ok { 146 | return 0 147 | } 148 | return child.ChanDir() 149 | } 150 | 151 | func (t *typeNamed) NumOut() int { 152 | child, ok := t.ToChild() 153 | if !ok { 154 | return 0 155 | } 156 | return child.NumOut() 157 | } 158 | 159 | func (t *typeNamed) Out(i int) Type { 160 | child, ok := t.ToChild() 161 | if !ok { 162 | return nil 163 | } 164 | return child.Out(i) 165 | } 166 | 167 | func (t *typeNamed) NumIn() int { 168 | child, ok := t.ToChild() 169 | if !ok { 170 | return 0 171 | } 172 | return child.NumIn() 173 | } 174 | 175 | func (t *typeNamed) In(i int) Type { 176 | child, ok := t.ToChild() 177 | if !ok { 178 | return nil 179 | } 180 | return child.In(i) 181 | } 182 | 183 | func (t *typeNamed) IsVariadic() bool { 184 | child, ok := t.ToChild() 185 | if !ok { 186 | return false 187 | } 188 | return child.IsVariadic() 189 | } 190 | 191 | func (t *typeNamed) NumMethod() int { 192 | if t.info == nil { 193 | return 0 194 | } 195 | b := t.info.Methods[t.Name()] 196 | return b.Len() 197 | } 198 | 199 | func (t *typeNamed) Method(i int) Type { 200 | if t.info == nil { 201 | return nil 202 | } 203 | b := t.info.Methods[t.Name()] 204 | if b.Len() <= i { 205 | return nil 206 | } 207 | return b.Index(i) 208 | } 209 | 210 | func (t *typeNamed) MethodByName(name string) (Type, bool) { 211 | if t.info == nil { 212 | return nil, false 213 | } 214 | b := t.info.Methods[t.Name()] 215 | m, ok := b.Search(name) 216 | if ok { 217 | return m, true 218 | } 219 | child, ok := t.ToChild() 220 | if ok { 221 | return child.MethodByName(name) 222 | } 223 | return nil, false 224 | } 225 | 226 | func (t *typeNamed) Child(i int) Type { 227 | child, ok := t.ToChild() 228 | if !ok { 229 | return nil 230 | } 231 | return child.Child(i) 232 | } 233 | 234 | func (t *typeNamed) ChildByName(name string) (Type, bool) { 235 | child, ok := t.ToChild() 236 | if !ok { 237 | return nil, false 238 | } 239 | return child.ChildByName(name) 240 | } 241 | 242 | func (t *typeNamed) NumChild() int { 243 | child, ok := t.ToChild() 244 | if !ok { 245 | return 0 246 | } 247 | return child.NumChild() 248 | } 249 | 250 | func (t *typeNamed) Origin() ast.Node { 251 | child, ok := t.ToChild() 252 | if !ok { 253 | return nil 254 | } 255 | return child.Origin() 256 | } 257 | 258 | func (t *typeNamed) Doc() *ast.CommentGroup { 259 | child, ok := t.ToChild() 260 | if !ok { 261 | return nil 262 | } 263 | return child.Doc() 264 | } 265 | 266 | func (t *typeNamed) Comment() *ast.CommentGroup { 267 | child, ok := t.ToChild() 268 | if !ok { 269 | return nil 270 | } 271 | return child.Comment() 272 | } 273 | -------------------------------------------------------------------------------- /types_origin.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "go/ast" 5 | ) 6 | 7 | func newTypeOrigin(v Type, ori ast.Node, info *info, doc, comment *ast.CommentGroup) Type { 8 | return &typeOrigin{ 9 | Type: v, 10 | info: info, 11 | ori: ori, 12 | doc: doc, 13 | comment: comment, 14 | } 15 | } 16 | 17 | type typeOrigin struct { 18 | Type 19 | ori ast.Node 20 | info *info 21 | goroot bool 22 | doc *ast.CommentGroup 23 | comment *ast.CommentGroup 24 | pkgPath string 25 | } 26 | 27 | func (t *typeOrigin) Origin() ast.Node { 28 | return t.ori 29 | } 30 | 31 | func (t *typeOrigin) PkgPath() string { 32 | if t.pkgPath != "" { 33 | return t.pkgPath 34 | } 35 | t.pkgPath = t.Type.PkgPath() 36 | if t.pkgPath == "" { 37 | t.pkgPath = t.info.PkgPath 38 | } 39 | return t.pkgPath 40 | } 41 | 42 | func (t *typeOrigin) IsGoroot() bool { 43 | return t.info.Goroot || t.Type.IsGoroot() 44 | } 45 | 46 | func (t *typeOrigin) Doc() *ast.CommentGroup { 47 | if t.doc != nil { 48 | return t.doc 49 | } 50 | return t.Type.Doc() 51 | } 52 | 53 | func (t *typeOrigin) Comment() *ast.CommentGroup { 54 | if t.comment != nil { 55 | return t.comment 56 | } 57 | return t.Type.Comment() 58 | } 59 | -------------------------------------------------------------------------------- /types_ptr.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import "fmt" 4 | 5 | func newTypePtr(elem Type) Type { 6 | return &typePtr{ 7 | elem: elem, 8 | } 9 | } 10 | 11 | type typePtr struct { 12 | typeBase 13 | elem Type 14 | } 15 | 16 | func (t *typePtr) String() string { 17 | return fmt.Sprintf("*%v", t.elem) 18 | } 19 | 20 | func (t *typePtr) Kind() Kind { 21 | return Ptr 22 | } 23 | 24 | func (t *typePtr) Elem() Type { 25 | return t.elem 26 | } 27 | -------------------------------------------------------------------------------- /types_scope.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | func newTypeScope(name string, info *info) Type { 4 | return &typeScope{ 5 | name: name, 6 | info: info, 7 | } 8 | } 9 | 10 | type typeScope struct { 11 | typeBase 12 | name string 13 | info *info 14 | } 15 | 16 | func (t *typeScope) String() string { 17 | return t.name 18 | } 19 | 20 | func (t *typeScope) ChildByName(name string) (Type, bool) { 21 | return t.info.Named.Search(name) 22 | } 23 | 24 | func (t *typeScope) Child(i int) Type { 25 | return t.info.Named.Index(i) 26 | } 27 | 28 | func (t *typeScope) NumChild() int { 29 | return t.info.Named.Len() 30 | } 31 | 32 | func (t *typeScope) Name() string { 33 | return t.name 34 | } 35 | 36 | func (t *typeScope) Kind() Kind { 37 | return Scope 38 | } 39 | -------------------------------------------------------------------------------- /types_selector.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "reflect" 7 | ) 8 | 9 | func newSelector(x Type, sel string) Type { 10 | return &typeSelector{ 11 | x: x, 12 | sel: sel, 13 | } 14 | } 15 | 16 | type typeSelector struct { 17 | x Type 18 | sel string 19 | typ Type 20 | } 21 | 22 | func (t *typeSelector) ToChild() (Type, bool) { 23 | if t.typ != nil { 24 | return t.typ, true 25 | } 26 | s := t.x 27 | for { 28 | k := s.Kind() 29 | if k == Declaration { 30 | s = s.Declaration() 31 | continue 32 | } 33 | if k == Ptr { 34 | s = s.Elem() 35 | continue 36 | } 37 | break 38 | } 39 | name := t.sel 40 | 41 | b, ok := s.ChildByName(name) 42 | if ok { 43 | t.typ = b 44 | return b, true 45 | } 46 | b, ok = s.MethodByName(name) 47 | if ok { 48 | t.typ = b 49 | return b, true 50 | } 51 | if s.Kind() == Struct { 52 | b, ok = s.FieldByName(name) 53 | if ok { 54 | t.typ = b 55 | return b, true 56 | } 57 | } 58 | return nil, false 59 | } 60 | 61 | func (t *typeSelector) String() string { 62 | return fmt.Sprintf("%v.%v", t.x, t.sel) 63 | } 64 | 65 | func (t *typeSelector) PkgPath() string { 66 | child, ok := t.ToChild() 67 | if !ok { 68 | return "" 69 | } 70 | return child.PkgPath() 71 | } 72 | 73 | func (t *typeSelector) IsGoroot() bool { 74 | child, ok := t.ToChild() 75 | if !ok { 76 | return false 77 | } 78 | return child.IsGoroot() 79 | } 80 | 81 | func (t *typeSelector) Name() string { 82 | return t.sel 83 | } 84 | 85 | func (t *typeSelector) Kind() Kind { 86 | child, ok := t.ToChild() 87 | if !ok { 88 | return Invalid 89 | } 90 | return child.Kind() 91 | } 92 | 93 | func (t *typeSelector) Key() Type { 94 | child, ok := t.ToChild() 95 | if !ok { 96 | return nil 97 | } 98 | return child.Key() 99 | } 100 | 101 | func (t *typeSelector) Elem() Type { 102 | child, ok := t.ToChild() 103 | if !ok { 104 | return nil 105 | } 106 | return child.Elem() 107 | } 108 | 109 | func (t *typeSelector) Declaration() Type { 110 | child, ok := t.ToChild() 111 | if !ok { 112 | return nil 113 | } 114 | return child.Declaration() 115 | } 116 | 117 | func (t *typeSelector) NumField() int { 118 | child, ok := t.ToChild() 119 | if !ok { 120 | return 0 121 | } 122 | return child.NumField() 123 | } 124 | 125 | func (t *typeSelector) Field(i int) Type { 126 | child, ok := t.ToChild() 127 | if !ok { 128 | return nil 129 | } 130 | return child.Field(i) 131 | } 132 | 133 | func (t *typeSelector) FieldByName(name string) (Type, bool) { 134 | child, ok := t.ToChild() 135 | if !ok { 136 | return nil, false 137 | } 138 | return child.FieldByName(name) 139 | } 140 | 141 | func (t *typeSelector) IsAnonymous() bool { 142 | child, ok := t.ToChild() 143 | if !ok { 144 | return false 145 | } 146 | return child.IsAnonymous() 147 | } 148 | 149 | func (t *typeSelector) Tag() reflect.StructTag { 150 | child, ok := t.ToChild() 151 | if !ok { 152 | return "" 153 | } 154 | return child.Tag() 155 | } 156 | 157 | func (t *typeSelector) Len() int { 158 | child, ok := t.ToChild() 159 | if !ok { 160 | return 0 161 | } 162 | return child.Len() 163 | } 164 | 165 | func (t *typeSelector) Value() string { 166 | child, ok := t.ToChild() 167 | if !ok { 168 | return "" 169 | } 170 | return child.Value() 171 | } 172 | 173 | func (t *typeSelector) ChanDir() ChanDir { 174 | child, ok := t.ToChild() 175 | if !ok { 176 | return 0 177 | } 178 | return child.ChanDir() 179 | } 180 | 181 | func (t *typeSelector) NumOut() int { 182 | child, ok := t.ToChild() 183 | if !ok { 184 | return 0 185 | } 186 | return child.NumOut() 187 | } 188 | 189 | func (t *typeSelector) Out(i int) Type { 190 | child, ok := t.ToChild() 191 | if !ok { 192 | return nil 193 | } 194 | return child.Out(i) 195 | } 196 | 197 | func (t *typeSelector) NumIn() int { 198 | child, ok := t.ToChild() 199 | if !ok { 200 | return 0 201 | } 202 | return child.NumIn() 203 | } 204 | 205 | func (t *typeSelector) In(i int) Type { 206 | child, ok := t.ToChild() 207 | if !ok { 208 | return nil 209 | } 210 | return child.In(i) 211 | } 212 | 213 | func (t *typeSelector) IsVariadic() bool { 214 | child, ok := t.ToChild() 215 | if !ok { 216 | return false 217 | } 218 | return child.IsVariadic() 219 | } 220 | 221 | func (t *typeSelector) NumMethod() int { 222 | child, ok := t.ToChild() 223 | if !ok { 224 | return 0 225 | } 226 | return child.NumMethod() 227 | } 228 | 229 | func (t *typeSelector) Method(i int) Type { 230 | child, ok := t.ToChild() 231 | if !ok { 232 | return nil 233 | } 234 | return child.Method(i) 235 | } 236 | 237 | func (t *typeSelector) MethodByName(name string) (Type, bool) { 238 | child, ok := t.ToChild() 239 | if !ok { 240 | return nil, false 241 | } 242 | return child.MethodByName(name) 243 | } 244 | 245 | func (t *typeSelector) Child(i int) Type { 246 | child, ok := t.ToChild() 247 | if !ok { 248 | return nil 249 | } 250 | return child.Child(i) 251 | } 252 | 253 | func (t *typeSelector) ChildByName(name string) (Type, bool) { 254 | child, ok := t.ToChild() 255 | if !ok { 256 | return nil, false 257 | } 258 | return child.ChildByName(name) 259 | } 260 | 261 | func (t *typeSelector) NumChild() int { 262 | child, ok := t.ToChild() 263 | if !ok { 264 | return 0 265 | } 266 | return child.NumChild() 267 | } 268 | 269 | func (t *typeSelector) Origin() ast.Node { 270 | child, ok := t.ToChild() 271 | if !ok { 272 | return nil 273 | } 274 | return child.Origin() 275 | } 276 | 277 | func (t *typeSelector) Doc() *ast.CommentGroup { 278 | child, ok := t.ToChild() 279 | if !ok { 280 | return nil 281 | } 282 | return child.Doc() 283 | } 284 | 285 | func (t *typeSelector) Comment() *ast.CommentGroup { 286 | child, ok := t.ToChild() 287 | if !ok { 288 | return nil 289 | } 290 | return child.Comment() 291 | } 292 | -------------------------------------------------------------------------------- /types_slice.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func newTypeSlice(v Type) Type { 8 | return &typeSlice{ 9 | elem: v, 10 | } 11 | } 12 | 13 | type typeSlice struct { 14 | typeBase 15 | elem Type 16 | } 17 | 18 | func (t *typeSlice) String() string { 19 | return fmt.Sprintf("[]%v", t.elem.String()) 20 | } 21 | 22 | func (t *typeSlice) Kind() Kind { 23 | return Slice 24 | } 25 | 26 | func (t *typeSlice) Elem() Type { 27 | return t.elem 28 | } 29 | -------------------------------------------------------------------------------- /types_struct.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type typeStruct struct { 8 | typeBase 9 | fields types // fields 10 | } 11 | 12 | func (t *typeStruct) String() string { 13 | buf := bytes.NewBuffer(nil) 14 | buf.WriteString("struct{") 15 | for i, v := range t.fields { 16 | if i != 0 { 17 | buf.WriteString("; ") 18 | } 19 | buf.WriteString(v.String()) 20 | } 21 | buf.WriteByte('}') 22 | return buf.String() 23 | } 24 | 25 | func (t *typeStruct) Kind() Kind { 26 | return Struct 27 | } 28 | 29 | func (t *typeStruct) NumField() int { 30 | return t.fields.Len() 31 | } 32 | 33 | func (t *typeStruct) Field(i int) Type { 34 | return t.fields.Index(i) 35 | } 36 | 37 | func (t *typeStruct) FieldByName(name string) (Type, bool) { 38 | anonymo := []int{} 39 | for i, v := range t.fields { 40 | fieldName := v.Name() 41 | if fieldName == name { 42 | return v, true 43 | } 44 | if v.IsAnonymous() { 45 | anonymo = append(anonymo, i) 46 | } 47 | } 48 | 49 | for _, i := range anonymo { 50 | v := t.fields[i] 51 | v = v.Elem() 52 | t, ok := v.FieldByName(name) 53 | if ok { 54 | return t, true 55 | } 56 | } 57 | return nil, false 58 | } 59 | 60 | func (t *typeStruct) MethodByName(name string) (Type, bool) { 61 | for _, v := range t.fields { 62 | if v.IsAnonymous() { 63 | v = v.Elem() 64 | b, ok := v.MethodByName(name) 65 | if ok { 66 | return b, true 67 | } 68 | } 69 | } 70 | return nil, false 71 | } 72 | -------------------------------------------------------------------------------- /types_struct_field.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type typeStructField struct { 9 | typeBase 10 | name string 11 | elem Type 12 | tag reflect.StructTag 13 | anonymous bool 14 | } 15 | 16 | func (t *typeStructField) String() string { 17 | tag := "" 18 | if t.tag != "" { 19 | tag = " `" + string(t.tag) + "`" 20 | } 21 | if t.anonymous { 22 | return fmt.Sprintf("%v%s", t.elem, tag) 23 | } 24 | return fmt.Sprintf("%v %v%s", t.name, t.elem, tag) 25 | } 26 | 27 | func (t *typeStructField) Name() string { 28 | return t.name 29 | } 30 | 31 | func (t *typeStructField) Elem() Type { 32 | return t.elem 33 | } 34 | 35 | func (t *typeStructField) Kind() Kind { 36 | return Field 37 | } 38 | 39 | func (t *typeStructField) Tag() reflect.StructTag { 40 | return t.tag 41 | } 42 | 43 | func (t *typeStructField) IsAnonymous() bool { 44 | return t.anonymous 45 | } 46 | -------------------------------------------------------------------------------- /types_tuple.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | func newTypeTuple(all types) Type { 4 | switch len(all) { 5 | case 0: 6 | return nil 7 | case 1: 8 | return all[0] 9 | default: 10 | return &typeTuple{ 11 | Type: all[0], 12 | all: all, 13 | } 14 | } 15 | 16 | } 17 | 18 | type typeTuple struct { 19 | Type // [0] 20 | all types // [:] 21 | } 22 | -------------------------------------------------------------------------------- /types_value_bind.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | func newTypeValueBind(typ, val Type, info *info) Type { 4 | switch typ.Kind() { 5 | case Struct: 6 | if v, ok := val.(*typeValuePairs); ok { 7 | nt := &typeStruct{} 8 | fl := typ.NumField() 9 | for i := 0; i != fl; i++ { 10 | f := typ.Field(i) 11 | name := f.Name() 12 | b := f 13 | if val, ok := v.li.Search(name); ok { 14 | b = newTypeValueBind(f, val, info) 15 | } 16 | nt.fields = append(nt.fields, b) 17 | } 18 | return nt 19 | } 20 | case Declaration: 21 | name := typ.Name() 22 | t := newTypeValueBind(typ.Declaration(), val, info) 23 | if typ.Origin() != nil { 24 | t = newTypeOrigin(t, typ.Origin(), info, typ.Doc(), typ.Comment()) 25 | } 26 | return newDeclaration(name, t) 27 | } 28 | 29 | return newValueBind(typ, val.Value) 30 | } 31 | 32 | func newValueBind(typ Type, valfunc func() string) *typeValueBind { 33 | if tv, ok := typ.(*typeValueBind); ok { 34 | return newValueBind(tv.Type, tv.Value) 35 | } 36 | return &typeValueBind{ 37 | Type: typ, 38 | valfunc: valfunc, 39 | } 40 | } 41 | 42 | func newEvalBind(val Type, index int64, info *infoFile) Type { 43 | ori := val.Origin() 44 | val = newValueBind(val, func() string { 45 | v, err := constantEval(ori, int64(index), info) 46 | if err != nil { 47 | return "" 48 | } 49 | return v.ExactString() 50 | }) 51 | return val 52 | } 53 | 54 | type typeValueBind struct { 55 | Type 56 | valfunc func() string 57 | val string 58 | } 59 | 60 | func (t *typeValueBind) Value() string { 61 | if t.val == "" { 62 | t.val = t.valfunc() 63 | } 64 | return t.val 65 | } 66 | -------------------------------------------------------------------------------- /types_value_pair.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | func newTypeValuePair(key, value Type) Type { 9 | return &typeValuePair{ 10 | key: key, 11 | value: value, 12 | } 13 | } 14 | 15 | type typeValuePair struct { 16 | typeBase 17 | key, value Type 18 | } 19 | 20 | func (t *typeValuePair) String() string { 21 | return fmt.Sprintf("%v: %v", t.key, t.value) 22 | } 23 | 24 | func (t *typeValuePair) Name() string { 25 | return t.key.Name() 26 | } 27 | 28 | func (t *typeValuePair) Value() string { 29 | return t.value.Value() 30 | } 31 | 32 | type typeValuePairs struct { 33 | typeBase 34 | li types 35 | } 36 | 37 | func (t *typeValuePairs) String() string { 38 | buf := bytes.NewBuffer(nil) 39 | buf.WriteString("{") 40 | for i, v := range t.li { 41 | if i != 0 { 42 | buf.WriteString("; ") 43 | } 44 | buf.WriteString(v.String()) 45 | } 46 | buf.WriteByte('}') 47 | return buf.String() 48 | } 49 | -------------------------------------------------------------------------------- /utils_test.go: -------------------------------------------------------------------------------- 1 | package gotype 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Parse(t *testing.T, src string) Type { 8 | imp := getImporter(t) 9 | typ, err := imp.ImportSource("_", []byte(src)) 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | return typ 14 | } 15 | 16 | func Import(t *testing.T, path string) Type { 17 | imp := getImporter(t) 18 | typ, err := imp.Import(path, "") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | return typ 23 | 24 | } 25 | 26 | func getImporter(t *testing.T) *Importer { 27 | imp := NewImporter( 28 | WithCommentLocator(), 29 | ErrorHandler(func(err error) { 30 | t.Error(err) 31 | }), 32 | ) 33 | return imp 34 | } 35 | --------------------------------------------------------------------------------