[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 linkification
721f2184 imports: remove unnecessary string conversion
fcfba28e go/internal/gccgoimporter: support for type aliases
add1aac0 static: don't use the jQuery func for looking up based on hash
608c3b09 present: fix typo
0d047c8d cmd/godex: handle printing of type aliases
de557280 go/gcimporter15: update import/export to handle type aliases
5d76f8ce static: detect platform to hide/show unix/windows instructions

Change-Id: If3753b030500781b5bf086e56485e75343744799
diff --git a/cmd/godex/gc.go b/cmd/godex/gc.go
index 66b0a0e..95eba65 100644
--- a/cmd/godex/gc.go
+++ b/cmd/godex/gc.go
@@ -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
diff --git a/cmd/godex/gccgo.go b/cmd/godex/gccgo.go
index 785441c..7644998 100644
--- a/cmd/godex/gccgo.go
+++ b/cmd/godex/gccgo.go
@@ -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
diff --git a/cmd/godex/godex.go b/cmd/godex/godex.go
index 5d40d87..a222ed63 100644
--- a/cmd/godex/godex.go
+++ b/cmd/godex/godex.go
@@ -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 (
diff --git a/cmd/godex/isAlias18.go b/cmd/godex/isAlias18.go
new file mode 100644
index 0000000..cab1292
--- /dev/null
+++ b/cmd/godex/isAlias18.go
@@ -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
+}
diff --git a/cmd/godex/isAlias19.go b/cmd/godex/isAlias19.go
new file mode 100644
index 0000000..6ebdd42
--- /dev/null
+++ b/cmd/godex/isAlias19.go
@@ -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()
+}
diff --git a/cmd/godex/print.go b/cmd/godex/print.go
index 02b4606..adce864 100644
--- a/cmd/godex/print.go
+++ b/cmd/godex/print.go
@@ -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 @@
 	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")
 		}
 	})
diff --git a/cmd/godex/source.go b/cmd/godex/source.go
index 0d3c006..85235e9 100644
--- a/cmd/godex/source.go
+++ b/cmd/godex/source.go
@@ -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
diff --git a/cmd/godex/writetype.go b/cmd/godex/writetype.go
index ed0bd9f..dd17f90 100644
--- a/cmd/godex/writetype.go
+++ b/cmd/godex/writetype.go
@@ -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.
diff --git a/cmd/guru/pos.go b/cmd/guru/pos.go
index 591aad8..2e659fe42 100644
--- a/cmd/guru/pos.go
+++ b/cmd/guru/pos.go
@@ -52,14 +52,14 @@
 	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)
diff --git a/go/gcimporter15/bexport.go b/go/gcimporter15/bexport.go
index af982ab..9f5db0e 100644
--- a/go/gcimporter15/bexport.go
+++ b/go/gcimporter15/bexport.go
@@ -39,11 +39,12 @@
 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 @@
 	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 @@
 		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 @@
 		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 @@
 		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 (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 @@
 	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 @@
 // 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 @@
 	-mapTag:       "map",
 	-chanTag:      "chan",
 
-	// Values:
+	// Values
 	-falseTag:    "false",
 	-trueTag:     "true",
 	-int64Tag:    "int64",
@@ -821,4 +822,7 @@
 	-complexTag:  "complex",
 	-stringTag:   "string",
 	-unknownTag:  "unknown",
+
+	// Type aliases
+	-aliasTag: "alias",
 }
diff --git a/go/gcimporter15/bexport18_test.go b/go/gcimporter15/bexport18_test.go
deleted file mode 100644
index d0decdd..0000000
--- a/go/gcimporter15/bexport18_test.go
+++ /dev/null
@@ -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())
-	}
-}
diff --git a/go/gcimporter15/bexport19_test.go b/go/gcimporter15/bexport19_test.go
new file mode 100644
index 0000000..f6e9733
--- /dev/null
+++ b/go/gcimporter15/bexport19_test.go
@@ -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")
+}
diff --git a/go/gcimporter15/bimport.go b/go/gcimporter15/bimport.go
index ce4b687..8bff64c 100644
--- a/go/gcimporter15/bimport.go
+++ b/go/gcimporter15/bimport.go
@@ -100,10 +100,10 @@
 
 	// 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 @@
 }
 
 // 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 @@
 		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 @@
 	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 @@
 		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 @@
 		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 @@
 
 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 @@
 		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 @@
 		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) 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 @@
 	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 @@
 	types.Typ[types.Complex128],
 	types.Typ[types.String],
 
