github.com/SkycoinProject/gomobile@v0.0.0-20190312151609-d3739f865fa6/internal/importers/ast.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // The importers package uses go/ast to analyze Go packages or Go files 6 // and collect references to types whose package has a package prefix. 7 // It is used by the language specific importers to determine the set of 8 // wrapper types to be generated. 9 // 10 // For example, in the Go file 11 // 12 // package javaprogram 13 // 14 // import "Java/java/lang" 15 // 16 // func F() { 17 // o := lang.Object.New() 18 // ... 19 // } 20 // 21 // the java importer uses this package to determine that the "java/lang" 22 // package and the wrapper interface, lang.Object, needs to be generated. 23 // After calling AnalyzeFile or AnalyzePackages, the References result 24 // contains the reference to lang.Object and the names set will contain 25 // "New". 26 package importers 27 28 import ( 29 "errors" 30 "go/ast" 31 "go/build" 32 "go/parser" 33 "go/token" 34 "path" 35 "path/filepath" 36 "sort" 37 "strconv" 38 "strings" 39 ) 40 41 // References is the result of analyzing a Go file or set of Go packages. 42 // 43 // For example, the Go file 44 // 45 // package pkg 46 // 47 // import "Prefix/some/Package" 48 // 49 // var A = Package.Identifier 50 // 51 // Will result in a single PkgRef with the "some/Package" package and 52 // the Identifier name. The Names set will contain the single name, 53 // "Identifier". 54 type References struct { 55 // The list of references to identifiers in packages that are 56 // identified by a package prefix. 57 Refs []PkgRef 58 // The list of names used in at least one selector expression. 59 // Useful as a conservative upper bound on the set of identifiers 60 // referenced from a set of packages. 61 Names map[string]struct{} 62 // Embedders is a list of struct types with prefixed types 63 // embedded. 64 Embedders []Struct 65 } 66 67 // Struct is a representation of a struct type with embedded 68 // types. 69 type Struct struct { 70 Name string 71 Pkg string 72 PkgPath string 73 Refs []PkgRef 74 } 75 76 // PkgRef is a reference to an identifier in a package. 77 type PkgRef struct { 78 Name string 79 Pkg string 80 } 81 82 type refsSaver struct { 83 pkgPrefix string 84 *References 85 refMap map[PkgRef]struct{} 86 insideStruct bool 87 } 88 89 // AnalyzeFile scans the provided file for references to packages with the given 90 // package prefix. The list of unique (package, identifier) pairs is returned 91 func AnalyzeFile(file *ast.File, pkgPrefix string) (*References, error) { 92 visitor := newRefsSaver(pkgPrefix) 93 fset := token.NewFileSet() 94 files := map[string]*ast.File{file.Name.Name: file} 95 // Ignore errors (from unknown packages) 96 pkg, _ := ast.NewPackage(fset, files, visitor.importer(), nil) 97 ast.Walk(visitor, pkg) 98 visitor.findEmbeddingStructs("", pkg) 99 return visitor.References, nil 100 } 101 102 // AnalyzePackages scans the provided packages for references to packages with the given 103 // package prefix. The list of unique (package, identifier) pairs is returned 104 func AnalyzePackages(pkgs []*build.Package, pkgPrefix string) (*References, error) { 105 visitor := newRefsSaver(pkgPrefix) 106 imp := visitor.importer() 107 fset := token.NewFileSet() 108 for _, pkg := range pkgs { 109 fileNames := append(append([]string{}, pkg.GoFiles...), pkg.CgoFiles...) 110 files := make(map[string]*ast.File) 111 for _, name := range fileNames { 112 f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, name), nil, 0) 113 if err != nil { 114 return nil, err 115 } 116 files[name] = f 117 } 118 // Ignore errors (from unknown packages) 119 astpkg, _ := ast.NewPackage(fset, files, imp, nil) 120 ast.Walk(visitor, astpkg) 121 visitor.findEmbeddingStructs(pkg.ImportPath, astpkg) 122 } 123 return visitor.References, nil 124 } 125 126 // findEmbeddingStructs finds all top level declarations embedding a prefixed type. 127 // 128 // For example: 129 // 130 // import "Prefix/some/Package" 131 // 132 // type T struct { 133 // Package.Class 134 // } 135 func (v *refsSaver) findEmbeddingStructs(pkgpath string, pkg *ast.Package) { 136 var names []string 137 for _, obj := range pkg.Scope.Objects { 138 if obj.Kind != ast.Typ || !ast.IsExported(obj.Name) { 139 continue 140 } 141 names = append(names, obj.Name) 142 } 143 sort.Strings(names) 144 for _, name := range names { 145 obj := pkg.Scope.Objects[name] 146 147 t, ok := obj.Decl.(*ast.TypeSpec).Type.(*ast.StructType) 148 if !ok { 149 continue 150 } 151 var refs []PkgRef 152 for _, f := range t.Fields.List { 153 sel, ok := f.Type.(*ast.SelectorExpr) 154 if !ok { 155 continue 156 } 157 ref, ok := v.addRef(sel) 158 if !ok { 159 continue 160 } 161 if len(f.Names) > 0 && !f.Names[0].IsExported() { 162 continue 163 } 164 refs = append(refs, ref) 165 } 166 if len(refs) > 0 { 167 v.Embedders = append(v.Embedders, Struct{ 168 Name: obj.Name, 169 Pkg: pkg.Name, 170 PkgPath: pkgpath, 171 172 Refs: refs, 173 }) 174 } 175 } 176 } 177 178 func newRefsSaver(pkgPrefix string) *refsSaver { 179 s := &refsSaver{ 180 pkgPrefix: pkgPrefix, 181 refMap: make(map[PkgRef]struct{}), 182 References: &References{}, 183 } 184 s.Names = make(map[string]struct{}) 185 return s 186 } 187 188 func (v *refsSaver) importer() ast.Importer { 189 return func(imports map[string]*ast.Object, pkgPath string) (*ast.Object, error) { 190 if pkg, exists := imports[pkgPath]; exists { 191 return pkg, nil 192 } 193 if !strings.HasPrefix(pkgPath, v.pkgPrefix) { 194 return nil, errors.New("ignored") 195 } 196 pkg := ast.NewObj(ast.Pkg, path.Base(pkgPath)) 197 imports[pkgPath] = pkg 198 return pkg, nil 199 } 200 } 201 202 func (v *refsSaver) addRef(sel *ast.SelectorExpr) (PkgRef, bool) { 203 x, ok := sel.X.(*ast.Ident) 204 if !ok || x.Obj == nil { 205 return PkgRef{}, false 206 } 207 imp, ok := x.Obj.Decl.(*ast.ImportSpec) 208 if !ok { 209 return PkgRef{}, false 210 } 211 pkgPath, err := strconv.Unquote(imp.Path.Value) 212 if err != nil { 213 return PkgRef{}, false 214 } 215 if !strings.HasPrefix(pkgPath, v.pkgPrefix) { 216 return PkgRef{}, false 217 } 218 pkgPath = pkgPath[len(v.pkgPrefix):] 219 ref := PkgRef{Pkg: pkgPath, Name: sel.Sel.Name} 220 if _, exists := v.refMap[ref]; !exists { 221 v.refMap[ref] = struct{}{} 222 v.Refs = append(v.Refs, ref) 223 } 224 return ref, true 225 } 226 227 func (v *refsSaver) Visit(n ast.Node) ast.Visitor { 228 switch n := n.(type) { 229 case *ast.StructType: 230 // Use a copy of refsSaver that only accepts exported fields. It refers 231 // to the original refsSaver for collecting references. 232 v2 := *v 233 v2.insideStruct = true 234 return &v2 235 case *ast.Field: 236 if v.insideStruct && len(n.Names) == 1 && !n.Names[0].IsExported() { 237 return nil 238 } 239 case *ast.SelectorExpr: 240 v.Names[n.Sel.Name] = struct{}{} 241 if _, ok := v.addRef(n); ok { 242 return nil 243 } 244 case *ast.FuncDecl: 245 if n.Recv != nil { // Methods 246 v.Names[n.Name.Name] = struct{}{} 247 } 248 } 249 return v 250 }