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