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