Merge "[release-branch.go1.8] all: merge master into release-branch.go1.8" into release-branch.go1.8
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/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/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:]
 				}