internal/importers: skip unexported embedded fields

When determining if a Go struct embeds prefixed types, don't consider
unexported fields. This is important to avoid references cycles with the
Android databinding library; see the reverse example for details.

Change-Id: Ia820ca7ba4d1ec11a1f48651fac248eb753aad75
Reviewed-on: https://go-review.googlesource.com/35188
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/example/reverse/reverse/reverse.go b/example/reverse/reverse/reverse.go
index 501be71..8cc8b15 100644
--- a/example/reverse/reverse/reverse.go
+++ b/example/reverse/reverse/reverse.go
@@ -11,18 +11,25 @@
 	"Java/android/support/v7/app"
 	gopkg "Java/reverse"
 	rlayout "Java/reverse/R/layout"
+	"Java/reverse/databinding"
 	"Java/reverse/databinding/ActivityMainBinding"
 )
 
 type MainActivity struct {
 	app.AppCompatActivity
+	binding databinding.ActivityMainBinding
 }
 
 func (a *MainActivity) OnCreate(this gopkg.MainActivity, b os.Bundle) {
 	this.Super().OnCreate(b)
 	db := DataBindingUtil.SetContentView(this, rlayout.Activity_main)
-	mainBind := ActivityMainBinding.Cast(db)
-	mainBind.SetAct(this)
+	a.binding = ActivityMainBinding.Cast(db)
+	a.binding.SetAct(this)
+}
+
+func (a *MainActivity) OnDestroy(this gopkg.MainActivity) {
+	a.binding = nil // break reference cycle
+	this.Super().OnDestroy()
 }
 
 func (a *MainActivity) GetLabel() string {
diff --git a/internal/importers/ast.go b/internal/importers/ast.go
index 4a7b920..3c798c1 100644
--- a/internal/importers/ast.go
+++ b/internal/importers/ast.go
@@ -80,8 +80,9 @@
 
 type refsSaver struct {
 	pkgPrefix string
-	References
-	refMap map[PkgRef]struct{}
+	*References
+	refMap       map[PkgRef]struct{}
+	insideStruct bool
 }
 
 // AnalyzeFile scans the provided file for references to packages with the given
@@ -94,7 +95,7 @@
 	pkg, _ := ast.NewPackage(fset, files, visitor.importer(), nil)
 	ast.Walk(visitor, pkg)
 	visitor.findEmbeddingStructs(pkg)
-	return &visitor.References, nil
+	return visitor.References, nil
 }
 
 // AnalyzePackages scans the provided packages for references to packages with the given
@@ -118,7 +119,7 @@
 		ast.Walk(visitor, astpkg)
 		visitor.findEmbeddingStructs(astpkg)
 	}
-	return &visitor.References, nil
+	return visitor.References, nil
 }
 
 // findEmbeddingStructs finds all top level declarations embedding a prefixed type.
@@ -148,6 +149,9 @@
 		}
 		var refs []PkgRef
 		for _, f := range t.Fields.List {
+			if len(f.Names) > 0 && !f.Names[0].IsExported() {
+				continue
+			}
 			sel, ok := f.Type.(*ast.SelectorExpr)
 			if !ok {
 				continue
@@ -169,8 +173,9 @@
 
 func newRefsSaver(pkgPrefix string) *refsSaver {
 	s := &refsSaver{
-		pkgPrefix: pkgPrefix,
-		refMap:    make(map[PkgRef]struct{}),
+		pkgPrefix:  pkgPrefix,
+		refMap:     make(map[PkgRef]struct{}),
+		References: &References{},
 	}
 	s.Names = make(map[string]struct{})
 	return s
@@ -212,6 +217,17 @@
 
 func (v *refsSaver) Visit(n ast.Node) ast.Visitor {
 	switch n := n.(type) {
+	case *ast.StructType:
+		// Use a copy of refsSaver that only accepts exported fields. It refers
+		// to the original refsSaver for collecting references.
+		v2 := *v
+		v2.insideStruct = true
+		return &v2
+	case *ast.Field:
+		if v.insideStruct && len(n.Names) == 1 && !n.Names[0].IsExported() {
+			return nil
+		}
+		return v
 	case *ast.SelectorExpr:
 		v.Names[n.Sel.Name] = struct{}{}
 		if ref, ok := v.parseRef(n); ok {
diff --git a/internal/importers/ast_test.go b/internal/importers/ast_test.go
index ee9bc8c..a26b3c3 100644
--- a/internal/importers/ast_test.go
+++ b/internal/importers/ast_test.go
@@ -16,6 +16,7 @@
 
 type T struct {
 	Name.Type
+	hidden Name.Type2
 }
 `
 	fset := token.NewFileSet()
diff --git a/internal/importers/java/java.go b/internal/importers/java/java.go
index 68a3073..dfba7be 100644
--- a/internal/importers/java/java.go
+++ b/internal/importers/java/java.go
@@ -243,7 +243,7 @@
 			jpkg := strings.Replace(ref.Pkg, "/", ".", -1)
 			super := jpkg + "." + ref.Name
 			if _, exists := j.clsMap[super]; !exists {
-				return nil, fmt.Errorf("class %q not found", super)
+				return nil, fmt.Errorf("failed to find Java class %s, embedded by %s", super, n)
 			}
 			cls.Supers = append(cls.Supers, super)
 		}