[release-branch.go1.8] all: merge master into release-branch.go1.8
f8ed2e40
cmd/guru: fix typo of 'hyphen' to rename to 'comma'61efd711
godoc: fix quadratic and ASCII-only struct field linkification721f2184
imports: remove unnecessary string conversionfcfba28e
go/internal/gccgoimporter: support for type aliasesadd1aac0
static: don't use the jQuery func for looking up based on hash608c3b09
present: fix typo0d047c8d
cmd/godex: handle printing of type aliasesde557280
go/gcimporter15: update import/export to handle type aliases5d76f8ce
static: detect platform to hide/show unix/windows instructions Change-Id: If3753b030500781b5bf086e56485e75343744799
This commit is contained in:
commit
9f028c45f3
|
@ -2,8 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
// This file implements access to gc-generated export data.
|
||||
|
||||
package main
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
// This file implements access to gccgo-generated export data.
|
||||
|
||||
package main
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build !go1.9
|
||||
|
||||
package main
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return false // there are no type aliases before Go 1.9
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package main
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return obj.IsAlias()
|
||||
}
|
|
@ -2,8 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -143,7 +141,13 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
|
|||
p.printDecl("type", len(typez), func() {
|
||||
for _, obj := range typez {
|
||||
p.printf("%s ", obj.Name())
|
||||
p.writeType(p.pkg, obj.Type().Underlying())
|
||||
typ := obj.Type()
|
||||
if isAlias(obj) {
|
||||
p.print("= ")
|
||||
p.writeType(p.pkg, typ)
|
||||
} else {
|
||||
p.writeType(p.pkg, typ.Underlying())
|
||||
}
|
||||
p.print("\n")
|
||||
}
|
||||
})
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
// This file implements access to export data from source.
|
||||
|
||||
package main
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
// This file implements writing of types. The functionality is lifted
|
||||
// directly from go/types, but now contains various modifications for
|
||||
// nicer output.
|
||||
|
|
|
@ -52,14 +52,14 @@ func parsePos(pos string) (filename string, startOffset, endOffset int, err erro
|
|||
filename, offset := pos[:colon], pos[colon+1:]
|
||||
startOffset = -1
|
||||
endOffset = -1
|
||||
if hyphen := strings.Index(offset, ","); hyphen < 0 {
|
||||
if comma := strings.Index(offset, ","); comma < 0 {
|
||||
// e.g. "foo.go:#123"
|
||||
startOffset = parseOctothorpDecimal(offset)
|
||||
endOffset = startOffset
|
||||
} else {
|
||||
// e.g. "foo.go:#123,#456"
|
||||
startOffset = parseOctothorpDecimal(offset[:hyphen])
|
||||
endOffset = parseOctothorpDecimal(offset[hyphen+1:])
|
||||
startOffset = parseOctothorpDecimal(offset[:comma])
|
||||
endOffset = parseOctothorpDecimal(offset[comma+1:])
|
||||
}
|
||||
if startOffset < 0 || endOffset < 0 {
|
||||
err = fmt.Errorf("invalid offset %q in query position", offset)
|
||||
|
|
|
@ -39,11 +39,12 @@ const debugFormat = false // default: false
|
|||
const trace = false // default: false
|
||||
|
||||
// Current export format version. Increase with each format change.
|
||||
// 3: added aliasTag and export of aliases
|
||||
// 4: type name objects support type aliases, uses aliasTag
|
||||
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
|
||||
// 2: removed unused bool in ODCL export (compiler only)
|
||||
// 1: header format change (more regular), export package for _ struct fields
|
||||
// 0: Go1.7 encoding
|
||||
const exportVersion = 3
|
||||
const exportVersion = 4
|
||||
|
||||
// trackAllTypes enables cycle tracking for all types, not just named
|
||||
// types. The existing compiler invariants assume that unnamed types
|
||||
|
@ -65,9 +66,6 @@ type exporter struct {
|
|||
pkgIndex map[*types.Package]int
|
||||
typIndex map[types.Type]int
|
||||
|
||||
// track objects that we've reexported already
|
||||
reexported map[types.Object]bool
|
||||
|
||||
// position encoding
|
||||
posInfoFormat bool
|
||||
prevFile string
|
||||
|
@ -86,7 +84,6 @@ func BExportData(fset *token.FileSet, pkg *types.Package) []byte {
|
|||
strIndex: map[string]int{"": 0}, // empty string is mapped to 0
|
||||
pkgIndex: make(map[*types.Package]int),
|
||||
typIndex: make(map[types.Type]int),
|
||||
reexported: make(map[types.Object]bool),
|
||||
posInfoFormat: true, // TODO(gri) might become a flag, eventually
|
||||
}
|
||||
|
||||
|
@ -188,7 +185,13 @@ func (p *exporter) obj(obj types.Object) {
|
|||
p.value(obj.Val())
|
||||
|
||||
case *types.TypeName:
|
||||
p.tag(typeTag)
|
||||
if isAlias(obj) {
|
||||
p.tag(aliasTag)
|
||||
p.pos(obj)
|
||||
p.qualifiedName(obj)
|
||||
} else {
|
||||
p.tag(typeTag)
|
||||
}
|
||||
p.typ(obj.Type())
|
||||
|
||||
case *types.Var:
|
||||
|
@ -205,21 +208,6 @@ func (p *exporter) obj(obj types.Object) {
|
|||
p.paramList(sig.Params(), sig.Variadic())
|
||||
p.paramList(sig.Results(), false)
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
// case *types_Alias:
|
||||
// // make sure the original is exported before the alias
|
||||
// // (if the alias declaration was invalid, orig will be nil)
|
||||
// orig := original(obj)
|
||||
// if orig != nil && !p.reexported[orig] {
|
||||
// p.obj(orig)
|
||||
// p.reexported[orig] = true
|
||||
// }
|
||||
|
||||
// p.tag(aliasTag)
|
||||
// p.pos(obj)
|
||||
// p.string(obj.Name())
|
||||
// p.qualifiedName(orig)
|
||||
|
||||
default:
|
||||
log.Fatalf("gcimporter: unexpected object %v (%T)", obj, obj)
|
||||
}
|
||||
|
@ -279,10 +267,6 @@ func commonPrefixLen(a, b string) int {
|
|||
}
|
||||
|
||||
func (p *exporter) qualifiedName(obj types.Object) {
|
||||
if obj == nil {
|
||||
p.string("")
|
||||
return
|
||||
}
|
||||
p.string(obj.Name())
|
||||
p.pkg(obj.Pkg(), false)
|
||||
}
|
||||
|
@ -482,28 +466,45 @@ func (p *exporter) method(m *types.Func) {
|
|||
p.paramList(sig.Results(), false)
|
||||
}
|
||||
|
||||
// fieldName is like qualifiedName but it doesn't record the package for exported names.
|
||||
func (p *exporter) fieldName(f *types.Var) {
|
||||
name := f.Name()
|
||||
|
||||
// anonymous field with unexported base type name: use "?" as field name
|
||||
// (bname != "" per spec, but we are conservative in case of errors)
|
||||
if f.Anonymous() {
|
||||
base := f.Type()
|
||||
if ptr, ok := base.(*types.Pointer); ok {
|
||||
base = ptr.Elem()
|
||||
}
|
||||
if named, ok := base.(*types.Named); ok && !named.Obj().Exported() {
|
||||
// anonymous field with unexported base type name
|
||||
name = "?" // unexported name to force export of package
|
||||
// anonymous field - we distinguish between 3 cases:
|
||||
// 1) field name matches base type name and is exported
|
||||
// 2) field name matches base type name and is not exported
|
||||
// 3) field name doesn't match base type name (alias name)
|
||||
bname := basetypeName(f.Type())
|
||||
if name == bname {
|
||||
if ast.IsExported(name) {
|
||||
name = "" // 1) we don't need to know the field name or package
|
||||
} else {
|
||||
name = "?" // 2) use unexported name "?" to force package export
|
||||
}
|
||||
} else {
|
||||
// 3) indicate alias and export name as is
|
||||
// (this requires an extra "@" but this is a rare case)
|
||||
p.string("@")
|
||||
}
|
||||
}
|
||||
|
||||
p.string(name)
|
||||
if !f.Exported() {
|
||||
if name != "" && !ast.IsExported(name) {
|
||||
p.pkg(f.Pkg(), false)
|
||||
}
|
||||
}
|
||||
|
||||
func basetypeName(typ types.Type) string {
|
||||
switch typ := deref(typ).(type) {
|
||||
case *types.Basic:
|
||||
return typ.Name()
|
||||
case *types.Named:
|
||||
return typ.Obj().Name()
|
||||
default:
|
||||
return "" // unnamed type
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) paramList(params *types.Tuple, variadic bool) {
|
||||
// use negative length to indicate unnamed parameters
|
||||
// (look at the first parameter only since either all
|
||||
|
@ -797,10 +798,10 @@ func (p *exporter) tracef(format string, args ...interface{}) {
|
|||
// Debugging support.
|
||||
// (tagString is only used when tracing is enabled)
|
||||
var tagString = [...]string{
|
||||
// Packages:
|
||||
// Packages
|
||||
-packageTag: "package",
|
||||
|
||||
// Types:
|
||||
// Types
|
||||
-namedTag: "named type",
|
||||
-arrayTag: "array",
|
||||
-sliceTag: "slice",
|
||||
|
@ -812,7 +813,7 @@ var tagString = [...]string{
|
|||
-mapTag: "map",
|
||||
-chanTag: "chan",
|
||||
|
||||
// Values:
|
||||
// Values
|
||||
-falseTag: "false",
|
||||
-trueTag: "true",
|
||||
-int64Tag: "int64",
|
||||
|
@ -821,4 +822,7 @@ var tagString = [...]string{
|
|||
-complexTag: "complex",
|
||||
-stringTag: "string",
|
||||
-unknownTag: "unknown",
|
||||
|
||||
// Type aliases
|
||||
-aliasTag: "alias",
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
// +build ignore
|
||||
|
||||
package gcimporter_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
gcimporter "golang.org/x/tools/go/gcimporter15"
|
||||
)
|
||||
|
||||
func disabledTestInvalidAlias(t *testing.T) {
|
||||
// parse and typecheck
|
||||
const src = "package p; func InvalidAlias => foo.f"
|
||||
fset1 := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset1, "p.go", src, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var conf types.Config
|
||||
pkg, err := conf.Check("p", fset1, []*ast.File{f}, nil)
|
||||
if err == nil {
|
||||
t.Fatal("invalid source type-checked without error")
|
||||
}
|
||||
if pkg == nil {
|
||||
t.Fatal("nil package returned")
|
||||
}
|
||||
|
||||
// export
|
||||
exportdata := gcimporter.BExportData(fset1, pkg)
|
||||
|
||||
// import
|
||||
imports := make(map[string]*types.Package)
|
||||
fset2 := token.NewFileSet()
|
||||
_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
|
||||
if err != nil {
|
||||
t.Fatalf("BImportData(%s): %v", pkg.Path(), err)
|
||||
}
|
||||
|
||||
// pkg2 must contain InvalidAlias as an invalid Alias
|
||||
obj := pkg2.Scope().Lookup("InvalidAlias")
|
||||
if obj == nil {
|
||||
t.Fatal("InvalidAlias not found")
|
||||
}
|
||||
alias, ok := obj.(*types.Alias)
|
||||
if !ok {
|
||||
t.Fatalf("got %v; want alias", alias)
|
||||
}
|
||||
if alias.Type() != types.Typ[types.Invalid] || alias.Orig() != nil {
|
||||
t.Fatalf("got %v (orig = %v); want invalid alias", alias, alias.Orig())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package gcimporter_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
gcimporter "golang.org/x/tools/go/gcimporter15"
|
||||
)
|
||||
|
||||
const src = `
|
||||
package p
|
||||
|
||||
type (
|
||||
T0 = int32
|
||||
T1 = struct{}
|
||||
T2 = struct{ T1 }
|
||||
Invalid = foo // foo is undeclared
|
||||
)
|
||||
`
|
||||
|
||||
func checkPkg(t *testing.T, pkg *types.Package, label string) {
|
||||
T1 := types.NewStruct(nil, nil)
|
||||
T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
typ types.Type
|
||||
}{
|
||||
{"T0", types.Typ[types.Int32]},
|
||||
{"T1", T1},
|
||||
{"T2", T2},
|
||||
{"Invalid", types.Typ[types.Invalid]},
|
||||
} {
|
||||
obj := pkg.Scope().Lookup(test.name)
|
||||
if obj == nil {
|
||||
t.Errorf("%s: %s not found", label, test.name)
|
||||
continue
|
||||
}
|
||||
tname, _ := obj.(*types.TypeName)
|
||||
if tname == nil {
|
||||
t.Errorf("%s: %v not a type name", label, obj)
|
||||
continue
|
||||
}
|
||||
if !tname.IsAlias() {
|
||||
t.Errorf("%s: %v: not marked as alias", label, tname)
|
||||
continue
|
||||
}
|
||||
if got := tname.Type(); !types.Identical(got, test.typ) {
|
||||
t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeAliases(t *testing.T) {
|
||||
// parse and typecheck
|
||||
fset1 := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset1, "p.go", src, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var conf types.Config
|
||||
pkg1, err := conf.Check("p", fset1, []*ast.File{f}, nil)
|
||||
if err == nil {
|
||||
// foo in undeclared in src; we should see an error
|
||||
t.Fatal("invalid source type-checked without error")
|
||||
}
|
||||
if pkg1 == nil {
|
||||
// despite incorrect src we should see a (partially) type-checked package
|
||||
t.Fatal("nil package returned")
|
||||
}
|
||||
checkPkg(t, pkg1, "export")
|
||||
|
||||
// export
|
||||
exportdata := gcimporter.BExportData(fset1, pkg1)
|
||||
|
||||
// import
|
||||
imports := make(map[string]*types.Package)
|
||||
fset2 := token.NewFileSet()
|
||||
_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg1.Path())
|
||||
if err != nil {
|
||||
t.Fatalf("BImportData(%s): %v", pkg1.Path(), err)
|
||||
}
|
||||
checkPkg(t, pkg2, "import")
|
||||
}
|
|
@ -100,10 +100,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
|
|||
|
||||
// read version specific flags - extend as necessary
|
||||
switch p.version {
|
||||
// case 4:
|
||||
// case 5:
|
||||
// ...
|
||||
// fallthrough
|
||||
case 3, 2, 1:
|
||||
case 4, 3, 2, 1:
|
||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||
p.trackAllTypes = p.int() != 0
|
||||
p.posInfoFormat = p.int() != 0
|
||||
|
@ -210,7 +210,6 @@ func (p *importer) pkg() *types.Package {
|
|||
}
|
||||
|
||||
// objTag returns the tag value for each object kind.
|
||||
// obj must not be a *types.Alias.
|
||||
func objTag(obj types.Object) int {
|
||||
switch obj.(type) {
|
||||
case *types.Const:
|
||||
|
@ -221,7 +220,6 @@ func objTag(obj types.Object) int {
|
|||
return varTag
|
||||
case *types.Func:
|
||||
return funcTag
|
||||
// Aliases are not exported multiple times, thus we should not see them here.
|
||||
default:
|
||||
errorf("unexpected object: %v (%T)", obj, obj) // panics
|
||||
panic("unreachable")
|
||||
|
@ -239,14 +237,14 @@ func (p *importer) declare(obj types.Object) {
|
|||
pkg := obj.Pkg()
|
||||
if alt := pkg.Scope().Insert(obj); alt != nil {
|
||||
// This can only trigger if we import a (non-type) object a second time.
|
||||
// Excluding aliases, this cannot happen because 1) we only import a package
|
||||
// Excluding type aliases, this cannot happen because 1) we only import a package
|
||||
// once; and b) we ignore compiler-specific export data which may contain
|
||||
// functions whose inlined function bodies refer to other functions that
|
||||
// were already imported.
|
||||
// However, aliases require reexporting the original object, so we need
|
||||
// However, type aliases require reexporting the original type, so we need
|
||||
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
|
||||
// method importer.obj, switch case importing functions).
|
||||
// Note that the original itself cannot be an alias.
|
||||
// TODO(gri) review/update this comment once the gc compiler handles type aliases.
|
||||
if !sameObj(obj, alt) {
|
||||
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
|
||||
}
|
||||
|
@ -262,6 +260,13 @@ func (p *importer) obj(tag int) {
|
|||
val := p.value()
|
||||
p.declare(types.NewConst(pos, pkg, name, typ, val))
|
||||
|
||||
case aliasTag:
|
||||
// TODO(gri) verify type alias hookup is correct
|
||||
pos := p.pos()
|
||||
pkg, name := p.qualifiedName()
|
||||
typ := p.typ(nil)
|
||||
p.declare(types.NewTypeName(pos, pkg, name, typ))
|
||||
|
||||
case typeTag:
|
||||
p.typ(nil)
|
||||
|
||||
|
@ -279,19 +284,6 @@ func (p *importer) obj(tag int) {
|
|||
sig := types.NewSignature(nil, params, result, isddd)
|
||||
p.declare(types.NewFunc(pos, pkg, name, sig))
|
||||
|
||||
case aliasTag:
|
||||
pos := p.pos()
|
||||
name := p.string()
|
||||
var orig types.Object
|
||||
if pkg, name := p.qualifiedName(); pkg != nil {
|
||||
orig = pkg.Scope().Lookup(name)
|
||||
}
|
||||
// Alias-related code. Keep for now.
|
||||
_ = pos
|
||||
_ = name
|
||||
_ = orig
|
||||
// p.declare(types.NewAlias(pos, p.pkgList[0], name, orig))
|
||||
|
||||
default:
|
||||
errorf("unexpected object tag %d", tag)
|
||||
}
|
||||
|
@ -351,9 +343,7 @@ var (
|
|||
|
||||
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
|
||||
name = p.string()
|
||||
if name != "" {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
pkg = p.pkg()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -558,17 +548,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
|
|||
fields = make([]*types.Var, n)
|
||||
tags = make([]string, n)
|
||||
for i := range fields {
|
||||
fields[i] = p.field(parent)
|
||||
tags[i] = p.string()
|
||||
fields[i], tags[i] = p.field(parent)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *importer) field(parent *types.Package) *types.Var {
|
||||
func (p *importer) field(parent *types.Package) (*types.Var, string) {
|
||||
pos := p.pos()
|
||||
pkg, name := p.fieldName(parent)
|
||||
pkg, name, alias := p.fieldName(parent)
|
||||
typ := p.typ(parent)
|
||||
tag := p.string()
|
||||
|
||||
anonymous := false
|
||||
if name == "" {
|
||||
|
@ -580,12 +570,15 @@ func (p *importer) field(parent *types.Package) *types.Var {
|
|||
case *types.Named:
|
||||
name = typ.Obj().Name()
|
||||
default:
|
||||
errorf("anonymous field expected")
|
||||
errorf("named base type expected")
|
||||
}
|
||||
anonymous = true
|
||||
} else if alias {
|
||||
// anonymous field: we have an explicit name because it's an alias
|
||||
anonymous = true
|
||||
}
|
||||
|
||||
return types.NewField(pos, pkg, name, typ, anonymous)
|
||||
return types.NewField(pos, pkg, name, typ, anonymous), tag
|
||||
}
|
||||
|
||||
func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
|
||||
|
@ -600,45 +593,42 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
|
|||
|
||||
func (p *importer) method(parent *types.Package) *types.Func {
|
||||
pos := p.pos()
|
||||
pkg, name := p.fieldName(parent)
|
||||
pkg, name, _ := p.fieldName(parent)
|
||||
params, isddd := p.paramList()
|
||||
result, _ := p.paramList()
|
||||
sig := types.NewSignature(nil, params, result, isddd)
|
||||
return types.NewFunc(pos, pkg, name, sig)
|
||||
}
|
||||
|
||||
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
|
||||
name := p.string()
|
||||
pkg := parent
|
||||
func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
|
||||
name = p.string()
|
||||
pkg = parent
|
||||
if pkg == nil {
|
||||
// use the imported package instead
|
||||
pkg = p.pkgList[0]
|
||||
}
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ fields
|
||||
// see issue #15514
|
||||
|
||||
// For bug-compatibility with gc, pretend all imported
|
||||
// blank fields belong to the same dummy package.
|
||||
// This avoids spurious "cannot assign A to B" errors
|
||||
// from go/types caused by types changing as they are
|
||||
// re-exported.
|
||||
const blankpkg = "<_>"
|
||||
pkg := p.imports[blankpkg]
|
||||
if pkg == nil {
|
||||
pkg = types.NewPackage(blankpkg, blankpkg)
|
||||
p.imports[blankpkg] = pkg
|
||||
}
|
||||
|
||||
return pkg, name
|
||||
return
|
||||
}
|
||||
if name != "" && !exported(name) {
|
||||
if name == "?" {
|
||||
name = ""
|
||||
}
|
||||
switch name {
|
||||
case "":
|
||||
// 1) field name matches base type name and is exported: nothing to do
|
||||
case "?":
|
||||
// 2) field name matches base type name and is not exported: need package
|
||||
name = ""
|
||||
pkg = p.pkg()
|
||||
case "@":
|
||||
// 3) field name doesn't match type name (alias)
|
||||
name = p.string()
|
||||
alias = true
|
||||
fallthrough
|
||||
default:
|
||||
if !exported(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
}
|
||||
return pkg, name
|
||||
return
|
||||
}
|
||||
|
||||
func (p *importer) paramList() (*types.Tuple, bool) {
|
||||
|
@ -909,7 +899,7 @@ const (
|
|||
nilTag // only used by gc (appears in exported inlined function bodies)
|
||||
unknownTag // not used by gc (only appears in packages with errors)
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
aliasTag
|
||||
)
|
||||
|
||||
|
@ -933,7 +923,7 @@ var predeclared = []types.Type{
|
|||
types.Typ[types.Complex128],
|
||||
types.Typ[types.String],
|
||||
|
||||
// aliases
|
||||
// basic type aliases
|
||||
types.Universe.Lookup("byte").Type(),
|
||||
types.Universe.Lookup("rune").Type(),
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build !go1.9
|
||||
|
||||
package gcimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return false // there are no type aliases before Go 1.9
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package gcimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return obj.IsAlias()
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package gccgoimporter
|
||||
|
||||
var aliasTests = []importerTest{
|
||||
{pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"},
|
||||
}
|
||||
|
||||
func init() {
|
||||
importerTests = append(importerTests, aliasTests...)
|
||||
}
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
package gccgoimporter
|
||||
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/importer_test.go.
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/importer_test.go
|
||||
// except for the importerTests variable which does not contain Go1.9-specific tests.
|
||||
// Those are added via importer19_test.go.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
@ -90,7 +92,7 @@ func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]Init
|
|||
}
|
||||
}
|
||||
|
||||
var importerTests = [...]importerTest{
|
||||
var importerTests = []importerTest{
|
||||
{pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"},
|
||||
{pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1 + -1i)"},
|
||||
{pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1 + 1i)"},
|
||||
|
|
|
@ -372,27 +372,41 @@ func (p *parser) parseConst(pkg *types.Package) *types.Const {
|
|||
return types.NewConst(token.NoPos, pkg, name, typ, val)
|
||||
}
|
||||
|
||||
// TypeName = ExportedName .
|
||||
func (p *parser) parseTypeName() *types.TypeName {
|
||||
pkg, name := p.parseExportedName()
|
||||
scope := pkg.Scope()
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
return obj.(*types.TypeName)
|
||||
}
|
||||
obj := types.NewTypeName(token.NoPos, pkg, name, nil)
|
||||
// a named type may be referred to before the underlying type
|
||||
// is known - set it up
|
||||
types.NewNamed(obj, nil, nil)
|
||||
scope.Insert(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
// NamedType = TypeName Type { Method } .
|
||||
// NamedType = TypeName [ "=" ] Type { Method } .
|
||||
// TypeName = ExportedName .
|
||||
// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
|
||||
func (p *parser) parseNamedType(n int) types.Type {
|
||||
obj := p.parseTypeName()
|
||||
pkg, name := p.parseExportedName()
|
||||
scope := pkg.Scope()
|
||||
|
||||
if p.tok == '=' {
|
||||
// type alias
|
||||
p.next()
|
||||
typ := p.parseType(pkg)
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
typ = obj.Type() // use previously imported type
|
||||
if typ == nil {
|
||||
p.errorf("%v (type alias) used in cycle", obj)
|
||||
}
|
||||
} else {
|
||||
obj = types.NewTypeName(token.NoPos, pkg, name, typ)
|
||||
scope.Insert(obj)
|
||||
}
|
||||
p.typeMap[n] = typ
|
||||
return typ
|
||||
}
|
||||
|
||||
// named type
|
||||
obj := scope.Lookup(name)
|
||||
if obj == nil {
|
||||
// a named type may be referred to before the underlying type
|
||||
// is known - set it up
|
||||
tname := types.NewTypeName(token.NoPos, pkg, name, nil)
|
||||
types.NewNamed(tname, nil, nil)
|
||||
scope.Insert(tname)
|
||||
obj = tname
|
||||
}
|
||||
|
||||
pkg := obj.Pkg()
|
||||
typ := obj.Type()
|
||||
p.typeMap[n] = typ
|
||||
|
||||
|
@ -411,8 +425,8 @@ func (p *parser) parseNamedType(n int) types.Type {
|
|||
nt.SetUnderlying(underlying.Underlying())
|
||||
}
|
||||
|
||||
// collect associated methods
|
||||
for p.tok == scanner.Ident {
|
||||
// collect associated methods
|
||||
p.expectKeyword("func")
|
||||
p.expect('(')
|
||||
receiver, _ := p.parseParam(pkg)
|
||||
|
|
|
@ -233,24 +233,28 @@ func addStructFieldIDAttributes(buf *bytes.Buffer, name string, st *ast.StructTy
|
|||
if st.Fields == nil {
|
||||
return
|
||||
}
|
||||
var scratch bytes.Buffer
|
||||
// needsLink is a set of identifiers that still need to be
|
||||
// linked, where value == key, to avoid an allocation in func
|
||||
// linkedField.
|
||||
needsLink := make(map[string]string)
|
||||
|
||||
for _, f := range st.Fields.List {
|
||||
if len(f.Names) == 0 {
|
||||
continue
|
||||
}
|
||||
fieldName := f.Names[0].Name
|
||||
scratch.Reset()
|
||||
var added bool
|
||||
foreachLine(buf.Bytes(), func(line []byte) {
|
||||
if !added && isLineForStructFieldID(line, fieldName) {
|
||||
added = true
|
||||
fmt.Fprintf(&scratch, `<span id="%s.%s"></span>`, name, fieldName)
|
||||
}
|
||||
scratch.Write(line)
|
||||
})
|
||||
buf.Reset()
|
||||
buf.Write(scratch.Bytes())
|
||||
needsLink[fieldName] = fieldName
|
||||
}
|
||||
var newBuf bytes.Buffer
|
||||
foreachLine(buf.Bytes(), func(line []byte) {
|
||||
if fieldName := linkedField(line, needsLink); fieldName != "" {
|
||||
fmt.Fprintf(&newBuf, `<span id="%s.%s"></span>`, name, fieldName)
|
||||
delete(needsLink, fieldName)
|
||||
}
|
||||
newBuf.Write(line)
|
||||
})
|
||||
buf.Reset()
|
||||
buf.Write(newBuf.Bytes())
|
||||
}
|
||||
|
||||
// foreachLine calls fn for each line of in, where a line includes
|
||||
|
@ -270,9 +274,12 @@ func foreachLine(in []byte, fn func(line []byte)) {
|
|||
// commentPrefix is the line prefix for comments after they've been HTMLified.
|
||||
var commentPrefix = []byte(`<span class="comment">// `)
|
||||
|
||||
// isLineForStructFieldID reports whether line is a line we should
|
||||
// add a <span id="#StructName.FieldName"> to. Only the fieldName is provided.
|
||||
func isLineForStructFieldID(line []byte, fieldName string) bool {
|
||||
// linkedField determines whether the given line starts with an
|
||||
// identifer in the provided ids map (mapping from identifier to the
|
||||
// same identifier). The line can start with either an identifier or
|
||||
// an identifier in a comment. If one matches, it returns the
|
||||
// identifier that matched. Otherwise it returns the empty string.
|
||||
func linkedField(line []byte, ids map[string]string) string {
|
||||
line = bytes.TrimSpace(line)
|
||||
|
||||
// For fields with a doc string of the
|
||||
|
@ -292,13 +299,39 @@ func isLineForStructFieldID(line []byte, fieldName string) bool {
|
|||
//
|
||||
// TODO: do this better, so it works for all
|
||||
// comments, including unconventional ones.
|
||||
// For comments
|
||||
if bytes.HasPrefix(line, commentPrefix) {
|
||||
if matchesIdentBoundary(line[len(commentPrefix):], fieldName) {
|
||||
return true
|
||||
}
|
||||
line = line[len(commentPrefix):]
|
||||
}
|
||||
return matchesIdentBoundary(line, fieldName)
|
||||
id := scanIdentifier(line)
|
||||
if len(id) == 0 {
|
||||
// No leading identifier. Avoid map lookup for
|
||||
// somewhat common case.
|
||||
return ""
|
||||
}
|
||||
return ids[string(id)]
|
||||
}
|
||||
|
||||
// scanIdentifier scans a valid Go identifier off the front of v and
|
||||
// either returns a subslice of v if there's a valid identifier, or
|
||||
// returns a zero-length slice.
|
||||
func scanIdentifier(v []byte) []byte {
|
||||
var n int // number of leading bytes of v belonging to an identifier
|
||||
for {
|
||||
r, width := utf8.DecodeRune(v[n:])
|
||||
if !(isLetter(r) || n > 0 && isDigit(r)) {
|
||||
break
|
||||
}
|
||||
n += width
|
||||
}
|
||||
return v[:n]
|
||||
}
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
|
||||
}
|
||||
|
||||
func isDigit(ch rune) bool {
|
||||
return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
|
||||
}
|
||||
|
||||
// matchesIdentBoundary reports whether line matches /^ident\b/.
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
package godoc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
|
@ -123,10 +125,7 @@ func TestSanitizeFunc(t *testing.T) {
|
|||
// Test that we add <span id="StructName.FieldName"> elements
|
||||
// to the HTML of struct fields.
|
||||
func TestStructFieldsIDAttributes(t *testing.T) {
|
||||
p := &Presentation{
|
||||
DeclLinks: true,
|
||||
}
|
||||
src := []byte(`
|
||||
got := linkifyStructFields(t, []byte(`
|
||||
package foo
|
||||
|
||||
type T struct {
|
||||
|
@ -137,8 +136,32 @@ type T struct {
|
|||
|
||||
// Opt, if non-nil, is an option.
|
||||
Opt *int
|
||||
|
||||
// Опция - другое поле.
|
||||
Опция bool
|
||||
}
|
||||
`)
|
||||
`))
|
||||
want := `type T struct {
|
||||
<span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
|
||||
|
||||
<span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
|
||||
Doc <a href="/pkg/builtin/#string">string</a>
|
||||
|
||||
<span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
|
||||
Opt *<a href="/pkg/builtin/#int">int</a>
|
||||
|
||||
<span id="T.Опция"></span><span class="comment">// Опция - другое поле.</span>
|
||||
Опция <a href="/pkg/builtin/#bool">bool</a>
|
||||
}`
|
||||
if got != want {
|
||||
t.Errorf("got: %s\n\nwant: %s\n", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func linkifyStructFields(t *testing.T, src []byte) string {
|
||||
p := &Presentation{
|
||||
DeclLinks: true,
|
||||
}
|
||||
fset := token.NewFileSet()
|
||||
af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
|
||||
if err != nil {
|
||||
|
@ -148,17 +171,46 @@ type T struct {
|
|||
pi := &PageInfo{
|
||||
FSet: fset,
|
||||
}
|
||||
got := p.node_htmlFunc(pi, genDecl, true)
|
||||
want := `type T struct {
|
||||
<span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
|
||||
return p.node_htmlFunc(pi, genDecl, true)
|
||||
}
|
||||
|
||||
<span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
|
||||
Doc <a href="/pkg/builtin/#string">string</a>
|
||||
|
||||
<span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
|
||||
Opt *<a href="/pkg/builtin/#int">int</a>
|
||||
}`
|
||||
if got != want {
|
||||
t.Errorf("got: %s\n\nwant: %s\n", got, want)
|
||||
// Verify that scanIdentifier isn't quadratic.
|
||||
// This doesn't actually measure and fail on its own, but it was previously
|
||||
// very obvious when running by hand.
|
||||
//
|
||||
// TODO: if there's a reliable and non-flaky way to test this, do so.
|
||||
// Maybe count user CPU time instead of wall time? But that's not easy
|
||||
// to do portably in Go.
|
||||
func TestStructField(t *testing.T) {
|
||||
for _, n := range []int{10, 100, 1000, 10000} {
|
||||
n := n
|
||||
t.Run(fmt.Sprint(n), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "package foo\n\ntype T struct {\n")
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Fprintf(&buf, "\t// Field%d is foo.\n\tField%d int\n\n", i, i)
|
||||
}
|
||||
fmt.Fprintf(&buf, "}\n")
|
||||
linkifyStructFields(t, buf.Bytes())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanIdentifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"foo bar", "foo"},
|
||||
{"foo/bar", "foo"},
|
||||
{" foo", ""},
|
||||
{"фоо", "фоо"},
|
||||
{"f123", "f123"},
|
||||
{"123f", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := scanIdentifier([]byte(tt.in))
|
||||
if string(got) != tt.want {
|
||||
t.Errorf("scanIdentifier(%q) = %q; want %q", tt.in, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ function fixFocus() {
|
|||
function toggleHash() {
|
||||
// Open all of the toggles for a particular hash.
|
||||
var els = $(document.getElementById(window.location.hash.substring(1)),
|
||||
$("a[name='" + window.location.hash.substring(1) + "']"));
|
||||
$.find("a[name='" + window.location.hash.substring(1) + "']"));
|
||||
while (els.length) {
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
var el = $(els[i]);
|
||||
|
@ -263,7 +263,14 @@ function personalizeInstallInstructions() {
|
|||
var prefix = '?download=';
|
||||
var s = window.location.search;
|
||||
if (s.indexOf(prefix) != 0) {
|
||||
// No 'download' query string; bail.
|
||||
// No 'download' query string; detect "test" instructions from User Agent.
|
||||
if (navigator.platform.indexOf('Win') != -1) {
|
||||
$('.testUnix').hide();
|
||||
$('.testWindows').show();
|
||||
} else {
|
||||
$('.testUnix').show();
|
||||
$('.testWindows').hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -833,7 +833,14 @@ function personalizeInstallInstructions() {
|
|||
var prefix = '?download=';
|
||||
var s = window.location.search;
|
||||
if (s.indexOf(prefix) != 0) {
|
||||
// No 'download' query string; bail.
|
||||
// No 'download' query string; detect "test" instructions from User Agent.
|
||||
if (navigator.platform.indexOf('Win') != -1) {
|
||||
$('.testUnix').hide();
|
||||
$('.testWindows').show();
|
||||
} else {
|
||||
$('.testUnix').show();
|
||||
$('.testWindows').hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -276,7 +276,7 @@ func addImportSpaces(r io.Reader, breaks []string) []byte {
|
|||
}
|
||||
if inImports && len(breaks) > 0 {
|
||||
if m := impLine.FindStringSubmatch(s); m != nil {
|
||||
if m[1] == string(breaks[0]) {
|
||||
if m[1] == breaks[0] {
|
||||
out.WriteByte('\n')
|
||||
breaks = breaks[1:]
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ func parseIframe(ctx *Context, fileName string, lineno int, text string) (Elem,
|
|||
i.Width = v
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("incorrect image invocation: %q", text)
|
||||
return nil, fmt.Errorf("incorrect iframe invocation: %q", text)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue