diff options
author | Russ Cox <rsc@golang.org> | 2012-09-18 15:57:03 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2012-09-18 15:57:03 -0400 |
commit | a29f3136b40c5a3b5da4034fe5def863d4ad2733 (patch) | |
tree | bab3565e9aa04a6cb3315114c2ab052c11c7555a /src/cmd/api/goapi.go | |
parent | 8ed026e783ded812ee4bd03a47278f95168b9087 (diff) | |
download | go-git-a29f3136b40c5a3b5da4034fe5def863d4ad2733.tar.gz |
cmd/api: allow extension of interfaces with unexported methods
Fixes #4061.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6525047
Diffstat (limited to 'src/cmd/api/goapi.go')
-rw-r--r-- | src/cmd/api/goapi.go | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 992762602e..a7485e0447 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -892,15 +892,18 @@ type method struct { sig string // "([]byte) (int, error)", from funcSigString } -// interfaceMethods returns the expanded list of methods for an interface. +// interfaceMethods returns the expanded list of exported methods for an interface. +// The boolean complete reports whether the list contains all methods (that is, the +// interface has no unexported methods). // pkg is the complete package name ("net/http") // iname is the interface name. -func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { +func (w *Walker) interfaceMethods(pkg, iname string) (methods []method, complete bool) { t, ok := w.interfaces[pkgSymbol{pkg, iname}] if !ok { log.Fatalf("failed to find interface %s.%s", pkg, iname) } + complete = true for _, f := range t.Methods.List { typ := f.Type switch tv := typ.(type) { @@ -912,6 +915,8 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { name: mname.Name, sig: w.funcSigString(ft), }) + } else { + complete = false } } case *ast.Ident: @@ -927,7 +932,9 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { log.Fatalf("unexported embedded interface %q in exported interface %s.%s; confused", embedded, pkg, iname) } - methods = append(methods, w.interfaceMethods(pkg, embedded)...) + m, c := w.interfaceMethods(pkg, embedded) + methods = append(methods, m...) + complete = complete && c case *ast.SelectorExpr: lhs := w.nodeString(tv.X) rhs := w.nodeString(tv.Sel) @@ -935,7 +942,9 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { if !ok { log.Fatalf("can't resolve selector %q in interface %s.%s", lhs, pkg, iname) } - methods = append(methods, w.interfaceMethods(fpkg, rhs)...) + m, c := w.interfaceMethods(fpkg, rhs) + methods = append(methods, m...) + complete = complete && c default: log.Fatalf("unknown type %T in interface field", typ) } @@ -945,14 +954,28 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) { methNames := []string{} - pop := w.pushScope("type " + name + " interface") - for _, m := range w.interfaceMethods(w.curPackageName, name) { + methods, complete := w.interfaceMethods(w.curPackageName, name) + for _, m := range methods { methNames = append(methNames, m.name) w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig)) } + if !complete { + // The method set has unexported methods, so all the + // implementations are provided by the same package, + // so the method set can be extended. Instead of recording + // the full set of names (below), record only that there were + // unexported methods. (If the interface shrinks, we will notice + // because a method signature emitted during the last loop, + // will disappear.) + w.emitFeature("unexported methods") + } pop() + if !complete { + return + } + sort.Strings(methNames) if len(methNames) == 0 { w.emitFeature(fmt.Sprintf("type %s interface {}", name)) |