-	// aliases
+	// basic type aliases
 	types.Universe.Lookup("byte").Type(),
 	types.Universe.Lookup("rune").Type(),
 
diff --git a/go/gcimporter15/isAlias18.go b/go/gcimporter15/isAlias18.go
new file mode 100644
index 0000000..225ffee
--- /dev/null
+++ b/go/gcimporter15/isAlias18.go
@@ -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
+}
diff --git a/go/gcimporter15/isAlias19.go b/go/gcimporter15/isAlias19.go
new file mode 100644
index 0000000..c2025d8
--- /dev/null
+++ b/go/gcimporter15/isAlias19.go
@@ -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()
+}
diff --git a/go/internal/gccgoimporter/importer19_test.go b/go/internal/gccgoimporter/importer19_test.go
new file mode 100644
index 0000000..0caa225
--- /dev/null
+++ b/go/internal/gccgoimporter/importer19_test.go
@@ -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...)
+}
diff --git a/go/internal/gccgoimporter/importer_test.go b/go/internal/gccgoimporter/importer_test.go
index 2749409..87b5ff2 100644
--- a/go/internal/gccgoimporter/importer_test.go
+++ b/go/internal/gccgoimporter/importer_test.go
@@ -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 @@
 	}
 }
 
-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)"},
diff --git a/go/internal/gccgoimporter/parser.go b/go/internal/gccgoimporter/parser.go
index ef59b29..7d0e39c 100644
--- a/go/internal/gccgoimporter/parser.go
+++ b/go/internal/gccgoimporter/parser.go
@@ -372,27 +372,41 @@
 	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()
 
-	pkg := obj.Pkg()
+	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
+	}
+
 	typ := obj.Type()
 	p.typeMap[n] = typ
 
@@ -411,8 +425,8 @@
 		nt.SetUnderlying(underlying.Underlying())
 	}
 
+	// collect associated methods
 	for p.tok == scanner.Ident {
-		// collect associated methods
 		p.expectKeyword("func")
 		p.expect('(')
 		receiver, _ := p.parseParam(pkg)
diff --git a/godoc/godoc.go b/godoc/godoc.go
index 8bda89a..1063244 100644
--- a/godoc/godoc.go
+++ b/godoc/godoc.go
@@ -233,24 +233,28 @@
 	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 @@
 // 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 @@
 	//
 	// 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/.
diff --git a/godoc/godoc_test.go b/godoc/godoc_test.go
index 51d41b3..011eca3 100644
--- a/godoc/godoc_test.go
+++ b/godoc/godoc_test.go
@@ -5,6 +5,8 @@
 package godoc
 
 import (
+	"bytes"
+	"fmt"
 	"go/ast"
 	"go/parser"
 	"go/token"
@@ -123,10 +125,7 @@
 // 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 @@
 
 	// 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 @@
 	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>
+// 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())
+		})
+	}
+}
 
-<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)
+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)
+		}
 	}
 }
diff --git a/godoc/static/godocs.js b/godoc/static/godocs.js
index 1fb8b91..23a580e 100644
--- a/godoc/static/godocs.js
+++ b/godoc/static/godocs.js
@@ -247,7 +247,7 @@
 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 @@
   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;
   }
 
diff --git a/godoc/static/static.go b/godoc/static/static.go
index a47a7fd..6fab8da 100644
--- a/godoc/static/static.go
+++ b/godoc/static/static.go
@@ -833,7 +833,14 @@
   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;
   }
 
diff --git a/imports/imports.go b/imports/imports.go
index c26c194..67573f4 100644
--- a/imports/imports.go
+++ b/imports/imports.go
@@ -276,7 +276,7 @@
 		}
 		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:]
 				}
diff --git a/present/iframe.go b/present/iframe.go
index 2f3c5e5..46649f0 100644
--- a/present/iframe.go
+++ b/present/iframe.go
@@ -39,7 +39,7 @@
 			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
 }