diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/cmd/api | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-upstream/1.1_hg20130304.tar.gz |
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/cmd/api')
-rw-r--r-- | src/cmd/api/clone.go | 251 | ||||
-rw-r--r-- | src/cmd/api/goapi.go | 225 | ||||
-rw-r--r-- | src/cmd/api/goapi_test.go | 66 | ||||
-rw-r--r-- | src/cmd/api/testdata/src/pkg/p1/golden.txt | 16 | ||||
-rw-r--r-- | src/cmd/api/testdata/src/pkg/p1/p1.go | 50 | ||||
-rw-r--r-- | src/cmd/api/testdata/src/pkg/p3/golden.txt | 3 | ||||
-rw-r--r-- | src/cmd/api/testdata/src/pkg/p3/p3.go | 6 |
7 files changed, 550 insertions, 67 deletions
diff --git a/src/cmd/api/clone.go b/src/cmd/api/clone.go new file mode 100644 index 000000000..180215f4b --- /dev/null +++ b/src/cmd/api/clone.go @@ -0,0 +1,251 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "go/ast" + "log" + "reflect" +) + +const debugClone = false + +// TODO(bradfitz): delete this function (and whole file) once +// http://golang.org/issue/4380 is fixed. +func clone(i interface{}) (cloned interface{}) { + if debugClone { + defer func() { + if !reflect.DeepEqual(i, cloned) { + log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned) + } + }() + } + switch v := i.(type) { + case nil: + return nil + case *ast.File: + o := &ast.File{ + Doc: v.Doc, // shallow + Package: v.Package, + Comments: v.Comments, // shallow + Name: v.Name, + Scope: v.Scope, + } + for _, x := range v.Decls { + o.Decls = append(o.Decls, clone(x).(ast.Decl)) + } + for _, x := range v.Imports { + o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec)) + } + for _, x := range v.Unresolved { + o.Unresolved = append(o.Unresolved, x) + } + return o + case *ast.GenDecl: + o := new(ast.GenDecl) + *o = *v + o.Specs = nil + for _, x := range v.Specs { + o.Specs = append(o.Specs, clone(x).(ast.Spec)) + } + return o + case *ast.TypeSpec: + o := new(ast.TypeSpec) + *o = *v + o.Type = cloneExpr(v.Type) + return o + case *ast.InterfaceType: + o := new(ast.InterfaceType) + *o = *v + o.Methods = clone(v.Methods).(*ast.FieldList) + return o + case *ast.FieldList: + if v == nil { + return v + } + o := new(ast.FieldList) + *o = *v + o.List = nil + for _, x := range v.List { + o.List = append(o.List, clone(x).(*ast.Field)) + } + return o + case *ast.Field: + o := &ast.Field{ + Doc: v.Doc, // shallow + Type: cloneExpr(v.Type), + Tag: clone(v.Tag).(*ast.BasicLit), + Comment: v.Comment, // shallow + } + for _, x := range v.Names { + o.Names = append(o.Names, clone(x).(*ast.Ident)) + } + return o + case *ast.FuncType: + if v == nil { + return v + } + return &ast.FuncType{ + Func: v.Func, + Params: clone(v.Params).(*ast.FieldList), + Results: clone(v.Results).(*ast.FieldList), + } + case *ast.FuncDecl: + if v == nil { + return v + } + return &ast.FuncDecl{ + Recv: clone(v.Recv).(*ast.FieldList), + Name: v.Name, + Type: clone(v.Type).(*ast.FuncType), + Body: v.Body, // shallow + } + case *ast.ValueSpec: + if v == nil { + return v + } + o := &ast.ValueSpec{ + Type: cloneExpr(v.Type), + } + for _, x := range v.Names { + o.Names = append(o.Names, x) + } + for _, x := range v.Values { + o.Values = append(o.Values, cloneExpr(x)) + } + return o + case *ast.CallExpr: + if v == nil { + return v + } + o := &ast.CallExpr{} + *o = *v + o.Args = cloneExprs(v.Args) + o.Fun = cloneExpr(v.Fun) + return o + case *ast.SelectorExpr: + if v == nil { + return nil + } + return &ast.SelectorExpr{ + X: cloneExpr(v.X), + Sel: v.Sel, + } + case *ast.ArrayType: + return &ast.ArrayType{ + Lbrack: v.Lbrack, + Len: cloneExpr(v.Len), + Elt: cloneExpr(v.Elt), + } + case *ast.StructType: + return &ast.StructType{ + Struct: v.Struct, + Fields: clone(v.Fields).(*ast.FieldList), + Incomplete: v.Incomplete, + } + case *ast.StarExpr: + return &ast.StarExpr{ + Star: v.Star, + X: cloneExpr(v.X), + } + case *ast.CompositeLit: + return &ast.CompositeLit{ + Type: cloneExpr(v.Type), + Lbrace: v.Lbrace, + Elts: cloneExprs(v.Elts), + Rbrace: v.Rbrace, + } + case *ast.UnaryExpr: + return &ast.UnaryExpr{ + OpPos: v.OpPos, + Op: v.Op, + X: cloneExpr(v.X), + } + case *ast.BinaryExpr: + return &ast.BinaryExpr{ + OpPos: v.OpPos, + Op: v.Op, + X: cloneExpr(v.X), + Y: cloneExpr(v.Y), + } + case *ast.Ellipsis: + return &ast.Ellipsis{ + Ellipsis: v.Ellipsis, + Elt: cloneExpr(v.Elt), + } + case *ast.KeyValueExpr: + return &ast.KeyValueExpr{ + Key: cloneExpr(v.Key), + Colon: v.Colon, + Value: cloneExpr(v.Value), + } + case *ast.FuncLit: + return &ast.FuncLit{ + Type: clone(v.Type).(*ast.FuncType), + Body: v.Body, // shallow + } + case *ast.MapType: + return &ast.MapType{ + Map: v.Map, + Key: cloneExpr(v.Key), + Value: cloneExpr(v.Value), + } + case *ast.ParenExpr: + return &ast.ParenExpr{ + Lparen: v.Lparen, + X: cloneExpr(v.X), + Rparen: v.Rparen, + } + case *ast.Ident, *ast.BasicLit: + return v + case *ast.ImportSpec: + return &ast.ImportSpec{ + Doc: v.Doc, // shallow + Name: v.Name, + Path: clone(v.Path).(*ast.BasicLit), + Comment: v.Comment, // shallow + EndPos: v.EndPos, + } + case *ast.ChanType: + return &ast.ChanType{ + Begin: v.Begin, + Arrow: v.Arrow, + Dir: v.Dir, + Value: cloneExpr(v.Value), + } + case *ast.TypeAssertExpr: + return &ast.TypeAssertExpr{ + X: cloneExpr(v.X), + Type: cloneExpr(v.Type), + } + case *ast.IndexExpr: + return &ast.IndexExpr{ + X: cloneExpr(v.X), + Index: cloneExpr(v.Index), + Lbrack: v.Lbrack, + Rbrack: v.Rbrack, + } + } + panic(fmt.Sprintf("Uncloneable type %T", i)) +} + +func cloneExpr(x ast.Expr) ast.Expr { + if x == nil { + return nil + } + return clone(x).(ast.Expr) +} + +func cloneExprs(x []ast.Expr) []ast.Expr { + if x == nil { + return nil + } + o := make([]ast.Expr, len(x)) + for i, x := range x { + o[i] = cloneExpr(x) + } + return o +} diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index ad1c6bb8c..0d76b0cdb 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -22,12 +22,14 @@ import ( "go/parser" "go/printer" "go/token" + "io" "io/ioutil" "log" "os" "os/exec" "path" "path/filepath" + "regexp" "runtime" "sort" "strconv" @@ -38,11 +40,12 @@ import ( var ( // TODO(bradfitz): once Go 1.1 comes out, allow the -c flag to take a comma-separated // list of files, rather than just one. - checkFile = flag.String("c", "", "optional filename to check API against") - allowNew = flag.Bool("allow_new", true, "allow API additions") - nextFile = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.") - verbose = flag.Bool("v", false, "verbose debugging") - forceCtx = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.") + checkFile = flag.String("c", "", "optional filename to check API against") + allowNew = flag.Bool("allow_new", true, "allow API additions") + exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool") + nextFile = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.") + verbose = flag.Bool("v", false, "verbose debugging") + forceCtx = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.") ) // contexts are the default contexts which are scanned, unless @@ -100,7 +103,7 @@ func setContexts() { func main() { flag.Parse() - if !strings.Contains(runtime.Version(), "weekly") { + if !strings.Contains(runtime.Version(), "weekly") && !strings.Contains(runtime.Version(), "devel") { if *nextFile != "" { fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFile) *nextFile = "" @@ -166,7 +169,6 @@ func main() { features = append(features, f2) } } - sort.Strings(features) fail := false defer func() { @@ -179,24 +181,45 @@ func main() { defer bw.Flush() if *checkFile == "" { + sort.Strings(features) for _, f := range features { fmt.Fprintf(bw, "%s\n", f) } return } - var required []string - for _, filename := range []string{*checkFile} { - required = append(required, fileFeatures(filename)...) + required := fileFeatures(*checkFile) + optional := fileFeatures(*nextFile) + exception := fileFeatures(*exceptFile) + fail = !compareAPI(bw, features, required, optional, exception) +} + +func set(items []string) map[string]bool { + s := make(map[string]bool) + for _, v := range items { + s[v] = true } - sort.Strings(required) + return s +} - var optional = make(map[string]bool) // feature => true - if *nextFile != "" { - for _, feature := range fileFeatures(*nextFile) { - optional[feature] = true - } +var spaceParensRx = regexp.MustCompile(` \(\S+?\)`) + +func featureWithoutContext(f string) string { + if !strings.Contains(f, "(") { + return f } + return spaceParensRx.ReplaceAllString(f, "") +} + +func compareAPI(w io.Writer, features, required, optional, exception []string) (ok bool) { + ok = true + + optionalSet := set(optional) + exceptionSet := set(exception) + featureSet := set(features) + + sort.Strings(features) + sort.Strings(required) take := func(sl *[]string) string { s := (*sl)[0] @@ -206,20 +229,27 @@ func main() { for len(required) > 0 || len(features) > 0 { switch { - case len(features) == 0 || required[0] < features[0]: - fmt.Fprintf(bw, "-%s\n", take(&required)) - fail = true // broke compatibility - case len(required) == 0 || required[0] > features[0]: + case len(features) == 0 || (len(required) > 0 && required[0] < features[0]): + feature := take(&required) + if exceptionSet[feature] { + fmt.Fprintf(w, "~%s\n", feature) + } else if featureSet[featureWithoutContext(feature)] { + // okay. + } else { + fmt.Fprintf(w, "-%s\n", feature) + ok = false // broke compatibility + } + case len(required) == 0 || (len(features) > 0 && required[0] > features[0]): newFeature := take(&features) - if optional[newFeature] { + if optionalSet[newFeature] { // Known added feature to the upcoming release. // Delete it from the map so we can detect any upcoming features // which were never seen. (so we can clean up the nextFile) - delete(optional, newFeature) + delete(optionalSet, newFeature) } else { - fmt.Fprintf(bw, "+%s\n", newFeature) + fmt.Fprintf(w, "+%s\n", newFeature) if !*allowNew { - fail = true // we're in lock-down mode for next release + ok = false // we're in lock-down mode for next release } } default: @@ -228,17 +258,22 @@ func main() { } } + // In next file, but not in API. var missing []string - for feature := range optional { + for feature := range optionalSet { missing = append(missing, feature) } sort.Strings(missing) for _, feature := range missing { - fmt.Fprintf(bw, "(in next file, but not in API) -%s\n", feature) + fmt.Fprintf(w, "±%s\n", feature) } + return } func fileFeatures(filename string) []string { + if filename == "" { + return nil + } bs, err := ioutil.ReadFile(filename) if err != nil { log.Fatalf("Error reading file %s: %v", filename, err) @@ -256,10 +291,11 @@ type pkgSymbol struct { symbol string // "RoundTripper" } +var fset = token.NewFileSet() + type Walker struct { context *build.Context root string - fset *token.FileSet scope []string features map[string]bool // set lastConstType string @@ -276,7 +312,6 @@ type Walker struct { func NewWalker() *Walker { return &Walker{ - fset: token.NewFileSet(), features: make(map[string]bool), packageState: make(map[string]loadState), interfaces: make(map[pkgSymbol]*ast.InterfaceType), @@ -297,20 +332,6 @@ const ( loaded ) -// hardCodedConstantType is a hack until the type checker is sufficient for our needs. -// Rather than litter the code with unnecessary type annotations, we'll hard-code -// the cases we can't handle yet. -func (w *Walker) hardCodedConstantType(name string) (typ string, ok bool) { - switch w.scope[0] { - case "pkg syscall": - switch name { - case "darwinAMD64": - return "bool", true - } - } - return "", false -} - func (w *Walker) Features() (fs []string) { for f := range w.features { fs = append(fs, f) @@ -333,6 +354,21 @@ func fileDeps(f *ast.File) (pkgs []string) { return } +var parsedFileCache = make(map[string]*ast.File) + +func parseFile(filename string) (*ast.File, error) { + f, ok := parsedFileCache[filename] + if !ok { + var err error + f, err = parser.ParseFile(fset, filename, nil, 0) + if err != nil { + return nil, err + } + parsedFileCache[filename] = f + } + return clone(f).(*ast.File), nil +} + // WalkPackage walks all files in package `name'. // WalkPackage does nothing if the package has already been loaded. func (w *Walker) WalkPackage(name string) { @@ -366,7 +402,7 @@ func (w *Walker) WalkPackage(name string) { files := append(append([]string{}, info.GoFiles...), info.CgoFiles...) for _, file := range files { - f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0) + f, err := parseFile(filepath.Join(dir, file)) if err != nil { log.Fatalf("error parsing package %s, file %s: %v", name, file, err) } @@ -501,7 +537,7 @@ func (w *Walker) walkFile(file *ast.File) { // Ignore. Handled in subsequent pass, by go/doc. default: log.Printf("unhandled %T, %#v\n", di, di) - printer.Fprint(os.Stderr, w.fset, di) + printer.Fprint(os.Stderr, fset, di) os.Stderr.Write([]byte("\n")) } } @@ -562,6 +598,10 @@ func (w *Walker) constValueType(vi interface{}) (string, error) { } return constDepPrefix + v.Name, nil case *ast.BinaryExpr: + switch v.Op { + case token.EQL, token.LSS, token.GTR, token.NOT, token.NEQ, token.LEQ, token.GEQ: + return "bool", nil + } left, err := w.constValueType(v.X) if err != nil { return "", err @@ -734,17 +774,11 @@ func (w *Walker) walkConst(vs *ast.ValueSpec) { var err error litType, err = w.constValueType(vs.Values[0]) if err != nil { - if t, ok := w.hardCodedConstantType(ident.Name); ok { - litType = t - err = nil - } else { - log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err) - } + log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err) } } } - if strings.HasPrefix(litType, constDepPrefix) { - dep := litType[len(constDepPrefix):] + if dep := strings.TrimPrefix(litType, constDepPrefix); dep != litType { w.constDep[ident.Name] = dep continue } @@ -816,7 +850,7 @@ func (w *Walker) nodeString(node interface{}) string { return "" } var b bytes.Buffer - printer.Fprint(&b, w.fset, node) + printer.Fprint(&b, fset, node) return b.String() } @@ -825,7 +859,7 @@ func (w *Walker) nodeDebug(node interface{}) string { return "" } var b bytes.Buffer - ast.Fprint(&b, w.fset, node, nil) + ast.Fprint(&b, fset, node, nil) return b.String() } @@ -844,7 +878,7 @@ func (w *Walker) walkTypeSpec(ts *ast.TypeSpec) { case *ast.InterfaceType: w.walkInterfaceType(name, t) default: - w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(ts.Type))) + w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(w.namelessType(ts.Type)))) } } @@ -892,15 +926,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 +949,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 +966,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 +976,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 +988,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)) @@ -994,18 +1051,38 @@ func (w *Walker) walkFuncDecl(f *ast.FuncDecl) { func (w *Walker) funcSigString(ft *ast.FuncType) string { var b bytes.Buffer + writeField := func(b *bytes.Buffer, f *ast.Field) { + if n := len(f.Names); n > 1 { + for i := 0; i < n; i++ { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(w.nodeString(w.namelessType(f.Type))) + } + } else { + b.WriteString(w.nodeString(w.namelessType(f.Type))) + } + } b.WriteByte('(') if ft.Params != nil { for i, f := range ft.Params.List { if i > 0 { b.WriteString(", ") } - b.WriteString(w.nodeString(w.namelessType(f.Type))) + writeField(&b, f) } } b.WriteByte(')') if ft.Results != nil { - if nr := len(ft.Results.List); nr > 0 { + nr := 0 + for _, f := range ft.Results.List { + if n := len(f.Names); n > 1 { + nr += n + } else { + nr++ + } + } + if nr > 0 { b.WriteByte(' ') if nr > 1 { b.WriteByte('(') @@ -1014,7 +1091,7 @@ func (w *Walker) funcSigString(ft *ast.FuncType) string { if i > 0 { b.WriteString(", ") } - b.WriteString(w.nodeString(w.namelessType(f.Type))) + writeField(&b, f) } if nr > 1 { b.WriteByte(')') @@ -1042,7 +1119,13 @@ func (w *Walker) namelessFieldList(fl *ast.FieldList) *ast.FieldList { fl2 := &ast.FieldList{} if fl != nil { for _, f := range fl.List { - fl2.List = append(fl2.List, w.namelessField(f)) + repeats := 1 + if len(f.Names) > 1 { + repeats = len(f.Names) + } + for i := 0; i < repeats; i++ { + fl2.List = append(fl2.List, w.namelessField(f)) + } } } return fl2 @@ -1056,10 +1139,21 @@ func (w *Walker) namelessField(f *ast.Field) *ast.Field { } } +var ( + byteRx = regexp.MustCompile(`\bbyte\b`) + runeRx = regexp.MustCompile(`\brune\b`) +) + func (w *Walker) emitFeature(feature string) { if !w.wantedPkg[w.curPackageName] { return } + if strings.Contains(feature, "byte") { + feature = byteRx.ReplaceAllString(feature, "uint8") + } + if strings.Contains(feature, "rune") { + feature = runeRx.ReplaceAllString(feature, "int32") + } f := strings.Join(w.scope, ", ") + ", " + feature if _, dup := w.features[f]; dup { panic("duplicate feature inserted: " + f) @@ -1075,6 +1169,7 @@ func (w *Walker) emitFeature(feature string) { } panic("feature contains newlines: " + f) } + w.features[f] = true if *verbose { log.Printf("feature: %s", f) diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go index c7cc601b1..1a86c0ec7 100644 --- a/src/cmd/api/goapi_test.go +++ b/src/cmd/api/goapi_test.go @@ -5,6 +5,7 @@ package main import ( + "bytes" "flag" "fmt" "io/ioutil" @@ -73,3 +74,68 @@ func TestGolden(t *testing.T) { } } } + +func TestCompareAPI(t *testing.T) { + tests := []struct { + name string + features, required, optional, exception []string + ok bool // want + out string // want + }{ + { + name: "feature added", + features: []string{"A", "B", "C", "D", "E", "F"}, + required: []string{"B", "D"}, + ok: true, + out: "+A\n+C\n+E\n+F\n", + }, + { + name: "feature removed", + features: []string{"C", "A"}, + required: []string{"A", "B", "C"}, + ok: false, + out: "-B\n", + }, + { + name: "feature added then removed", + features: []string{"A", "C"}, + optional: []string{"B"}, + required: []string{"A", "C"}, + ok: true, + out: "±B\n", + }, + { + name: "exception removal", + required: []string{"A", "B", "C"}, + features: []string{"A", "C"}, + exception: []string{"B"}, + ok: true, + out: "~B\n", + }, + { + // http://golang.org/issue/4303 + name: "contexts reconverging", + required: []string{ + "A", + "pkg syscall (darwin-386), type RawSockaddrInet6 struct", + "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct", + }, + features: []string{ + "A", + "pkg syscall, type RawSockaddrInet6 struct", + }, + ok: true, + out: "+pkg syscall, type RawSockaddrInet6 struct\n", + }, + } + for _, tt := range tests { + buf := new(bytes.Buffer) + gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception) + if gotok != tt.ok { + t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok) + } + if got := buf.String(); got != tt.out { + t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out) + } + } +} diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt index e334e5776..abcc0ce6c 100644 --- a/src/cmd/api/testdata/src/pkg/p1/golden.txt +++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt @@ -10,6 +10,7 @@ pkg p1, func Bar(int8, int16, int64) pkg p1, func Bar1(int8, int16, int64) uint64 pkg p1, func Bar2(int8, int16, int64) (uint8, uint64) pkg p1, func BarE() Error +pkg p1, func PlainFunc(int, int, string) (*B, error) pkg p1, func TakesFunc(func(int) int) pkg p1, method (*B) JustOnB() pkg p1, method (*B) OnBothTandBPtr() @@ -27,6 +28,9 @@ pkg p1, method (TPtrExported) OnEmbedded() pkg p1, method (TPtrUnexported) OnBothTandBPtr() pkg p1, method (TPtrUnexported) OnBothTandBVal() pkg p1, type B struct +pkg p1, type ByteStruct struct +pkg p1, type ByteStruct struct, B uint8 +pkg p1, type ByteStruct struct, R int32 pkg p1, type Codec struct pkg p1, type Codec struct, Func func(int, int) int pkg p1, type EmbedSelector struct @@ -37,15 +41,21 @@ pkg p1, type Embedded struct pkg p1, type Error interface { Error, Temporary } pkg p1, type Error interface, Error() string pkg p1, type Error interface, Temporary() bool -pkg p1, type I interface { Get, GetNamed, Name, PackageTwoMeth, Set } +pkg p1, type FuncType func(int, int, string) (*B, error) pkg p1, type I interface, Get(string) int64 pkg p1, type I interface, GetNamed(string) int64 pkg p1, type I interface, Name() string pkg p1, type I interface, PackageTwoMeth() pkg p1, type I interface, Set(string, int64) +pkg p1, type I interface, unexported methods pkg p1, type MyInt int pkg p1, type Namer interface { Name } pkg p1, type Namer interface, Name() string +pkg p1, type Private interface, X() +pkg p1, type Private interface, unexported methods +pkg p1, type Public interface { X, Y } +pkg p1, type Public interface, X() +pkg p1, type Public interface, Y() pkg p1, type S struct pkg p1, type S struct, Public *int pkg p1, type S struct, PublicTime time.Time @@ -58,7 +68,9 @@ pkg p1, type T struct pkg p1, type TPtrExported struct pkg p1, type TPtrExported struct, embedded *Embedded pkg p1, type TPtrUnexported struct -pkg p1, var ByteConv []byte +pkg p1, var Byte uint8 +pkg p1, var ByteConv []uint8 +pkg p1, var ByteFunc func(uint8) int32 pkg p1, var ChecksumError error pkg p1, var SIPtr *SI pkg p1, var SIPtr2 *SI diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go index d965bb75e..f94c9ceeb 100644 --- a/src/cmd/api/testdata/src/pkg/p1/p1.go +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -78,6 +78,16 @@ type I interface { private() } +type Public interface { + X() + Y() +} + +type Private interface { + X() + y() +} + type Error interface { error Temporary() bool @@ -139,8 +149,12 @@ type TPtrExported struct { *Embedded } +type FuncType func(x, y int, s string) (b *B, err error) + type Embedded struct{} +func PlainFunc(x, y int, s string) (b *B, err error) + func (*Embedded) OnEmbedded() {} func (*T) JustOnT() {} @@ -151,3 +165,39 @@ func (common) OnBothTandBVal() {} type EmbedSelector struct { time.Time } + +const ( + foo = "foo" + foo2 string = "foo2" + truth = foo == "foo" || foo2 == "foo2" +) + +func ellipsis(...string) {} + +var x = &S{ + Public: nil, + private: nil, + publicTime: time.Now(), +} + +var parenExpr = (1 + 5) + +var funcLit = func() {} + +var m map[string]int + +var chanVar chan int + +var ifaceVar interface{} = 5 + +var assertVar = ifaceVar.(int) + +var indexVar = m["foo"] + +var Byte byte +var ByteFunc func(byte) rune + +type ByteStruct struct { + B byte + R rune +} diff --git a/src/cmd/api/testdata/src/pkg/p3/golden.txt b/src/cmd/api/testdata/src/pkg/p3/golden.txt new file mode 100644 index 000000000..a7dcccd1b --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p3/golden.txt @@ -0,0 +1,3 @@ +pkg p3, func BadHop(int, int, int) (bool, bool, *ThirdBase, *ThirdBase, error) +pkg p3, method (*ThirdBase) GoodPlayer() (int, int, int) +pkg p3, type ThirdBase struct diff --git a/src/cmd/api/testdata/src/pkg/p3/p3.go b/src/cmd/api/testdata/src/pkg/p3/p3.go new file mode 100644 index 000000000..1b2b1a426 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p3/p3.go @@ -0,0 +1,6 @@ +package p3 + +type ThirdBase struct{} + +func (tb *ThirdBase) GoodPlayer() (i, j, k int) +func BadHop(i, j, k int) (l, m bool, n, o *ThirdBase, err error) |