[gopls-release-branch.0.6] all: merge master into gopls-release-branch.0.6

0e232fa9 gopls: add scheme to CodeDescription.href
2363391a all: go fmt ./...
b3696403 all: fmt tests with new gofmt
f6e04434 go/analysis: add unusedwrite pass
bdaa8bfb go/gcexportdata: warn that {Read,Write}Bundle are experimental
f4301d9e internal/imports: update stdlib index for 1.16
f3748ed8 internal/lsp/source: filter out comparable from completion results
f47cb783 go/analysis/passes/buildtag: update check for //go:build
06713c25 go/loader: fix race
1f00549a internal/regtest: fix regtests for the dev.typeparams Go branch
4b197900 txtar: minor fix in unit test input
9eb35354 internal/lsp: 'go get' packages instead of modules
67e49ef2 go/internal/cgo: set pkgdir with -srcdir instead of CWD
19ff21fb go/analysis/unitchecker: tell the user how to list the flags and analyzers
d5b83329 internal/lsp/command: rename package generate to gen
4534fc34 go/internal/gcimporter: reference golang/go#44339 in TODO
35839b70 go/internal/gcimporter: fix tests on darwin
a1db63cc go/internal/gcimporter: add "bundled" export data formats
b79f76fe go/internal/gcimporter: fix reexporting compiler data
7fde01ff go/internal/gcimporter: refactor IExportData tests
6055ccf0 go/internal/gcimporter: simplify IExportData API
1e7abacf internal/lsp: refactor go command error handling
ffc20750 internal/lsp: fix nil pointer in hover when (types.Object).Pkg() is nil
fca89925 internal/lsp: handle nil pointer with import shortcut = link
5848b84f internal/typesinternal: sync error codes with go1.16
3a5a0519 go/analysis/passes: add sigchanyzer Analyzer

Change-Id: I1d4a41669e2f8e8115fb5d62cc25a62f7c755ba2
diff --git a/cmd/bundle/main.go b/cmd/bundle/main.go
index 8e5ad2c..fd8b0e5 100644
--- a/cmd/bundle/main.go
+++ b/cmd/bundle/main.go
@@ -228,6 +228,7 @@
 
 	var out bytes.Buffer
 	if buildTags != "" {
+		fmt.Fprintf(&out, "//go:build %s\n", buildTags)
 		fmt.Fprintf(&out, "// +build %s\n\n", buildTags)
 	}
 
diff --git a/cmd/bundle/testdata/out.golden b/cmd/bundle/testdata/out.golden
index ed18e3d..a8f0cfe 100644
--- a/cmd/bundle/testdata/out.golden
+++ b/cmd/bundle/testdata/out.golden
@@ -1,3 +1,4 @@
+//go:build tag
 // +build tag
 
 // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
diff --git a/cmd/callgraph/main_test.go b/cmd/callgraph/main_test.go
index 6aeae6f..f486def 100644
--- a/cmd/callgraph/main_test.go
+++ b/cmd/callgraph/main_test.go
@@ -4,8 +4,8 @@
 
 // No testdata on Android.
 
-// +build !android
-// +build go1.11
+//go:build !android && go1.11
+// +build !android,go1.11
 
 package main
 
diff --git a/cmd/cover/cover_test.go b/cmd/cover/cover_test.go
index 54d3465..228c911 100644
--- a/cmd/cover/cover_test.go
+++ b/cmd/cover/cover_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package main_test
diff --git a/cmd/fiximports/main_test.go b/cmd/fiximports/main_test.go
index 2e011d3..9d2c94c 100644
--- a/cmd/fiximports/main_test.go
+++ b/cmd/fiximports/main_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package main
diff --git a/cmd/getgo/download.go b/cmd/getgo/download.go
index 383cb3d..1731131 100644
--- a/cmd/getgo/download.go
+++ b/cmd/getgo/download.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 package main
diff --git a/cmd/getgo/download_test.go b/cmd/getgo/download_test.go
index 5a5cc29..76cd96c 100644
--- a/cmd/getgo/download_test.go
+++ b/cmd/getgo/download_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 package main
diff --git a/cmd/getgo/main.go b/cmd/getgo/main.go
index 417e860..441fd89 100644
--- a/cmd/getgo/main.go
+++ b/cmd/getgo/main.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 // The getgo command installs Go to the user's system.
diff --git a/cmd/getgo/main_test.go b/cmd/getgo/main_test.go
index e430895..0c0e8b9 100644
--- a/cmd/getgo/main_test.go
+++ b/cmd/getgo/main_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 package main
diff --git a/cmd/getgo/path.go b/cmd/getgo/path.go
index 52e0462..f1799a8 100644
--- a/cmd/getgo/path.go
+++ b/cmd/getgo/path.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 package main
diff --git a/cmd/getgo/path_test.go b/cmd/getgo/path_test.go
index 5355c6e..2249c54 100644
--- a/cmd/getgo/path_test.go
+++ b/cmd/getgo/path_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 package main
diff --git a/cmd/getgo/steps.go b/cmd/getgo/steps.go
index e505f5a..fe69aa6 100644
--- a/cmd/getgo/steps.go
+++ b/cmd/getgo/steps.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 package main
diff --git a/cmd/getgo/system.go b/cmd/getgo/system.go
index 232ca36..3449c9c 100644
--- a/cmd/getgo/system.go
+++ b/cmd/getgo/system.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 package main
diff --git a/cmd/getgo/system_unix.go b/cmd/getgo/system_unix.go
index adc3e55..09606f8 100644
--- a/cmd/getgo/system_unix.go
+++ b/cmd/getgo/system_unix.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build aix || darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
 // +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package main
diff --git a/cmd/getgo/system_windows.go b/cmd/getgo/system_windows.go
index d8a6191..5b1e247 100644
--- a/cmd/getgo/system_windows.go
+++ b/cmd/getgo/system_windows.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build windows
 // +build windows
 
 package main
diff --git a/cmd/godex/isAlias18.go b/cmd/godex/isAlias18.go
index cab1292..431602b 100644
--- a/cmd/godex/isAlias18.go
+++ b/cmd/godex/isAlias18.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.9
 // +build !go1.9
 
 package main
diff --git a/cmd/godex/isAlias19.go b/cmd/godex/isAlias19.go
index 6ebdd42..e588911 100644
--- a/cmd/godex/isAlias19.go
+++ b/cmd/godex/isAlias19.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.9
 // +build go1.9
 
 package main
diff --git a/cmd/goimports/goimports_gc.go b/cmd/goimports/goimports_gc.go
index 21d867e..190a565 100644
--- a/cmd/goimports/goimports_gc.go
+++ b/cmd/goimports/goimports_gc.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build gc
 // +build gc
 
 package main
diff --git a/cmd/goimports/goimports_not_gc.go b/cmd/goimports/goimports_not_gc.go
index f5531ce..344fe75 100644
--- a/cmd/goimports/goimports_not_gc.go
+++ b/cmd/goimports/goimports_not_gc.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !gc
 // +build !gc
 
 package main
diff --git a/cmd/gotype/sizesFor18.go b/cmd/gotype/sizesFor18.go
index 94e8176..39e3d9f 100644
--- a/cmd/gotype/sizesFor18.go
+++ b/cmd/gotype/sizesFor18.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.9
 // +build !go1.9
 
 // This file contains a copy of the implementation of types.SizesFor
diff --git a/cmd/gotype/sizesFor19.go b/cmd/gotype/sizesFor19.go
index 9e0b481..34181c8 100644
--- a/cmd/gotype/sizesFor19.go
+++ b/cmd/gotype/sizesFor19.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.9
 // +build go1.9
 
 package main
diff --git a/cmd/guru/isAlias18.go b/cmd/guru/isAlias18.go
index 6a2a4c0..6d91017 100644
--- a/cmd/guru/isAlias18.go
+++ b/cmd/guru/isAlias18.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.9
 // +build !go1.9
 
 package main
diff --git a/cmd/guru/isAlias19.go b/cmd/guru/isAlias19.go
index 25096ab..4d63679 100644
--- a/cmd/guru/isAlias19.go
+++ b/cmd/guru/isAlias19.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.9
 // +build go1.9
 
 package main
diff --git a/cmd/splitdwarf/splitdwarf.go b/cmd/splitdwarf/splitdwarf.go
index 44e7a7a..a13b9f3 100644
--- a/cmd/splitdwarf/splitdwarf.go
+++ b/cmd/splitdwarf/splitdwarf.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !js && !nacl && !plan9 && !solaris && !windows
 // +build !js,!nacl,!plan9,!solaris,!windows
 
 /*
diff --git a/cmd/stress/stress.go b/cmd/stress/stress.go
index 4ff6cf3..9ba6ef3 100644
--- a/cmd/stress/stress.go
+++ b/cmd/stress/stress.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !plan9
 // +build !plan9
 
 // The stress utility is intended for catching sporadic failures.
diff --git a/cmd/stringer/endtoend_test.go b/cmd/stringer/endtoend_test.go
index af106b5..3b0b39d 100644
--- a/cmd/stringer/endtoend_test.go
+++ b/cmd/stringer/endtoend_test.go
@@ -4,6 +4,7 @@
 
 // go command is not available on android
 
+//go:build !android
 // +build !android
 
 package main
diff --git a/container/intsets/popcnt_amd64.go b/container/intsets/popcnt_amd64.go
index 99ea813..25c02f4 100644
--- a/container/intsets/popcnt_amd64.go
+++ b/container/intsets/popcnt_amd64.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build amd64 && !appengine && !gccgo
 // +build amd64,!appengine,!gccgo
 
 package intsets
diff --git a/container/intsets/popcnt_gccgo.go b/container/intsets/popcnt_gccgo.go
index 3fc5e85..5e1efcf 100644
--- a/container/intsets/popcnt_gccgo.go
+++ b/container/intsets/popcnt_gccgo.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build gccgo
 // +build gccgo
 
 package intsets
diff --git a/container/intsets/popcnt_generic.go b/container/intsets/popcnt_generic.go
index 3985a1d..caffedc 100644
--- a/container/intsets/popcnt_generic.go
+++ b/container/intsets/popcnt_generic.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build (!amd64 || appengine) && !gccgo
 // +build !amd64 appengine
 // +build !gccgo
 
diff --git a/go/analysis/analysistest/analysistest.go b/go/analysis/analysistest/analysistest.go
index 5e99afe..8447244 100644
--- a/go/analysis/analysistest/analysistest.go
+++ b/go/analysis/analysistest/analysistest.go
@@ -354,13 +354,13 @@
 		// Any comment starting with "want" is treated
 		// as an expectation, even without following whitespace.
 		if rest := strings.TrimPrefix(text, "want"); rest != text {
-			expects, err := parseExpectations(rest)
+			lineDelta, expects, err := parseExpectations(rest)
 			if err != nil {
 				t.Errorf("%s:%d: in 'want' comment: %s", filename, linenum, err)
 				return
 			}
 			if expects != nil {
-				want[key{filename, linenum}] = expects
+				want[key{filename, linenum + lineDelta}] = expects
 			}
 		}
 	}
@@ -526,13 +526,13 @@
 // parseExpectations parses the content of a "// want ..." comment
 // and returns the expectations, a mixture of diagnostics ("rx") and
 // facts (name:"rx").
-func parseExpectations(text string) ([]expectation, error) {
+func parseExpectations(text string) (lineDelta int, expects []expectation, err error) {
 	var scanErr string
 	sc := new(scanner.Scanner).Init(strings.NewReader(text))
 	sc.Error = func(s *scanner.Scanner, msg string) {
 		scanErr = msg // e.g. bad string escape
 	}
-	sc.Mode = scanner.ScanIdents | scanner.ScanStrings | scanner.ScanRawStrings
+	sc.Mode = scanner.ScanIdents | scanner.ScanStrings | scanner.ScanRawStrings | scanner.ScanInts
 
 	scanRegexp := func(tok rune) (*regexp.Regexp, error) {
 		if tok != scanner.String && tok != scanner.RawString {
@@ -543,14 +543,19 @@
 		return regexp.Compile(pattern)
 	}
 
-	var expects []expectation
 	for {
 		tok := sc.Scan()
 		switch tok {
+		case '+':
+			tok = sc.Scan()
+			if tok != scanner.Int {
+				return 0, nil, fmt.Errorf("got +%s, want +Int", scanner.TokenString(tok))
+			}
+			lineDelta, _ = strconv.Atoi(sc.TokenText())
 		case scanner.String, scanner.RawString:
 			rx, err := scanRegexp(tok)
 			if err != nil {
-				return nil, err
+				return 0, nil, err
 			}
 			expects = append(expects, expectation{"diagnostic", "", rx})
 
@@ -558,24 +563,24 @@
 			name := sc.TokenText()
 			tok = sc.Scan()
 			if tok != ':' {
-				return nil, fmt.Errorf("got %s after %s, want ':'",
+				return 0, nil, fmt.Errorf("got %s after %s, want ':'",
 					scanner.TokenString(tok), name)
 			}
 			tok = sc.Scan()
 			rx, err := scanRegexp(tok)
 			if err != nil {
-				return nil, err
+				return 0, nil, err
 			}
 			expects = append(expects, expectation{"fact", name, rx})
 
 		case scanner.EOF:
 			if scanErr != "" {
-				return nil, fmt.Errorf("%s", scanErr)
+				return 0, nil, fmt.Errorf("%s", scanErr)
 			}
-			return expects, nil
+			return lineDelta, expects, nil
 
 		default:
-			return nil, fmt.Errorf("unexpected %s", scanner.TokenString(tok))
+			return 0, nil, fmt.Errorf("unexpected %s", scanner.TokenString(tok))
 		}
 	}
 }
diff --git a/go/analysis/multichecker/multichecker_test.go b/go/analysis/multichecker/multichecker_test.go
index c7a2c46..07bf977 100644
--- a/go/analysis/multichecker/multichecker_test.go
+++ b/go/analysis/multichecker/multichecker_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.12
 // +build go1.12
 
 package multichecker_test
diff --git a/go/analysis/passes/buildtag/buildtag.go b/go/analysis/passes/buildtag/buildtag.go
index 841b928..c4407ad 100644
--- a/go/analysis/passes/buildtag/buildtag.go
+++ b/go/analysis/passes/buildtag/buildtag.go
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.16
+// +build go1.16
+
 // Package buildtag defines an Analyzer that checks build tags.
 package buildtag
 
 import (
-	"bytes"
-	"fmt"
 	"go/ast"
+	"go/build/constraint"
 	"go/parser"
+	"go/token"
 	"strings"
 	"unicode"
 
@@ -52,118 +55,313 @@
 }
 
 func checkGoFile(pass *analysis.Pass, f *ast.File) {
-	pastCutoff := false
+	var check checker
+	check.init(pass)
+	defer check.finish()
+
 	for _, group := range f.Comments {
 		// A +build comment is ignored after or adjoining the package declaration.
 		if group.End()+1 >= f.Package {
-			pastCutoff = true
+			check.plusBuildOK = false
 		}
-
-		// "+build" is ignored within or after a /*...*/ comment.
-		if !strings.HasPrefix(group.List[0].Text, "//") {
-			pastCutoff = true
-			continue
+		// A //go:build comment is ignored after the package declaration
+		// (but adjoining it is OK, in contrast to +build comments).
+		if group.Pos() >= f.Package {
+			check.goBuildOK = false
 		}
 
 		// Check each line of a //-comment.
 		for _, c := range group.List {
-			if !strings.Contains(c.Text, "+build") {
-				continue
+			// "+build" is ignored within or after a /*...*/ comment.
+			if !strings.HasPrefix(c.Text, "//") {
+				check.plusBuildOK = false
 			}
-			if err := checkLine(c.Text, pastCutoff); err != nil {
-				pass.Reportf(c.Pos(), "%s", err)
-			}
+			check.comment(c.Slash, c.Text)
 		}
 	}
 }
 
 func checkOtherFile(pass *analysis.Pass, filename string) error {
+	var check checker
+	check.init(pass)
+	defer check.finish()
+
+	// We cannot use the Go parser, since this may not be a Go source file.
+	// Read the raw bytes instead.
 	content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
 	if err != nil {
 		return err
 	}
 
-	// We must look at the raw lines, as build tags may appear in non-Go
-	// files such as assembly files.
-	lines := bytes.SplitAfter(content, nl)
+	check.file(token.Pos(tf.Base()), string(content))
+	return nil
+}
 
+type checker struct {
+	pass         *analysis.Pass
+	plusBuildOK  bool            // "+build" lines still OK
+	goBuildOK    bool            // "go:build" lines still OK
+	crossCheck   bool            // cross-check go:build and +build lines when done reading file
+	inStar       bool            // currently in a /* */ comment
+	goBuildPos   token.Pos       // position of first go:build line found
+	plusBuildPos token.Pos       // position of first "+build" line found
+	goBuild      constraint.Expr // go:build constraint found
+	plusBuild    constraint.Expr // AND of +build constraints found
+}
+
+func (check *checker) init(pass *analysis.Pass) {
+	check.pass = pass
+	check.goBuildOK = true
+	check.plusBuildOK = true
+	check.crossCheck = true
+}
+
+func (check *checker) file(pos token.Pos, text string) {
 	// Determine cutpoint where +build comments are no longer valid.
 	// They are valid in leading // comments in the file followed by
 	// a blank line.
 	//
 	// This must be done as a separate pass because of the
 	// requirement that the comment be followed by a blank line.
-	var cutoff int
-	for i, line := range lines {
-		line = bytes.TrimSpace(line)
-		if !bytes.HasPrefix(line, slashSlash) {
-			if len(line) > 0 {
-				break
+	var plusBuildCutoff int
+	fullText := text
+	for text != "" {
+		i := strings.Index(text, "\n")
+		if i < 0 {
+			i = len(text)
+		} else {
+			i++
+		}
+		offset := len(fullText) - len(text)
+		line := text[:i]
+		text = text[i:]
+		line = strings.TrimSpace(line)
+		if !strings.HasPrefix(line, "//") && line != "" {
+			break
+		}
+		if line == "" {
+			plusBuildCutoff = offset
+		}
+	}
+
+	// Process each line.
+	// Must stop once we hit goBuildOK == false
+	text = fullText
+	check.inStar = false
+	for text != "" {
+		i := strings.Index(text, "\n")
+		if i < 0 {
+			i = len(text)
+		} else {
+			i++
+		}
+		offset := len(fullText) - len(text)
+		line := text[:i]
+		text = text[i:]
+		check.plusBuildOK = offset < plusBuildCutoff
+
+		if strings.HasPrefix(line, "//") {
+			check.comment(pos+token.Pos(offset), line)
+			continue
+		}
+
+		// Keep looking for the point at which //go:build comments
+		// stop being allowed. Skip over, cut out any /* */ comments.
+		for {
+			line = strings.TrimSpace(line)
+			if check.inStar {
+				i := strings.Index(line, "*/")
+				if i < 0 {
+					line = ""
+					break
+				}
+				line = line[i+len("*/"):]
+				check.inStar = false
+				continue
 			}
-			cutoff = i
+			if strings.HasPrefix(line, "/*") {
+				check.inStar = true
+				line = line[len("/*"):]
+				continue
+			}
+			break
+		}
+		if line != "" {
+			// Found non-comment non-blank line.
+			// Ends space for valid //go:build comments,
+			// but also ends the fraction of the file we can
+			// reliably parse. From this point on we might
+			// incorrectly flag "comments" inside multiline
+			// string constants or anything else (this might
+			// not even be a Go program). So stop.
+			break
 		}
 	}
-
-	for i, line := range lines {
-		line = bytes.TrimSpace(line)
-		if !bytes.HasPrefix(line, slashSlash) {
-			continue
-		}
-		if !bytes.Contains(line, []byte("+build")) {
-			continue
-		}
-		if err := checkLine(string(line), i >= cutoff); err != nil {
-			pass.Reportf(analysisutil.LineStart(tf, i+1), "%s", err)
-			continue
-		}
-	}
-	return nil
 }
 
-// checkLine checks a line that starts with "//" and contains "+build".
-func checkLine(line string, pastCutoff bool) error {
-	line = strings.TrimPrefix(line, "//")
-	line = strings.TrimSpace(line)
+func (check *checker) comment(pos token.Pos, text string) {
+	if strings.HasPrefix(text, "//") {
+		if strings.Contains(text, "+build") {
+			check.plusBuildLine(pos, text)
+		}
+		if strings.Contains(text, "//go:build") {
+			check.goBuildLine(pos, text)
+		}
+	}
+	if strings.HasPrefix(text, "/*") {
+		if i := strings.Index(text, "\n"); i >= 0 {
+			// multiline /* */ comment - process interior lines
+			check.inStar = true
+			i++
+			pos += token.Pos(i)
+			text = text[i:]
+			for text != "" {
+				i := strings.Index(text, "\n")
+				if i < 0 {
+					i = len(text)
+				} else {
+					i++
+				}
+				line := text[:i]
+				if strings.HasPrefix(line, "//") {
+					check.comment(pos, line)
+				}
+				pos += token.Pos(i)
+				text = text[i:]
+			}
+			check.inStar = false
+		}
+	}
+}
 
-	if strings.HasPrefix(line, "+build") {
-		fields := strings.Fields(line)
-		if fields[0] != "+build" {
-			// Comment is something like +buildasdf not +build.
-			return fmt.Errorf("possible malformed +build comment")
+func (check *checker) goBuildLine(pos token.Pos, line string) {
+	if !constraint.IsGoBuild(line) {
+		if !strings.HasPrefix(line, "//go:build") && constraint.IsGoBuild("//"+strings.TrimSpace(line[len("//"):])) {
+			check.pass.Reportf(pos, "malformed //go:build line (space between // and go:build)")
 		}
-		if pastCutoff {
-			return fmt.Errorf("+build comment must appear before package clause and be followed by a blank line")
-		}
-		if err := checkArguments(fields); err != nil {
-			return err
-		}
+		return
+	}
+	if !check.goBuildOK || check.inStar {
+		check.pass.Reportf(pos, "misplaced //go:build comment")
+		check.crossCheck = false
+		return
+	}
+
+	if check.goBuildPos == token.NoPos {
+		check.goBuildPos = pos
 	} else {
-		// Comment with +build but not at beginning.
-		if !pastCutoff {
-			return fmt.Errorf("possible malformed +build comment")
-		}
+		check.pass.Reportf(pos, "unexpected extra //go:build line")
+		check.crossCheck = false
 	}
-	return nil
+
+	// testing hack: stop at // ERROR
+	if i := strings.Index(line, " // ERROR "); i >= 0 {
+		line = line[:i]
+	}
+
+	x, err := constraint.Parse(line)
+	if err != nil {
+		check.pass.Reportf(pos, "%v", err)
+		check.crossCheck = false
+		return
+	}
+
+	if check.goBuild == nil {
+		check.goBuild = x
+	}
 }
 
-func checkArguments(fields []string) error {
+func (check *checker) plusBuildLine(pos token.Pos, line string) {
+	line = strings.TrimSpace(line)
+	if !constraint.IsPlusBuild(line) {
+		// Comment with +build but not at beginning.
+		// Only report early in file.
+		if check.plusBuildOK && !strings.HasPrefix(line, "// want") {
+			check.pass.Reportf(pos, "possible malformed +build comment")
+		}
+		return
+	}
+	if !check.plusBuildOK { // inStar implies !plusBuildOK
+		check.pass.Reportf(pos, "misplaced +build comment")
+		check.crossCheck = false
+	}
+
+	if check.plusBuildPos == token.NoPos {
+		check.plusBuildPos = pos
+	}
+
+	// testing hack: stop at // ERROR
+	if i := strings.Index(line, " // ERROR "); i >= 0 {
+		line = line[:i]
+	}
+
+	fields := strings.Fields(line[len("//"):])
+	// IsPlusBuildConstraint check above implies fields[0] == "+build"
 	for _, arg := range fields[1:] {
 		for _, elem := range strings.Split(arg, ",") {
 			if strings.HasPrefix(elem, "!!") {
-				return fmt.Errorf("invalid double negative in build constraint: %s", arg)
+				check.pass.Reportf(pos, "invalid double negative in build constraint: %s", arg)
+				check.crossCheck = false
+				continue
 			}
 			elem = strings.TrimPrefix(elem, "!")
 			for _, c := range elem {
 				if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
-					return fmt.Errorf("invalid non-alphanumeric build constraint: %s", arg)
+					check.pass.Reportf(pos, "invalid non-alphanumeric build constraint: %s", arg)
+					check.crossCheck = false
+					break
 				}
 			}
 		}
 	}
-	return nil
+
+	if check.crossCheck {
+		y, err := constraint.Parse(line)
+		if err != nil {
+			// Should never happen - constraint.Parse never rejects a // +build line.
+			// Also, we just checked the syntax above.
+			// Even so, report.
+			check.pass.Reportf(pos, "%v", err)
+			check.crossCheck = false
+			return
+		}
+		if check.plusBuild == nil {
+			check.plusBuild = y
+		} else {
+			check.plusBuild = &constraint.AndExpr{X: check.plusBuild, Y: y}
+		}
+	}
 }
 
-var (
-	nl         = []byte("\n")
-	slashSlash = []byte("//")
-)
+func (check *checker) finish() {
+	if !check.crossCheck || check.plusBuildPos == token.NoPos || check.goBuildPos == token.NoPos {
+		return
+	}
+
+	// Have both //go:build and // +build,
+	// with no errors found (crossCheck still true).
+	// Check they match.
+	var want constraint.Expr
+	lines, err := constraint.PlusBuildLines(check.goBuild)
+	if err != nil {
+		check.pass.Reportf(check.goBuildPos, "%v", err)
+		return
+	}
+	for _, line := range lines {
+		y, err := constraint.Parse(line)
+		if err != nil {
+			// Definitely should not happen, but not the user's fault.
+			// Do not report.
+			return
+		}
+		if want == nil {
+			want = y
+		} else {
+			want = &constraint.AndExpr{X: want, Y: y}
+		}
+	}
+	if want.String() != check.plusBuild.String() {
+		check.pass.Reportf(check.plusBuildPos, "+build lines do not match //go:build condition")
+		return
+	}
+}
diff --git a/go/analysis/passes/buildtag/buildtag_old.go b/go/analysis/passes/buildtag/buildtag_old.go
new file mode 100644
index 0000000..e923492
--- /dev/null
+++ b/go/analysis/passes/buildtag/buildtag_old.go
@@ -0,0 +1,174 @@
+// Copyright 2013 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.
+
+// TODO(rsc): Delete this file once Go 1.17 comes out and we can retire Go 1.15 support.
+
+//go:build !go1.16
+// +build !go1.16
+
+// Package buildtag defines an Analyzer that checks build tags.
+package buildtag
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"strings"
+	"unicode"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
+)
+
+const Doc = "check that +build tags are well-formed and correctly located"
+
+var Analyzer = &analysis.Analyzer{
+	Name: "buildtag",
+	Doc:  Doc,
+	Run:  runBuildTag,
+}
+
+func runBuildTag(pass *analysis.Pass) (interface{}, error) {
+	for _, f := range pass.Files {
+		checkGoFile(pass, f)
+	}
+	for _, name := range pass.OtherFiles {
+		if err := checkOtherFile(pass, name); err != nil {
+			return nil, err
+		}
+	}
+	for _, name := range pass.IgnoredFiles {
+		if strings.HasSuffix(name, ".go") {
+			f, err := parser.ParseFile(pass.Fset, name, nil, parser.ParseComments)
+			if err != nil {
+				// Not valid Go source code - not our job to diagnose, so ignore.
+				return nil, nil
+			}
+			checkGoFile(pass, f)
+		} else {
+			if err := checkOtherFile(pass, name); err != nil {
+				return nil, err
+			}
+		}
+	}
+	return nil, nil
+}
+
+func checkGoFile(pass *analysis.Pass, f *ast.File) {
+	pastCutoff := false
+	for _, group := range f.Comments {
+		// A +build comment is ignored after or adjoining the package declaration.
+		if group.End()+1 >= f.Package {
+			pastCutoff = true
+		}
+
+		// "+build" is ignored within or after a /*...*/ comment.
+		if !strings.HasPrefix(group.List[0].Text, "//") {
+			pastCutoff = true
+			continue
+		}
+
+		// Check each line of a //-comment.
+		for _, c := range group.List {
+			if !strings.Contains(c.Text, "+build") {
+				continue
+			}
+			if err := checkLine(c.Text, pastCutoff); err != nil {
+				pass.Reportf(c.Pos(), "%s", err)
+			}
+		}
+	}
+}
+
+func checkOtherFile(pass *analysis.Pass, filename string) error {
+	content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
+	if err != nil {
+		return err
+	}
+
+	// We must look at the raw lines, as build tags may appear in non-Go
+	// files such as assembly files.
+	lines := bytes.SplitAfter(content, nl)
+
+	// Determine cutpoint where +build comments are no longer valid.
+	// They are valid in leading // comments in the file followed by
+	// a blank line.
+	//
+	// This must be done as a separate pass because of the
+	// requirement that the comment be followed by a blank line.
+	var cutoff int
+	for i, line := range lines {
+		line = bytes.TrimSpace(line)
+		if !bytes.HasPrefix(line, slashSlash) {
+			if len(line) > 0 {
+				break
+			}
+			cutoff = i
+		}
+	}
+
+	for i, line := range lines {
+		line = bytes.TrimSpace(line)
+		if !bytes.HasPrefix(line, slashSlash) {
+			continue
+		}
+		if !bytes.Contains(line, []byte("+build")) {
+			continue
+		}
+		if err := checkLine(string(line), i >= cutoff); err != nil {
+			pass.Reportf(analysisutil.LineStart(tf, i+1), "%s", err)
+			continue
+		}
+	}
+	return nil
+}
+
+// checkLine checks a line that starts with "//" and contains "+build".
+func checkLine(line string, pastCutoff bool) error {
+	line = strings.TrimPrefix(line, "//")
+	line = strings.TrimSpace(line)
+
+	if strings.HasPrefix(line, "+build") {
+		fields := strings.Fields(line)
+		if fields[0] != "+build" {
+			// Comment is something like +buildasdf not +build.
+			return fmt.Errorf("possible malformed +build comment")
+		}
+		if pastCutoff {
+			return fmt.Errorf("+build comment must appear before package clause and be followed by a blank line")
+		}
+		if err := checkArguments(fields); err != nil {
+			return err
+		}
+	} else {
+		// Comment with +build but not at beginning.
+		if !pastCutoff {
+			return fmt.Errorf("possible malformed +build comment")
+		}
+	}
+	return nil
+}
+
+func checkArguments(fields []string) error {
+	for _, arg := range fields[1:] {
+		for _, elem := range strings.Split(arg, ",") {
+			if strings.HasPrefix(elem, "!!") {
+				return fmt.Errorf("invalid double negative in build constraint: %s", arg)
+			}
+			elem = strings.TrimPrefix(elem, "!")
+			for _, c := range elem {
+				if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+					return fmt.Errorf("invalid non-alphanumeric build constraint: %s", arg)
+				}
+			}
+		}
+	}
+	return nil
+}
+
+var (
+	nl         = []byte("\n")
+	slashSlash = []byte("//")
+)
diff --git a/go/analysis/passes/buildtag/buildtag_test.go b/go/analysis/passes/buildtag/buildtag_test.go
index 110343c..163e8e3 100644
--- a/go/analysis/passes/buildtag/buildtag_test.go
+++ b/go/analysis/passes/buildtag/buildtag_test.go
@@ -5,6 +5,8 @@
 package buildtag_test
 
 import (
+	"runtime"
+	"strings"
 	"testing"
 
 	"golang.org/x/tools/go/analysis"
@@ -13,6 +15,9 @@
 )
 
 func Test(t *testing.T) {
+	if strings.HasPrefix(runtime.Version(), "go1.") && runtime.Version() < "go1.16" {
+		t.Skipf("skipping on %v", runtime.Version())
+	}
 	analyzer := *buildtag.Analyzer
 	analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
 		defer func() {
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag.go b/go/analysis/passes/buildtag/testdata/src/a/buildtag.go
index dcc980c..5bc5d3c 100644
--- a/go/analysis/passes/buildtag/testdata/src/a/buildtag.go
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag.go
@@ -4,15 +4,19 @@
 
 // This file contains tests for the buildtag checker.
 
-// +builder // want `possible malformed \+build comment`
-// +build !ignore
+// want +1 `possible malformed \+build comment`
+// +builder
+// +build ignore
 
 // Mention +build // want `possible malformed \+build comment`
 
-// +build nospace // want "build comment must appear before package clause and be followed by a blank line"
+// want +1 `misplaced \+build comment`
+// +build nospace
+//go:build ok
 package a
 
-// +build toolate // want "build comment must appear before package clause and be followed by a blank line$"
+// want +1 `misplaced \+build comment`
+// +build toolate
 
 var _ = 3
 
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag2.go b/go/analysis/passes/buildtag/testdata/src/a/buildtag2.go
index 3b71ca5..453cbea 100644
--- a/go/analysis/passes/buildtag/testdata/src/a/buildtag2.go
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag2.go
@@ -6,4 +6,12 @@
 
 package a
 
-// +build toolate // want "build comment must appear before package clause and be followed by a blank line$"
+// want +1 `misplaced \+build comment`
+// +build toolate
+
+// want +1 `misplaced //go:build comment`
+//go:build toolate
+
+var _ = `
+// +build notacomment
+`
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag3.go b/go/analysis/passes/buildtag/testdata/src/a/buildtag3.go
new file mode 100644
index 0000000..0e81c49
--- /dev/null
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag3.go
@@ -0,0 +1,14 @@
+// Copyright 2020 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.
+
+// want +3 `[+]build lines do not match //go:build condition`
+
+//go:build good
+// +build bad
+
+package a
+
+var _ = `
+// +build notacomment
+`
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag4.go b/go/analysis/passes/buildtag/testdata/src/a/buildtag4.go
new file mode 100644
index 0000000..2651130
--- /dev/null
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag4.go
@@ -0,0 +1,9 @@
+// Copyright 2020 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.
+
+//go:build !(bad || worse)
+// +build !bad
+// +build !worse
+
+package a
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag5.go b/go/analysis/passes/buildtag/testdata/src/a/buildtag5.go
new file mode 100644
index 0000000..bd5e039
--- /dev/null
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag5.go
@@ -0,0 +1,11 @@
+// Copyright 2020 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.
+
+//go:build !(bad || worse)
+// +build !bad,!worse
+
+package a
+
+//want +1 `misplaced \+build comment`
+// +build other
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag6.s b/go/analysis/passes/buildtag/testdata/src/a/buildtag6.s
new file mode 100644
index 0000000..40fe14c
--- /dev/null
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag6.s
@@ -0,0 +1,9 @@
+// Copyright 2020 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.
+
+#include "go_asm.h"
+
+// ok because we cannot parse assembly files.
+// +build no
+
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag7.s b/go/analysis/passes/buildtag/testdata/src/a/buildtag7.s
new file mode 100644
index 0000000..b622d48
--- /dev/null
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag7.s
@@ -0,0 +1,11 @@
+// Copyright 2020 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 ignore
+
+#include "go_asm.h"
+
+// ok because we cannot parse assembly files
+// the assembler would complain if we did assemble this file.
+//go:build no
diff --git a/go/analysis/passes/buildtag/testdata/src/a/buildtag8.s b/go/analysis/passes/buildtag/testdata/src/a/buildtag8.s
new file mode 100644
index 0000000..2f4edd3
--- /dev/null
+++ b/go/analysis/passes/buildtag/testdata/src/a/buildtag8.s
@@ -0,0 +1,14 @@
+// Copyright 2020 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.
+
+// want +3 `\+build lines do not match //go:build condition`
+
+//go:build something
+// +build ignore
+
+#include "go_asm.h"
+
+// ok because we cannot parse assembly files
+// the assembler would complain if we did assemble this file.
+//go:build no
diff --git a/go/analysis/passes/errorsas/errorsas_test.go b/go/analysis/passes/errorsas/errorsas_test.go
index 19e783e..5ef8668 100644
--- a/go/analysis/passes/errorsas/errorsas_test.go
+++ b/go/analysis/passes/errorsas/errorsas_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.13
 // +build go1.13
 
 package errorsas_test
diff --git a/go/analysis/passes/sigchanyzer/sigchanyzer.go b/go/analysis/passes/sigchanyzer/sigchanyzer.go
new file mode 100644
index 0000000..3d89061
--- /dev/null
+++ b/go/analysis/passes/sigchanyzer/sigchanyzer.go
@@ -0,0 +1,129 @@
+// Copyright 2020 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 sigchanyzer defines an Analyzer that detects
+// misuse of unbuffered signal as argument to signal.Notify.
+package sigchanyzer
+
+import (
+	"bytes"
+	"go/ast"
+	"go/format"
+	"go/token"
+	"go/types"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/inspect"
+	"golang.org/x/tools/go/ast/inspector"
+)
+
+const Doc = `check for unbuffered channel of os.Signal
+
+This checker reports call expression of the form signal.Notify(c <-chan os.Signal, sig ...os.Signal),
+where c is an unbuffered channel, which can be at risk of missing the signal.`
+
+// Analyzer describes sigchanyzer analysis function detector.
+var Analyzer = &analysis.Analyzer{
+	Name:     "sigchanyzer",
+	Doc:      Doc,
+	Requires: []*analysis.Analyzer{inspect.Analyzer},
+	Run:      run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+	nodeFilter := []ast.Node{
+		(*ast.CallExpr)(nil),
+	}
+	inspect.Preorder(nodeFilter, func(n ast.Node) {
+		call := n.(*ast.CallExpr)
+		if !isSignalNotify(pass.TypesInfo, call) {
+			return
+		}
+		var chanDecl *ast.CallExpr
+		switch arg := call.Args[0].(type) {
+		case *ast.Ident:
+			if decl, ok := findDecl(arg).(*ast.CallExpr); ok {
+				chanDecl = decl
+			}
+		case *ast.CallExpr:
+			chanDecl = arg
+		}
+		if chanDecl == nil || len(chanDecl.Args) != 1 {
+			return
+		}
+		chanDecl.Args = append(chanDecl.Args, &ast.BasicLit{
+			Kind:  token.INT,
+			Value: "1",
+		})
+		var buf bytes.Buffer
+		if err := format.Node(&buf, token.NewFileSet(), chanDecl); err != nil {
+			return
+		}
+		pass.Report(analysis.Diagnostic{
+			Pos:     call.Pos(),
+			End:     call.End(),
+			Message: "misuse of unbuffered os.Signal channel as argument to signal.Notify",
+			SuggestedFixes: []analysis.SuggestedFix{{
+				Message: "Change to buffer channel",
+				TextEdits: []analysis.TextEdit{{
+					Pos:     chanDecl.Pos(),
+					End:     chanDecl.End(),
+					NewText: buf.Bytes(),
+				}},
+			}},
+		})
+	})
+	return nil, nil
+}
+
+func isSignalNotify(info *types.Info, call *ast.CallExpr) bool {
+	check := func(id *ast.Ident) bool {
+		obj := info.ObjectOf(id)
+		return obj.Name() == "Notify" && obj.Pkg().Path() == "os/signal"
+	}
+	switch fun := call.Fun.(type) {
+	case *ast.SelectorExpr:
+		return check(fun.Sel)
+	case *ast.Ident:
+		if fun, ok := findDecl(fun).(*ast.SelectorExpr); ok {
+			return check(fun.Sel)
+		}
+		return false
+	default:
+		return false
+	}
+}
+
+func findDecl(arg *ast.Ident) ast.Node {
+	if arg.Obj == nil {
+		return nil
+	}
+	switch as := arg.Obj.Decl.(type) {
+	case *ast.AssignStmt:
+		if len(as.Lhs) != len(as.Rhs) {
+			return nil
+		}
+		for i, lhs := range as.Lhs {
+			lid, ok := lhs.(*ast.Ident)
+			if !ok {
+				continue
+			}
+			if lid.Obj == arg.Obj {
+				return as.Rhs[i]
+			}
+		}
+	case *ast.ValueSpec:
+		if len(as.Names) != len(as.Values) {
+			return nil
+		}
+		for i, name := range as.Names {
+			if name.Obj == arg.Obj {
+				return as.Values[i]
+			}
+		}
+	}
+	return nil
+}
diff --git a/go/analysis/passes/sigchanyzer/sigchanyzer_test.go b/go/analysis/passes/sigchanyzer/sigchanyzer_test.go
new file mode 100644
index 0000000..50b3f4b
--- /dev/null
+++ b/go/analysis/passes/sigchanyzer/sigchanyzer_test.go
@@ -0,0 +1,17 @@
+// Copyright 2020 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 sigchanyzer_test
+
+import (
+	"testing"
+
+	"golang.org/x/tools/go/analysis/analysistest"
+	"golang.org/x/tools/go/analysis/passes/sigchanyzer"
+)
+
+func Test(t *testing.T) {
+	testdata := analysistest.TestData()
+	analysistest.RunWithSuggestedFixes(t, testdata, sigchanyzer.Analyzer, "a")
+}
diff --git a/go/analysis/passes/sigchanyzer/testdata/src/a/a.go b/go/analysis/passes/sigchanyzer/testdata/src/a/a.go
new file mode 100644
index 0000000..277bf20
--- /dev/null
+++ b/go/analysis/passes/sigchanyzer/testdata/src/a/a.go
@@ -0,0 +1,38 @@
+package p
+
+import (
+	"os"
+	ao "os"
+	"os/signal"
+)
+
+var c = make(chan os.Signal)
+var d = make(chan os.Signal)
+
+func f() {
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, os.Interrupt) // ok
+	_ = <-c
+}
+
+func g() {
+	c := make(chan os.Signal)
+	signal.Notify(c, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+	_ = <-c
+}
+
+func h() {
+	c := make(chan ao.Signal)
+	signal.Notify(c, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+	_ = <-c
+}
+
+func i() {
+	signal.Notify(d, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+}
+
+func j() {
+	c := make(chan os.Signal)
+	f := signal.Notify
+	f(c, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+}
diff --git a/go/analysis/passes/sigchanyzer/testdata/src/a/a.go.golden b/go/analysis/passes/sigchanyzer/testdata/src/a/a.go.golden
new file mode 100644
index 0000000..e3702d7
--- /dev/null
+++ b/go/analysis/passes/sigchanyzer/testdata/src/a/a.go.golden
@@ -0,0 +1,38 @@
+package p
+
+import (
+	"os"
+	ao "os"
+	"os/signal"
+)
+
+var c = make(chan os.Signal)
+var d = make(chan os.Signal, 1)
+
+func f() {
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, os.Interrupt) // ok
+	_ = <-c
+}
+
+func g() {
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+	_ = <-c
+}
+
+func h() {
+	c := make(chan ao.Signal, 1)
+	signal.Notify(c, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+	_ = <-c
+}
+
+func i() {
+	signal.Notify(d, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+}
+
+func j() {
+	c := make(chan os.Signal, 1)
+	f := signal.Notify
+	f(c, os.Interrupt) // want "misuse of unbuffered os.Signal channel as argument to signal.Notify"
+}
diff --git a/go/analysis/passes/unusedwrite/testdata/src/a/unusedwrite.go b/go/analysis/passes/unusedwrite/testdata/src/a/unusedwrite.go
new file mode 100644
index 0000000..7e43ee4
--- /dev/null
+++ b/go/analysis/passes/unusedwrite/testdata/src/a/unusedwrite.go
@@ -0,0 +1,75 @@
+package a
+
+type T1 struct{ x int }
+
+type T2 struct {
+	x int
+	y int
+}
+
+type T3 struct{ y *T1 }
+
+func BadWrites() {
+	// Test struct field writes.
+	var s1 T1
+	s1.x = 10 // want "unused write to field x"
+
+	// Test array writes.
+	var s2 [10]int
+	s2[1] = 10 // want "unused write to array index 1:int"
+
+	// Test range variables of struct type.
+	s3 := []T1{T1{x: 100}}
+	for i, v := range s3 {
+		v.x = i // want "unused write to field x"
+	}
+
+	// Test the case where a different field is read after the write.
+	s4 := []T2{T2{x: 1, y: 2}}
+	for i, v := range s4 {
+		v.x = i // want "unused write to field x"
+		_ = v.y
+	}
+}
+
+func (t T1) BadValueReceiverWrite(v T2) {
+	t.x = 10 // want "unused write to field x"
+	v.y = 20 // want "unused write to field y"
+}
+
+func GoodWrites(m map[int]int) {
+	// A map is copied by reference such that a write will affect the original map.
+	m[1] = 10
+
+	// Test struct field writes.
+	var s1 T1
+	s1.x = 10
+	print(s1.x)
+
+	// Test array writes.
+	var s2 [10]int
+	s2[1] = 10
+	// Current the checker doesn't distinguish index 1 and index 2.
+	_ = s2[2]
+
+	// Test range variables of struct type.
+	s3 := []T1{T1{x: 100}}
+	for i, v := range s3 { // v is a copy
+		v.x = i
+		_ = v.x // still a usage
+	}
+
+	// Test an object with multiple fields.
+	o := &T2{x: 10, y: 20}
+	print(o)
+
+	// Test an object of embedded struct/pointer type.
+	t1 := &T1{x: 10}
+	t2 := &T3{y: t1}
+	print(t2)
+}
+
+func (t *T1) GoodPointerReceiverWrite(v *T2) {
+	t.x = 10
+	v.y = 20
+}
diff --git a/go/analysis/passes/unusedwrite/unusedwrite.go b/go/analysis/passes/unusedwrite/unusedwrite.go
new file mode 100644
index 0000000..37a0e78
--- /dev/null
+++ b/go/analysis/passes/unusedwrite/unusedwrite.go
@@ -0,0 +1,184 @@
+// Copyright 2021 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 unusedwrite checks for unused writes to the elements of a struct or array object.
+package unusedwrite
+
+import (
+	"fmt"
+	"go/types"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/buildssa"
+	"golang.org/x/tools/go/ssa"
+)
+
+// Doc is a documentation string.
+const Doc = `checks for unused writes
+
+The analyzer reports instances of writes to struct fields and
+arrays that are never read. Specifically, when a struct object
+or an array is copied, its elements are copied implicitly by
+the compiler, and any element write to this copy does nothing
+with the original object.
+
+For example:
+
+	type T struct { x int }
+	func f(input []T) {
+		for i, v := range input {  // v is a copy
+			v.x = i  // unused write to field x
+		}
+	}
+
+Another example is about non-pointer receiver:
+
+	type T struct { x int }
+	func (t T) f() {  // t is a copy
+		t.x = i  // unused write to field x
+	}
+`
+
+// Analyzer reports instances of writes to struct fields and arrays
+//that are never read.
+var Analyzer = &analysis.Analyzer{
+	Name:     "unusedwrite",
+	Doc:      Doc,
+	Requires: []*analysis.Analyzer{buildssa.Analyzer},
+	Run:      run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+	// Check the writes to struct and array objects.
+	checkStore := func(store *ssa.Store) {
+		// Consider field/index writes to an object whose elements are copied and not shared.
+		// MapUpdate is excluded since only the reference of the map is copied.
+		switch addr := store.Addr.(type) {
+		case *ssa.FieldAddr:
+			if isDeadStore(store, addr.X, addr) {
+				// Report the bug.
+				pass.Reportf(store.Pos(),
+					"unused write to field %s",
+					getFieldName(addr.X.Type(), addr.Field))
+			}
+		case *ssa.IndexAddr:
+			if isDeadStore(store, addr.X, addr) {
+				// Report the bug.
+				pass.Reportf(store.Pos(),
+					"unused write to array index %s", addr.Index)
+			}
+		}
+	}
+
+	ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
+	for _, fn := range ssainput.SrcFuncs {
+		// Visit each block. No need to visit fn.Recover.
+		for _, blk := range fn.Blocks {
+			for _, instr := range blk.Instrs {
+				// Identify writes.
+				if store, ok := instr.(*ssa.Store); ok {
+					checkStore(store)
+				}
+			}
+		}
+	}
+	return nil, nil
+}
+
+// isDeadStore determines whether a field/index write to an object is dead.
+// Argument "obj" is the object, and "addr" is the instruction fetching the field/index.
+func isDeadStore(store *ssa.Store, obj ssa.Value, addr ssa.Instruction) bool {
+	// Consider only struct or array objects.
+	if !hasStructOrArrayType(obj) {
+		return false
+	}
+	// Check liveness: if the value is used later, then don't report the write.
+	for _, ref := range *obj.Referrers() {
+		if ref == store || ref == addr {
+			continue
+		}
+		switch ins := ref.(type) {
+		case ssa.CallInstruction:
+			return false
+		case *ssa.FieldAddr:
+			// Check whether the same field is used.
+			if ins.X == obj {
+				if faddr, ok := addr.(*ssa.FieldAddr); ok {
+					if faddr.Field == ins.Field {
+						return false
+					}
+				}
+			}
+			// Otherwise another field is used, and this usage doesn't count.
+			continue
+		case *ssa.IndexAddr:
+			if ins.X == obj {
+				return false
+			}
+			continue // Otherwise another object is used
+		case *ssa.Lookup:
+			if ins.X == obj {
+				return false
+			}
+			continue // Otherwise another object is used
+		case *ssa.Store:
+			if ins.Val == obj {
+				return false
+			}
+			continue // Otherwise other object is stored
+		default: // consider live if the object is used in any other instruction
+			return false
+		}
+	}
+	return true
+}
+
+// isStructOrArray returns whether the underlying type is struct or array.
+func isStructOrArray(tp types.Type) bool {
+	if named, ok := tp.(*types.Named); ok {
+		tp = named.Underlying()
+	}
+	switch tp.(type) {
+	case *types.Array:
+		return true
+	case *types.Struct:
+		return true
+	}
+	return false
+}
+
+// hasStructOrArrayType returns whether a value is of struct or array type.
+func hasStructOrArrayType(v ssa.Value) bool {
+	if instr, ok := v.(ssa.Instruction); ok {
+		if alloc, ok := instr.(*ssa.Alloc); ok {
+			// Check the element type of an allocated register (which always has pointer type)
+			// e.g., for
+			//   func (t T) f() { ...}
+			// the receiver object is of type *T:
+			//   t0 = local T (t)   *T
+			if tp, ok := alloc.Type().(*types.Pointer); ok {
+				return isStructOrArray(tp.Elem())
+			}
+			return false
+		}
+	}
+	return isStructOrArray(v.Type())
+}
+
+// getFieldName returns the name of a field in a struct.
+// It the field is not found, then it returns the string format of the index.
+//
+// For example, for struct T {x int, y int), getFieldName(*T, 1) returns "y".
+func getFieldName(tp types.Type, index int) string {
+	if pt, ok := tp.(*types.Pointer); ok {
+		tp = pt.Elem()
+	}
+	if named, ok := tp.(*types.Named); ok {
+		tp = named.Underlying()
+	}
+	if stp, ok := tp.(*types.Struct); ok {
+		return stp.Field(index).Name()
+	}
+	return fmt.Sprintf("%d", index)
+}
diff --git a/go/analysis/passes/unusedwrite/unusedwrite_test.go b/go/analysis/passes/unusedwrite/unusedwrite_test.go
new file mode 100644
index 0000000..9658849
--- /dev/null
+++ b/go/analysis/passes/unusedwrite/unusedwrite_test.go
@@ -0,0 +1,17 @@
+// Copyright 2021 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 unusedwrite_test
+
+import (
+	"testing"
+
+	"golang.org/x/tools/go/analysis/analysistest"
+	"golang.org/x/tools/go/analysis/passes/unusedwrite"
+)
+
+func Test(t *testing.T) {
+	testdata := analysistest.TestData()
+	analysistest.Run(t, testdata, unusedwrite.Analyzer, "a")
+}
diff --git a/go/analysis/unitchecker/main.go b/go/analysis/unitchecker/main.go
index 7fa7c85..23acb7e 100644
--- a/go/analysis/unitchecker/main.go
+++ b/go/analysis/unitchecker/main.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 // This file provides an example command for static checkers
diff --git a/go/analysis/unitchecker/unitchecker.go b/go/analysis/unitchecker/unitchecker.go
index 713e138..5424489 100644
--- a/go/analysis/unitchecker/unitchecker.go
+++ b/go/analysis/unitchecker/unitchecker.go
@@ -97,7 +97,7 @@
 
 Usage of %[1]s:
 	%.16[1]s unit.cfg	# execute analysis specified by config file
-	%.16[1]s help    	# general help
+	%.16[1]s help    	# general help, including listing analyzers and flags
 	%.16[1]s help name	# help on specific analyzer and its flags
 `, progname)
 		os.Exit(1)
diff --git a/go/analysis/unitchecker/unitchecker112.go b/go/analysis/unitchecker/unitchecker112.go
index 9051456..3180f4a 100644
--- a/go/analysis/unitchecker/unitchecker112.go
+++ b/go/analysis/unitchecker/unitchecker112.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.12
 // +build go1.12
 
 package unitchecker
diff --git a/go/analysis/unitchecker/unitchecker_test.go b/go/analysis/unitchecker/unitchecker_test.go
index 4a302ff..7e5b848 100644
--- a/go/analysis/unitchecker/unitchecker_test.go
+++ b/go/analysis/unitchecker/unitchecker_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.12
 // +build go1.12
 
 package unitchecker_test
diff --git a/go/buildutil/allpackages_test.go b/go/buildutil/allpackages_test.go
index ccdc31b..1aa194d 100644
--- a/go/buildutil/allpackages_test.go
+++ b/go/buildutil/allpackages_test.go
@@ -4,6 +4,7 @@
 
 // Incomplete source tree on Android.
 
+//go:build !android
 // +build !android
 
 package buildutil_test
diff --git a/go/callgraph/cha/cha_test.go b/go/callgraph/cha/cha_test.go
index cb2d585..3dc0314 100644
--- a/go/callgraph/cha/cha_test.go
+++ b/go/callgraph/cha/cha_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package cha_test
diff --git a/go/callgraph/rta/rta_test.go b/go/callgraph/rta/rta_test.go
index 28a00b3..9ae1bdf 100644
--- a/go/callgraph/rta/rta_test.go
+++ b/go/callgraph/rta/rta_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package rta_test
diff --git a/go/gcexportdata/example_test.go b/go/gcexportdata/example_test.go
index a50bc40..fda3f60 100644
--- a/go/gcexportdata/example_test.go
+++ b/go/gcexportdata/example_test.go
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build go1.7
-// +build gc
+//go:build go1.7 && gc
+// +build go1.7,gc
 
 package gcexportdata_test
 
diff --git a/go/gcexportdata/gcexportdata.go b/go/gcexportdata/gcexportdata.go
index f8363d8..fc8beea 100644
--- a/go/gcexportdata/gcexportdata.go
+++ b/go/gcexportdata/gcexportdata.go
@@ -100,10 +100,34 @@
 // Write writes encoded type information for the specified package to out.
 // The FileSet provides file position information for named objects.
 func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
-	b, err := gcimporter.IExportData(fset, pkg)
-	if err != nil {
+	if _, err := io.WriteString(out, "i"); err != nil {
 		return err
 	}
-	_, err = out.Write(b)
-	return err
+	return gcimporter.IExportData(out, fset, pkg)
+}
+
+// ReadBundle reads an export bundle from in, decodes it, and returns type
+// information for the packages.
+// File position information is added to fset.
+//
+// ReadBundle may inspect and add to the imports map to ensure that references
+// within the export bundle to other packages are consistent.
+//
+// On return, the state of the reader is undefined.
+//
+// Experimental: This API is experimental and may change in the future.
+func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) {
+	data, err := ioutil.ReadAll(in)
+	if err != nil {
+		return nil, fmt.Errorf("reading export bundle: %v", err)
+	}
+	return gcimporter.IImportBundle(fset, imports, data)
+}
+
+// WriteBundle writes encoded type information for the specified packages to out.
+// The FileSet provides file position information for named objects.
+//
+// Experimental: This API is experimental and may change in the future.
+func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
+	return gcimporter.IExportBundle(out, fset, pkgs)
 }
diff --git a/go/gcexportdata/main.go b/go/gcexportdata/main.go
index 2713dce..e9df4e9 100644
--- a/go/gcexportdata/main.go
+++ b/go/gcexportdata/main.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 // The gcexportdata command is a diagnostic tool that displays the
diff --git a/go/internal/cgo/cgo.go b/go/internal/cgo/cgo.go
index 9772504..d9074ea 100644
--- a/go/internal/cgo/cgo.go
+++ b/go/internal/cgo/cgo.go
@@ -57,13 +57,14 @@
 	"go/build"
 	"go/parser"
 	"go/token"
-	exec "golang.org/x/sys/execabs"
 	"io/ioutil"
 	"log"
 	"os"
 	"path/filepath"
 	"regexp"
 	"strings"
+
+	exec "golang.org/x/sys/execabs"
 )
 
 // ProcessFiles invokes the cgo preprocessor on bp.CgoFiles, parses
@@ -159,14 +160,13 @@
 	}
 
 	args := stringList(
-		"go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--",
+		"go", "tool", "cgo", "-srcdir", pkgdir, "-objdir", tmpdir, cgoflags, "--",
 		cgoCPPFLAGS, cgoexeCFLAGS, cgoFiles,
 	)
 	if false {
-		log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir)
+		log.Printf("Running cgo for package %q: %s", bp.ImportPath, args)
 	}
 	cmd := exec.Command(args[0], args[1:]...)
-	cmd.Dir = pkgdir
 	cmd.Stdout = os.Stderr
 	cmd.Stderr = os.Stderr
 	if err := cmd.Run(); err != nil {
diff --git a/go/internal/gccgoimporter/newInterface10.go b/go/internal/gccgoimporter/newInterface10.go
index 9a108d4..1b449ef 100644
--- a/go/internal/gccgoimporter/newInterface10.go
+++ b/go/internal/gccgoimporter/newInterface10.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.11
 // +build !go1.11
 
 package gccgoimporter
diff --git a/go/internal/gccgoimporter/newInterface11.go b/go/internal/gccgoimporter/newInterface11.go
index 1636610..631546e 100644
--- a/go/internal/gccgoimporter/newInterface11.go
+++ b/go/internal/gccgoimporter/newInterface11.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.11
 // +build go1.11
 
 package gccgoimporter
diff --git a/go/internal/gcimporter/bexport_test.go b/go/internal/gcimporter/bexport_test.go
index de7b921..702278e 100644
--- a/go/internal/gcimporter/bexport_test.go
+++ b/go/internal/gcimporter/bexport_test.go
@@ -12,6 +12,7 @@
 	"go/parser"
 	"go/token"
 	"go/types"
+	"path/filepath"
 	"reflect"
 	"runtime"
 	"strings"
@@ -117,7 +118,8 @@
 
 func fileLine(fset *token.FileSet, obj types.Object) string {
 	posn := fset.Position(obj.Pos())
-	return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
+	filename := filepath.Clean(strings.ReplaceAll(posn.Filename, "$GOROOT", runtime.GOROOT()))
+	return fmt.Sprintf("%s:%d", filename, posn.Line)
 }
 
 // equalObj reports how x and y differ.  They are assumed to belong to
diff --git a/go/internal/gcimporter/iexport.go b/go/internal/gcimporter/iexport.go
index 4be32a2..d2fc8b6 100644
--- a/go/internal/gcimporter/iexport.go
+++ b/go/internal/gcimporter/iexport.go
@@ -25,12 +25,25 @@
 // 0: Go1.11 encoding
 const iexportVersion = 0
 
-// IExportData returns the binary export data for pkg.
+// Current bundled export format version. Increase with each format change.
+// 0: initial implementation
+const bundleVersion = 0
+
+// IExportData writes indexed export data for pkg to out.
 //
 // If no file set is provided, position info will be missing.
 // The package path of the top-level package will not be recorded,
 // so that calls to IImportData can override with a provided package path.
-func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) {
+func IExportData(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
+	return iexportCommon(out, fset, false, []*types.Package{pkg})
+}
+
+// IExportBundle writes an indexed export bundle for pkgs to out.
+func IExportBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
+	return iexportCommon(out, fset, true, pkgs)
+}
+
+func iexportCommon(out io.Writer, fset *token.FileSet, bundle bool, pkgs []*types.Package) (err error) {
 	defer func() {
 		if e := recover(); e != nil {
 			if ierr, ok := e.(internalError); ok {
@@ -43,13 +56,14 @@
 	}()
 
 	p := iexporter{
-		out:         bytes.NewBuffer(nil),
 		fset:        fset,
 		allPkgs:     map[*types.Package]bool{},
 		stringIndex: map[string]uint64{},
 		declIndex:   map[types.Object]uint64{},
 		typIndex:    map[types.Type]uint64{},
-		localpkg:    pkg,
+	}
+	if !bundle {
+		p.localpkg = pkgs[0]
 	}
 
 	for i, pt := range predeclared() {
@@ -60,10 +74,20 @@
 	}
 
 	// Initialize work queue with exported declarations.
-	scope := pkg.Scope()
-	for _, name := range scope.Names() {
-		if ast.IsExported(name) {
-			p.pushDecl(scope.Lookup(name))
+	for _, pkg := range pkgs {
+		scope := pkg.Scope()
+		for _, name := range scope.Names() {
+			if ast.IsExported(name) {
+				p.pushDecl(scope.Lookup(name))
+			}
+		}
+
+		if bundle {
+			// Ensure pkg and its imports are included in the index.
+			p.allPkgs[pkg] = true
+			for _, imp := range pkg.Imports() {
+				p.allPkgs[imp] = true
+			}
 		}
 	}
 
@@ -76,21 +100,35 @@
 	dataLen := uint64(p.data0.Len())
 	w := p.newWriter()
 	w.writeIndex(p.declIndex)
+
+	if bundle {
+		w.uint64(uint64(len(pkgs)))
+		for _, pkg := range pkgs {
+			w.pkg(pkg)
+			imps := pkg.Imports()
+			w.uint64(uint64(len(imps)))
+			for _, imp := range imps {
+				w.pkg(imp)
+			}
+		}
+	}
 	w.flush()
 
 	// Assemble header.
 	var hdr intWriter
-	hdr.WriteByte('i')
+	if bundle {
+		hdr.uint64(bundleVersion)
+	}
 	hdr.uint64(iexportVersion)
 	hdr.uint64(uint64(p.strings.Len()))
 	hdr.uint64(dataLen)
 
 	// Flush output.
-	io.Copy(p.out, &hdr)
-	io.Copy(p.out, &p.strings)
-	io.Copy(p.out, &p.data0)
+	io.Copy(out, &hdr)
+	io.Copy(out, &p.strings)
+	io.Copy(out, &p.data0)
 
-	return p.out.Bytes(), nil
+	return nil
 }
 
 // writeIndex writes out an object index. mainIndex indicates whether
@@ -104,7 +142,9 @@
 	// For the main index, make sure to include every package that
 	// we reference, even if we're not exporting (or reexporting)
 	// any symbols from it.
-	pkgObjs[w.p.localpkg] = nil
+	if w.p.localpkg != nil {
+		pkgObjs[w.p.localpkg] = nil
+	}
 	for pkg := range w.p.allPkgs {
 		pkgObjs[pkg] = nil
 	}
@@ -474,10 +514,10 @@
 func (w *exportWriter) value(typ types.Type, v constant.Value) {
 	w.typ(typ, nil)
 
-	switch v.Kind() {
-	case constant.Bool:
+	switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
+	case types.IsBoolean:
 		w.bool(constant.BoolVal(v))
-	case constant.Int:
+	case types.IsInteger:
 		var i big.Int
 		if i64, exact := constant.Int64Val(v); exact {
 			i.SetInt64(i64)
@@ -487,25 +527,27 @@
 			i.SetString(v.ExactString(), 10)
 		}
 		w.mpint(&i, typ)
-	case constant.Float:
+	case types.IsFloat:
 		f := constantToFloat(v)
 		w.mpfloat(f, typ)
-	case constant.Complex:
+	case types.IsComplex:
 		w.mpfloat(constantToFloat(constant.Real(v)), typ)
 		w.mpfloat(constantToFloat(constant.Imag(v)), typ)
-	case constant.String:
+	case types.IsString:
 		w.string(constant.StringVal(v))
-	case constant.Unknown:
-		// package contains type errors
 	default:
-		panic(internalErrorf("unexpected value %v (%T)", v, v))
+		if b.Kind() == types.Invalid {
+			// package contains type errors
+			break
+		}
+		panic(internalErrorf("unexpected type %v (%v)", typ, typ.Underlying()))
 	}
 }
 
 // constantToFloat converts a constant.Value with kind constant.Float to a
 // big.Float.
 func constantToFloat(x constant.Value) *big.Float {
-	assert(x.Kind() == constant.Float)
+	x = constant.ToFloat(x)
 	// Use the same floating-point precision (512) as cmd/compile
 	// (see Mpprec in cmd/compile/internal/gc/mpfloat.go).
 	const mpprec = 512
diff --git a/go/internal/gcimporter/iexport_test.go b/go/internal/gcimporter/iexport_test.go
index 5024570..5385011 100644
--- a/go/internal/gcimporter/iexport_test.go
+++ b/go/internal/gcimporter/iexport_test.go
@@ -4,11 +4,14 @@
 
 // This is a copy of bexport_test.go for iexport.go.
 
+//go:build go1.11
 // +build go1.11
 
 package gcimporter_test
 
 import (
+	"bufio"
+	"bytes"
 	"fmt"
 	"go/ast"
 	"go/build"
@@ -16,7 +19,9 @@
 	"go/parser"
 	"go/token"
 	"go/types"
+	"io/ioutil"
 	"math/big"
+	"os"
 	"reflect"
 	"runtime"
 	"sort"
@@ -28,6 +33,35 @@
 	"golang.org/x/tools/go/loader"
 )
 
+func readExportFile(filename string) ([]byte, error) {
+	f, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	buf := bufio.NewReader(f)
+	if _, err := gcimporter.FindExportData(buf); err != nil {
+		return nil, err
+	}
+
+	if ch, err := buf.ReadByte(); err != nil {
+		return nil, err
+	} else if ch != 'i' {
+		return nil, fmt.Errorf("unexpected byte: %v", ch)
+	}
+
+	return ioutil.ReadAll(buf)
+}
+
+func iexport(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
+	var buf bytes.Buffer
+	if err := gcimporter.IExportData(&buf, fset, pkg); err != nil {
+		return nil, err
+	}
+	return buf.Bytes(), nil
+}
+
 func TestIExportData_stdlib(t *testing.T) {
 	if runtime.Compiler == "gccgo" {
 		t.Skip("gccgo standard library is inaccessible")
@@ -45,6 +79,9 @@
 	conf := loader.Config{
 		Build:       &ctxt,
 		AllowErrors: true,
+		TypeChecker: types.Config{
+			Sizes: types.SizesFor(ctxt.Compiler, ctxt.GOARCH),
+		},
 	}
 	for _, path := range buildutil.AllPackages(conf.Build) {
 		conf.Import(path)
@@ -72,63 +109,87 @@
 	}
 
 	var sorted []*types.Package
-	for pkg := range prog.AllPackages {
-		sorted = append(sorted, pkg)
+	for pkg, info := range prog.AllPackages {
+		if info.Files != nil { // non-empty directory
+			sorted = append(sorted, pkg)
+		}
 	}
 	sort.Slice(sorted, func(i, j int) bool {
 		return sorted[i].Path() < sorted[j].Path()
 	})
 
 	for _, pkg := range sorted {
-		info := prog.AllPackages[pkg]
-		if info.Files == nil {
-			continue // empty directory
-		}
-		exportdata, err := gcimporter.IExportData(conf.Fset, pkg)
-		if err != nil {
-			t.Fatal(err)
-		}
-		if exportdata[0] == 'i' {
-			exportdata = exportdata[1:] // trim the 'i' in the header
+		if exportdata, err := iexport(conf.Fset, pkg); err != nil {
+			t.Error(err)
 		} else {
-			t.Fatalf("unexpected first character of export data: %v", exportdata[0])
+			testPkgData(t, conf.Fset, pkg, exportdata)
 		}
 
-		imports := make(map[string]*types.Package)
-		fset2 := token.NewFileSet()
-		n, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path())
-		if err != nil {
-			t.Errorf("IImportData(%s): %v", pkg.Path(), err)
+		if pkg.Name() == "main" || pkg.Name() == "haserrors" {
+			// skip; no export data
+		} else if bp, err := ctxt.Import(pkg.Path(), "", build.FindOnly); err != nil {
+			t.Log("warning:", err)
+		} else if exportdata, err := readExportFile(bp.PkgObj); err != nil {
+			t.Log("warning:", err)
+		} else {
+			testPkgData(t, conf.Fset, pkg, exportdata)
+		}
+	}
+
+	var bundle bytes.Buffer
+	if err := gcimporter.IExportBundle(&bundle, conf.Fset, sorted); err != nil {
+		t.Fatal(err)
+	}
+	fset2 := token.NewFileSet()
+	imports := make(map[string]*types.Package)
+	pkgs2, err := gcimporter.IImportBundle(fset2, imports, bundle.Bytes())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for i, pkg := range sorted {
+		testPkg(t, conf.Fset, pkg, fset2, pkgs2[i])
+	}
+}
+
+func testPkgData(t *testing.T, fset *token.FileSet, pkg *types.Package, exportdata []byte) {
+	imports := make(map[string]*types.Package)
+	fset2 := token.NewFileSet()
+	_, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path())
+	if err != nil {
+		t.Errorf("IImportData(%s): %v", pkg.Path(), err)
+	}
+
+	testPkg(t, fset, pkg, fset2, pkg2)
+}
+
+func testPkg(t *testing.T, fset *token.FileSet, pkg *types.Package, fset2 *token.FileSet, pkg2 *types.Package) {
+	if _, err := iexport(fset2, pkg2); err != nil {
+		t.Errorf("reexport %q: %v", pkg.Path(), err)
+	}
+
+	// Compare the packages' corresponding members.
+	for _, name := range pkg.Scope().Names() {
+		if !ast.IsExported(name) {
 			continue
 		}
-		if n != len(exportdata) {
-			t.Errorf("IImportData(%s) decoded %d bytes, want %d",
-				pkg.Path(), n, len(exportdata))
+		obj1 := pkg.Scope().Lookup(name)
+		obj2 := pkg2.Scope().Lookup(name)
+		if obj2 == nil {
+			t.Errorf("%s.%s not found, want %s", pkg.Path(), name, obj1)
+			continue
 		}
 
-		// Compare the packages' corresponding members.
-		for _, name := range pkg.Scope().Names() {
-			if !ast.IsExported(name) {
-				continue
-			}
-			obj1 := pkg.Scope().Lookup(name)
-			obj2 := pkg2.Scope().Lookup(name)
-			if obj2 == nil {
-				t.Fatalf("%s.%s not found, want %s", pkg.Path(), name, obj1)
-				continue
-			}
+		fl1 := fileLine(fset, obj1)
+		fl2 := fileLine(fset2, obj2)
+		if fl1 != fl2 {
+			t.Errorf("%s.%s: got posn %s, want %s",
+				pkg.Path(), name, fl2, fl1)
+		}
 
-			fl1 := fileLine(conf.Fset, obj1)
-			fl2 := fileLine(fset2, obj2)
-			if fl1 != fl2 {
-				t.Errorf("%s.%s: got posn %s, want %s",
-					pkg.Path(), name, fl2, fl1)
-			}
-
-			if err := cmpObj(obj1, obj2); err != nil {
-				t.Errorf("%s.%s: %s\ngot:  %s\nwant: %s",
-					pkg.Path(), name, err, obj2, obj1)
-			}
+		if err := cmpObj(obj1, obj2); err != nil {
+			t.Errorf("%s.%s: %s\ngot:  %s\nwant: %s",
+				pkg.Path(), name, err, obj2, obj1)
 		}
 	}
 }
@@ -151,15 +212,10 @@
 	}
 
 	// export
-	exportdata, err := gcimporter.IExportData(fset1, pkg)
+	exportdata, err := iexport(fset1, pkg)
 	if err != nil {
 		t.Fatal(err)
 	}
-	if exportdata[0] == 'i' {
-		exportdata = exportdata[1:] // trim the 'i' in the header
-	} else {
-		t.Fatalf("unexpected first character of export data: %v", exportdata[0])
-	}
 
 	// import
 	imports := make(map[string]*types.Package)
@@ -199,15 +255,10 @@
 
 	// export
 	// use a nil fileset here to confirm that it doesn't panic
-	exportdata, err := gcimporter.IExportData(nil, pkg1)
+	exportdata, err := iexport(nil, pkg1)
 	if err != nil {
 		t.Fatal(err)
 	}
-	if exportdata[0] == 'i' {
-		exportdata = exportdata[1:] // trim the 'i' in the header
-	} else {
-		t.Fatalf("unexpected first character of export data: %v", exportdata[0])
-	}
 
 	// import
 	imports := make(map[string]*types.Package)
diff --git a/go/internal/gcimporter/iimport.go b/go/internal/gcimporter/iimport.go
index a31a880..b236deb 100644
--- a/go/internal/gcimporter/iimport.go
+++ b/go/internal/gcimporter/iimport.go
@@ -59,10 +59,23 @@
 )
 
 // IImportData imports a package from the serialized package data
-// and returns the number of bytes consumed and a reference to the package.
+// and returns 0 and a reference to the package.
 // If the export data version is not recognized or the format is otherwise
 // compromised, an error is returned.
-func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
+func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
+	pkgs, err := iimportCommon(fset, imports, data, false, path)
+	if err != nil {
+		return 0, nil, err
+	}
+	return 0, pkgs[0], nil
+}
+
+// IImportBundle imports a set of packages from the serialized package bundle.
+func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) {
+	return iimportCommon(fset, imports, data, true, "")
+}
+
+func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data []byte, bundle bool, path string) (pkgs []*types.Package, err error) {
 	const currentVersion = 1
 	version := int64(-1)
 	defer func() {
@@ -77,6 +90,15 @@
 
 	r := &intReader{bytes.NewReader(data), path}
 
+	if bundle {
+		bundleVersion := r.uint64()
+		switch bundleVersion {
+		case bundleVersion:
+		default:
+			errorf("unknown bundle format version %d", bundleVersion)
+		}
+	}
+
 	version = int64(r.uint64())
 	switch version {
 	case currentVersion, 0:
@@ -143,39 +165,58 @@
 		p.pkgIndex[pkg] = nameIndex
 		pkgList[i] = pkg
 	}
-	if len(pkgList) == 0 {
-		errorf("no packages found for %s", path)
-		panic("unreachable")
+
+	if bundle {
+		pkgs = make([]*types.Package, r.uint64())
+		for i := range pkgs {
+			pkg := p.pkgAt(r.uint64())
+			imps := make([]*types.Package, r.uint64())
+			for j := range imps {
+				imps[j] = p.pkgAt(r.uint64())
+			}
+			pkg.SetImports(imps)
+			pkgs[i] = pkg
+		}
+	} else {
+		if len(pkgList) == 0 {
+			errorf("no packages found for %s", path)
+			panic("unreachable")
+		}
+		pkgs = pkgList[:1]
+
+		// record all referenced packages as imports
+		list := append(([]*types.Package)(nil), pkgList[1:]...)
+		sort.Sort(byPath(list))
+		pkgs[0].SetImports(list)
 	}
-	p.ipkg = pkgList[0]
-	names := make([]string, 0, len(p.pkgIndex[p.ipkg]))
-	for name := range p.pkgIndex[p.ipkg] {
-		names = append(names, name)
-	}
-	sort.Strings(names)
-	for _, name := range names {
-		p.doDecl(p.ipkg, name)
+
+	for _, pkg := range pkgs {
+		if pkg.Complete() {
+			continue
+		}
+
+		names := make([]string, 0, len(p.pkgIndex[pkg]))
+		for name := range p.pkgIndex[pkg] {
+			names = append(names, name)
+		}
+		sort.Strings(names)
+		for _, name := range names {
+			p.doDecl(pkg, name)
+		}
+
+		// package was imported completely and without errors
+		pkg.MarkComplete()
 	}
 
 	for _, typ := range p.interfaceList {
 		typ.Complete()
 	}
 
-	// record all referenced packages as imports
-	list := append(([]*types.Package)(nil), pkgList[1:]...)
-	sort.Sort(byPath(list))
-	p.ipkg.SetImports(list)
-
-	// package was imported completely and without errors
-	p.ipkg.MarkComplete()
-
-	consumed, _ := r.Seek(0, io.SeekCurrent)
-	return int(consumed), p.ipkg, nil
+	return pkgs, nil
 }
 
 type iimporter struct {
 	ipath   string
-	ipkg    *types.Package
 	version int
 
 	stringData  []byte
@@ -227,9 +268,6 @@
 		return pkg
 	}
 	path := p.stringAt(off)
-	if path == p.ipath {
-		return p.ipkg
-	}
 	errorf("missing package %q in %q", path, p.ipath)
 	return nil
 }
diff --git a/go/internal/gcimporter/israce_test.go b/go/internal/gcimporter/israce_test.go
index af8e52b..885ba1c 100644
--- a/go/internal/gcimporter/israce_test.go
+++ b/go/internal/gcimporter/israce_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build race
 // +build race
 
 package gcimporter_test
diff --git a/go/internal/gcimporter/newInterface10.go b/go/internal/gcimporter/newInterface10.go
index 463f252..8b163e3 100644
--- a/go/internal/gcimporter/newInterface10.go
+++ b/go/internal/gcimporter/newInterface10.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.11
 // +build !go1.11
 
 package gcimporter
diff --git a/go/internal/gcimporter/newInterface11.go b/go/internal/gcimporter/newInterface11.go
index ab28b95..49984f4 100644
--- a/go/internal/gcimporter/newInterface11.go
+++ b/go/internal/gcimporter/newInterface11.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.11
 // +build go1.11
 
 package gcimporter
diff --git a/go/loader/loader.go b/go/loader/loader.go
index bc12ca3..508a1fd 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -869,21 +869,6 @@
 // caused these imports.
 //
 func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) {
-	// TODO(adonovan): opt: do the loop in parallel once
-	// findPackage is non-blocking.
-	var pending []*importInfo
-	for importPath := range imports {
-		bp, err := imp.findPackage(importPath, fromDir, mode)
-		if err != nil {
-			errors = append(errors, importError{
-				path: importPath,
-				err:  err,
-			})
-			continue
-		}
-		pending = append(pending, imp.startLoad(bp))
-	}
-
 	if fromPath != "" {
 		// We're loading a set of imports.
 		//
@@ -895,29 +880,36 @@
 			deps = make(map[string]bool)
 			imp.graph[fromPath] = deps
 		}
-		for _, ii := range pending {
-			deps[ii.path] = true
+		for importPath := range imports {
+			deps[importPath] = true
 		}
 		imp.graphMu.Unlock()
 	}
 
-	for _, ii := range pending {
+	var pending []*importInfo
+	for importPath := range imports {
 		if fromPath != "" {
-			if cycle := imp.findPath(ii.path, fromPath); cycle != nil {
-				// Cycle-forming import: we must not await its
-				// completion since it would deadlock.
-				//
-				// We don't record the error in ii since
-				// the error is really associated with the
-				// cycle-forming edge, not the package itself.
-				// (Also it would complicate the
-				// invariants of importPath completion.)
+			if cycle := imp.findPath(importPath, fromPath); cycle != nil {
+				// Cycle-forming import: we must not check it
+				// since it would deadlock.
 				if trace {
 					fmt.Fprintf(os.Stderr, "import cycle: %q\n", cycle)
 				}
 				continue
 			}
 		}
+		bp, err := imp.findPackage(importPath, fromDir, mode)
+		if err != nil {
+			errors = append(errors, importError{
+				path: importPath,
+				err:  err,
+			})
+			continue
+		}
+		pending = append(pending, imp.startLoad(bp))
+	}
+
+	for _, ii := range pending {
 		ii.awaitCompletion()
 		infos = append(infos, ii.info)
 	}
diff --git a/go/loader/loader_test.go b/go/loader/loader_test.go
index e68405a..e39653c 100644
--- a/go/loader/loader_test.go
+++ b/go/loader/loader_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package loader_test
diff --git a/go/packages/packagestest/modules_111.go b/go/packages/packagestest/modules_111.go
index 61fa969..4b976f6 100644
--- a/go/packages/packagestest/modules_111.go
+++ b/go/packages/packagestest/modules_111.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.11
 // +build go1.11
 
 package packagestest
diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go
index 07a241b..2f6e069 100644
--- a/go/pointer/pointer_test.go
+++ b/go/pointer/pointer_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package pointer_test
diff --git a/go/pointer/stdlib_test.go b/go/pointer/stdlib_test.go
index d3ba721..2d5097f 100644
--- a/go/pointer/stdlib_test.go
+++ b/go/pointer/stdlib_test.go
@@ -4,6 +4,7 @@
 
 // Incomplete source tree on Android.
 
+//go:build !android
 // +build !android
 
 package pointer
diff --git a/go/ssa/identical.go b/go/ssa/identical.go
index f3cc8ac..e802696 100644
--- a/go/ssa/identical.go
+++ b/go/ssa/identical.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.8
 // +build go1.8
 
 package ssa
diff --git a/go/ssa/identical_17.go b/go/ssa/identical_17.go
index faa124f..575aa5d 100644
--- a/go/ssa/identical_17.go
+++ b/go/ssa/identical_17.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.8
 // +build !go1.8
 
 package ssa
diff --git a/go/ssa/identical_test.go b/go/ssa/identical_test.go
index 2fd4ae9..25484a5 100644
--- a/go/ssa/identical_test.go
+++ b/go/ssa/identical_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.8
 // +build go1.8
 
 package ssa_test
diff --git a/go/ssa/ssautil/switch_test.go b/go/ssa/ssautil/switch_test.go
index a47dbef..bad8bdd 100644
--- a/go/ssa/ssautil/switch_test.go
+++ b/go/ssa/ssautil/switch_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package ssautil_test
diff --git a/go/ssa/stdlib_test.go b/go/ssa/stdlib_test.go
index 41b87ff..1c358b0 100644
--- a/go/ssa/stdlib_test.go
+++ b/go/ssa/stdlib_test.go
@@ -4,6 +4,7 @@
 
 // Incomplete source tree on Android.
 
+//go:build !android
 // +build !android
 
 package ssa_test
diff --git a/godoc/godoc17_test.go b/godoc/godoc17_test.go
index d153991..82e23e6 100644
--- a/godoc/godoc17_test.go
+++ b/godoc/godoc17_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.7
 // +build go1.7
 
 package godoc
diff --git a/godoc/static/makestatic.go b/godoc/static/makestatic.go
index 0f910f0..ef7b904 100644
--- a/godoc/static/makestatic.go
+++ b/godoc/static/makestatic.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 // Command makestatic writes the generated file buffer to "static.go".
diff --git a/godoc/vfs/fs.go b/godoc/vfs/fs.go
index b033666..f12d653 100644
--- a/godoc/vfs/fs.go
+++ b/godoc/vfs/fs.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.16
 // +build go1.16
 
 package vfs
diff --git a/gopls/go.mod b/gopls/go.mod
index 2bf051d..a38cac5 100644
--- a/gopls/go.mod
+++ b/gopls/go.mod
@@ -14,3 +14,5 @@
 	mvdan.cc/gofumpt v0.1.0
 	mvdan.cc/xurls/v2 v2.2.0
 )
+
+replace golang.org/x/tools => ../
diff --git a/gopls/internal/hooks/analysis.go b/gopls/internal/hooks/analysis.go
index 6bab2be..23d4ab6 100644
--- a/gopls/internal/hooks/analysis.go
+++ b/gopls/internal/hooks/analysis.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.15
 // +build go1.15
 
 package hooks
diff --git a/gopls/internal/hooks/analysis_115.go b/gopls/internal/hooks/analysis_115.go
index ffb01a4..187e522 100644
--- a/gopls/internal/hooks/analysis_115.go
+++ b/gopls/internal/hooks/analysis_115.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.15
 // +build !go1.15
 
 package hooks
diff --git a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go
index 3a73152..84b8b3d 100644
--- a/gopls/internal/regtest/diagnostics/diagnostics_test.go
+++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go
@@ -16,7 +16,6 @@
 	"golang.org/x/tools/internal/lsp"
 	"golang.org/x/tools/internal/lsp/fake"
 	"golang.org/x/tools/internal/lsp/protocol"
-	"golang.org/x/tools/internal/lsp/tests"
 	"golang.org/x/tools/internal/testenv"
 )
 
@@ -526,7 +525,6 @@
 `
 	Run(t, generated, func(t *testing.T, env *Env) {
 		env.OpenFile("main.go")
-		original := env.ReadWorkspaceFile("main.go")
 		var d protocol.PublishDiagnosticsParams
 		env.Await(
 			OnceMet(
@@ -534,12 +532,8 @@
 				ReadDiagnostics("main.go", &d),
 			),
 		)
-		// Apply fixes and save the buffer.
-		env.ApplyQuickFixes("main.go", d.Diagnostics)
-		env.SaveBuffer("main.go")
-		fixed := env.ReadWorkspaceFile("main.go")
-		if original != fixed {
-			t.Fatalf("generated file was changed by quick fixes:\n%s", tests.Diff(t, original, fixed))
+		if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 {
+			t.Errorf("got quick fixes %v, wanted none", fixes)
 		}
 	})
 }
diff --git a/gopls/internal/regtest/expectation.go b/gopls/internal/regtest/expectation.go
index e520099..02e00dd 100644
--- a/gopls/internal/regtest/expectation.go
+++ b/gopls/internal/regtest/expectation.go
@@ -12,6 +12,7 @@
 	"golang.org/x/tools/internal/lsp"
 	"golang.org/x/tools/internal/lsp/fake"
 	"golang.org/x/tools/internal/lsp/protocol"
+	"golang.org/x/tools/internal/testenv"
 )
 
 // An Expectation asserts that the state of the editor at a point in time
@@ -580,3 +581,16 @@
 func NoDiagnosticWithMessage(name, msg string) DiagnosticExpectation {
 	return DiagnosticExpectation{path: name, message: msg, present: false}
 }
+
+// GoSum asserts that a "go.sum is out of sync" diagnostic for the given module
+// (as formatted in a go.mod file, e.g. "example.com v1.0.0") is present.
+func (e *Env) GoSumDiagnostic(name, module string) Expectation {
+	e.T.Helper()
+	// In 1.16, go.sum diagnostics should appear on the relevant module. Earlier
+	// errors have no information and appear on the module declaration.
+	if testenv.Go1Point() >= 16 {
+		return e.DiagnosticAtRegexpWithMessage(name, module, "go.sum is out of sync")
+	} else {
+		return e.DiagnosticAtRegexpWithMessage(name, `module`, "go.sum is out of sync")
+	}
+}
diff --git a/gopls/internal/regtest/misc/generate_test.go b/gopls/internal/regtest/misc/generate_test.go
index 6987924..a7631d9 100644
--- a/gopls/internal/regtest/misc/generate_test.go
+++ b/gopls/internal/regtest/misc/generate_test.go
@@ -4,6 +4,7 @@
 
 // TODO(rfindley): figure out why go generate fails on android builders.
 
+//go:build !android
 // +build !android
 
 package misc
diff --git a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regtest/modfile/modfile_test.go
index cf7dad1..33b65fe 100644
--- a/gopls/internal/regtest/modfile/modfile_test.go
+++ b/gopls/internal/regtest/modfile/modfile_test.go
@@ -548,6 +548,7 @@
 			d := protocol.PublishDiagnosticsParams{}
 			env.Await(
 				OnceMet(
+					// Make sure the diagnostic mentions the new version -- the old diagnostic is in the same place.
 					env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "example.com@v1.2.3"),
 					ReadDiagnostics("a/go.mod", &d),
 				),
@@ -822,7 +823,7 @@
 		env.OpenFile("go.mod")
 		env.Await(
 			OnceMet(
-				DiagnosticAt("go.mod", 0, 0),
+				env.GoSumDiagnostic("go.mod", `example.com v1.2.3`),
 				ReadDiagnostics("go.mod", d),
 			),
 		)
@@ -1001,9 +1002,7 @@
 }
 
 func TestSumUpdateQuickFix(t *testing.T) {
-	// Error messages changed in 1.16 that changed the diagnostic positions.
-	testenv.NeedsGo1Point(t, 16)
-
+	testenv.NeedsGo1Point(t, 14)
 	const mod = `
 -- go.mod --
 module mod.com
@@ -1030,22 +1029,14 @@
 		Modes(Singleton),
 	).Run(t, mod, func(t *testing.T, env *Env) {
 		env.OpenFile("go.mod")
-		pos := env.RegexpSearch("go.mod", "example.com")
 		params := &protocol.PublishDiagnosticsParams{}
 		env.Await(
 			OnceMet(
-				env.DiagnosticAtRegexp("go.mod", "example.com"),
+				env.GoSumDiagnostic("go.mod", "example.com"),
 				ReadDiagnostics("go.mod", params),
 			),
 		)
-		var diagnostic protocol.Diagnostic
-		for _, d := range params.Diagnostics {
-			if d.Range.Start.Line == uint32(pos.Line) {
-				diagnostic = d
-				break
-			}
-		}
-		env.ApplyQuickFixes("go.mod", []protocol.Diagnostic{diagnostic})
+		env.ApplyQuickFixes("go.mod", params.Diagnostics)
 		const want = `example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
 `
diff --git a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/regtest/workspace/workspace_test.go
index 8712784..f0128d8 100644
--- a/gopls/internal/regtest/workspace/workspace_test.go
+++ b/gopls/internal/regtest/workspace/workspace_test.go
@@ -818,25 +818,25 @@
 		Modes(Experimental),
 	).Run(t, mod, func(t *testing.T, env *Env) {
 		params := &protocol.PublishDiagnosticsParams{}
-		env.OpenFile("a/go.mod")
+		env.OpenFile("b/go.mod")
 		env.Await(
-			ReadDiagnostics("a/go.mod", params),
+			OnceMet(
+				env.GoSumDiagnostic("b/go.mod", `example.com v1.2.3`),
+				ReadDiagnostics("b/go.mod", params),
+			),
 		)
 		for _, d := range params.Diagnostics {
-			if d.Message != `go.sum is out of sync with go.mod. Please update it by applying the quick fix.` {
+			if !strings.Contains(d.Message, "go.sum is out of sync") {
 				continue
 			}
-			actions, err := env.Editor.GetQuickFixes(env.Ctx, "a/go.mod", nil, []protocol.Diagnostic{d})
-			if err != nil {
-				t.Fatal(err)
-			}
+			actions := env.GetQuickFixes("b/go.mod", []protocol.Diagnostic{d})
 			if len(actions) != 2 {
 				t.Fatalf("expected 2 code actions, got %v", len(actions))
 			}
-			env.ApplyQuickFixes("a/go.mod", []protocol.Diagnostic{d})
+			env.ApplyQuickFixes("b/go.mod", []protocol.Diagnostic{d})
 		}
 		env.Await(
-			EmptyDiagnostics("a/go.mod"),
+			EmptyDiagnostics("b/go.mod"),
 		)
 	})
 }
diff --git a/gopls/internal/regtest/wrappers.go b/gopls/internal/regtest/wrappers.go
index fa9367e..c77de5b 100644
--- a/gopls/internal/regtest/wrappers.go
+++ b/gopls/internal/regtest/wrappers.go
@@ -200,6 +200,16 @@
 	}
 }
 
+// GetQuickFixes returns the available quick fix code actions.
+func (e *Env) GetQuickFixes(path string, diagnostics []protocol.Diagnostic) []protocol.CodeAction {
+	e.T.Helper()
+	actions, err := e.Editor.GetQuickFixes(e.Ctx, path, nil, diagnostics)
+	if err != nil {
+		e.T.Fatal(err)
+	}
+	return actions
+}
+
 // Hover in the editor, calling t.Fatal on any error.
 func (e *Env) Hover(name string, pos fake.Pos) (*protocol.MarkupContent, fake.Pos) {
 	e.T.Helper()
@@ -265,6 +275,8 @@
 	}
 }
 
+// DumpGoSum prints the correct go.sum contents for dir in txtar format,
+// for use in creating regtests.
 func (e *Env) DumpGoSum(dir string) {
 	e.T.Helper()
 
diff --git a/internal/fastwalk/fastwalk_dirent_fileno.go b/internal/fastwalk/fastwalk_dirent_fileno.go
index ccffec5..d58595d 100644
--- a/internal/fastwalk/fastwalk_dirent_fileno.go
+++ b/internal/fastwalk/fastwalk_dirent_fileno.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build freebsd || openbsd || netbsd
 // +build freebsd openbsd netbsd
 
 package fastwalk
diff --git a/internal/fastwalk/fastwalk_dirent_ino.go b/internal/fastwalk/fastwalk_dirent_ino.go
index ab7fbc0..ea02b9e 100644
--- a/internal/fastwalk/fastwalk_dirent_ino.go
+++ b/internal/fastwalk/fastwalk_dirent_ino.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build (linux || darwin) && !appengine
 // +build linux darwin
 // +build !appengine
 
diff --git a/internal/fastwalk/fastwalk_dirent_namlen_bsd.go b/internal/fastwalk/fastwalk_dirent_namlen_bsd.go
index a3b26a7..d5c9c32 100644
--- a/internal/fastwalk/fastwalk_dirent_namlen_bsd.go
+++ b/internal/fastwalk/fastwalk_dirent_namlen_bsd.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build darwin || freebsd || openbsd || netbsd
 // +build darwin freebsd openbsd netbsd
 
 package fastwalk
diff --git a/internal/fastwalk/fastwalk_dirent_namlen_linux.go b/internal/fastwalk/fastwalk_dirent_namlen_linux.go
index e880d35..c82e57d 100644
--- a/internal/fastwalk/fastwalk_dirent_namlen_linux.go
+++ b/internal/fastwalk/fastwalk_dirent_namlen_linux.go
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build linux
-// +build !appengine
+//go:build linux && !appengine
+// +build linux,!appengine
 
 package fastwalk
 
diff --git a/internal/fastwalk/fastwalk_portable.go b/internal/fastwalk/fastwalk_portable.go
index b0d6327..085d311 100644
--- a/internal/fastwalk/fastwalk_portable.go
+++ b/internal/fastwalk/fastwalk_portable.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build appengine || (!linux && !darwin && !freebsd && !openbsd && !netbsd)
 // +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd
 
 package fastwalk
diff --git a/internal/fastwalk/fastwalk_unix.go b/internal/fastwalk/fastwalk_unix.go
index 5901a8f..e4edb5c 100644
--- a/internal/fastwalk/fastwalk_unix.go
+++ b/internal/fastwalk/fastwalk_unix.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build (linux || darwin || freebsd || openbsd || netbsd) && !appengine
 // +build linux darwin freebsd openbsd netbsd
 // +build !appengine
 
diff --git a/internal/imports/mkindex.go b/internal/imports/mkindex.go
index ef8c0d2..36a532b 100644
--- a/internal/imports/mkindex.go
+++ b/internal/imports/mkindex.go
@@ -1,3 +1,4 @@
+//go:build ignore
 // +build ignore
 
 // Copyright 2013 The Go Authors. All rights reserved.
diff --git a/internal/imports/mkstdlib.go b/internal/imports/mkstdlib.go
index cf0fc49..f5ea292 100644
--- a/internal/imports/mkstdlib.go
+++ b/internal/imports/mkstdlib.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 // mkstdlib generates the zstdlib.go file, containing the Go standard
@@ -14,7 +15,6 @@
 	"bytes"
 	"fmt"
 	"go/format"
-	exec "golang.org/x/sys/execabs"
 	"io"
 	"io/ioutil"
 	"log"
@@ -23,6 +23,8 @@
 	"regexp"
 	"runtime"
 	"sort"
+
+	exec "golang.org/x/sys/execabs"
 )
 
 func mustOpen(name string) io.Reader {
@@ -66,6 +68,7 @@
 		mustOpen(api("go1.13.txt")),
 		mustOpen(api("go1.14.txt")),
 		mustOpen(api("go1.15.txt")),
+		mustOpen(api("go1.16.txt")),
 
 		// The API of the syscall/js package needs to be computed explicitly,
 		// because it's not included in the GOROOT/api/go1.*.txt files at this time.
diff --git a/internal/imports/zstdlib.go b/internal/imports/zstdlib.go
index 7b573b9..ccdd4e0 100644
--- a/internal/imports/zstdlib.go
+++ b/internal/imports/zstdlib.go
@@ -974,13 +974,29 @@
 		"DF_STATIC_TLS",
 		"DF_SYMBOLIC",
 		"DF_TEXTREL",
+		"DT_ADDRRNGHI",
+		"DT_ADDRRNGLO",
+		"DT_AUDIT",
+		"DT_AUXILIARY",
 		"DT_BIND_NOW",
+		"DT_CHECKSUM",
+		"DT_CONFIG",
 		"DT_DEBUG",
+		"DT_DEPAUDIT",
 		"DT_ENCODING",
+		"DT_FEATURE",
+		"DT_FILTER",
 		"DT_FINI",
 		"DT_FINI_ARRAY",
 		"DT_FINI_ARRAYSZ",
 		"DT_FLAGS",
+		"DT_FLAGS_1",
+		"DT_GNU_CONFLICT",
+		"DT_GNU_CONFLICTSZ",
+		"DT_GNU_HASH",
+		"DT_GNU_LIBLIST",
+		"DT_GNU_LIBLISTSZ",
+		"DT_GNU_PRELINKED",
 		"DT_HASH",
 		"DT_HIOS",
 		"DT_HIPROC",
@@ -990,28 +1006,100 @@
 		"DT_JMPREL",
 		"DT_LOOS",
 		"DT_LOPROC",
+		"DT_MIPS_AUX_DYNAMIC",
+		"DT_MIPS_BASE_ADDRESS",
+		"DT_MIPS_COMPACT_SIZE",
+		"DT_MIPS_CONFLICT",
+		"DT_MIPS_CONFLICTNO",
+		"DT_MIPS_CXX_FLAGS",
+		"DT_MIPS_DELTA_CLASS",
+		"DT_MIPS_DELTA_CLASSSYM",
+		"DT_MIPS_DELTA_CLASSSYM_NO",
+		"DT_MIPS_DELTA_CLASS_NO",
+		"DT_MIPS_DELTA_INSTANCE",
+		"DT_MIPS_DELTA_INSTANCE_NO",
+		"DT_MIPS_DELTA_RELOC",
+		"DT_MIPS_DELTA_RELOC_NO",
+		"DT_MIPS_DELTA_SYM",
+		"DT_MIPS_DELTA_SYM_NO",
+		"DT_MIPS_DYNSTR_ALIGN",
+		"DT_MIPS_FLAGS",
+		"DT_MIPS_GOTSYM",
+		"DT_MIPS_GP_VALUE",
+		"DT_MIPS_HIDDEN_GOTIDX",
+		"DT_MIPS_HIPAGENO",
+		"DT_MIPS_ICHECKSUM",
+		"DT_MIPS_INTERFACE",
+		"DT_MIPS_INTERFACE_SIZE",
+		"DT_MIPS_IVERSION",
+		"DT_MIPS_LIBLIST",
+		"DT_MIPS_LIBLISTNO",
+		"DT_MIPS_LOCALPAGE_GOTIDX",
+		"DT_MIPS_LOCAL_GOTIDX",
+		"DT_MIPS_LOCAL_GOTNO",
+		"DT_MIPS_MSYM",
+		"DT_MIPS_OPTIONS",
+		"DT_MIPS_PERF_SUFFIX",
+		"DT_MIPS_PIXIE_INIT",
+		"DT_MIPS_PLTGOT",
+		"DT_MIPS_PROTECTED_GOTIDX",
+		"DT_MIPS_RLD_MAP",
+		"DT_MIPS_RLD_MAP_REL",
+		"DT_MIPS_RLD_TEXT_RESOLVE_ADDR",
+		"DT_MIPS_RLD_VERSION",
+		"DT_MIPS_RWPLT",
+		"DT_MIPS_SYMBOL_LIB",
+		"DT_MIPS_SYMTABNO",
+		"DT_MIPS_TIME_STAMP",
+		"DT_MIPS_UNREFEXTNO",
+		"DT_MOVEENT",
+		"DT_MOVESZ",
+		"DT_MOVETAB",
 		"DT_NEEDED",
 		"DT_NULL",
 		"DT_PLTGOT",
+		"DT_PLTPAD",
+		"DT_PLTPADSZ",
 		"DT_PLTREL",
 		"DT_PLTRELSZ",
+		"DT_POSFLAG_1",
+		"DT_PPC64_GLINK",
+		"DT_PPC64_OPD",
+		"DT_PPC64_OPDSZ",
+		"DT_PPC64_OPT",
+		"DT_PPC_GOT",
+		"DT_PPC_OPT",
 		"DT_PREINIT_ARRAY",
 		"DT_PREINIT_ARRAYSZ",
 		"DT_REL",
 		"DT_RELA",
+		"DT_RELACOUNT",
 		"DT_RELAENT",
 		"DT_RELASZ",
+		"DT_RELCOUNT",
 		"DT_RELENT",
 		"DT_RELSZ",
 		"DT_RPATH",
 		"DT_RUNPATH",
 		"DT_SONAME",
+		"DT_SPARC_REGISTER",
 		"DT_STRSZ",
 		"DT_STRTAB",
 		"DT_SYMBOLIC",
 		"DT_SYMENT",
+		"DT_SYMINENT",
+		"DT_SYMINFO",
+		"DT_SYMINSZ",
 		"DT_SYMTAB",
+		"DT_SYMTAB_SHNDX",
 		"DT_TEXTREL",
+		"DT_TLSDESC_GOT",
+		"DT_TLSDESC_PLT",
+		"DT_USED",
+		"DT_VALRNGHI",
+		"DT_VALRNGLO",
+		"DT_VERDEF",
+		"DT_VERDEFNUM",
 		"DT_VERNEED",
 		"DT_VERNEEDNUM",
 		"DT_VERSYM",
@@ -1271,17 +1359,38 @@
 		"PF_R",
 		"PF_W",
 		"PF_X",
+		"PT_AARCH64_ARCHEXT",
+		"PT_AARCH64_UNWIND",
+		"PT_ARM_ARCHEXT",
+		"PT_ARM_EXIDX",
 		"PT_DYNAMIC",
+		"PT_GNU_EH_FRAME",
+		"PT_GNU_MBIND_HI",
+		"PT_GNU_MBIND_LO",
+		"PT_GNU_PROPERTY",
+		"PT_GNU_RELRO",
+		"PT_GNU_STACK",
 		"PT_HIOS",
 		"PT_HIPROC",
 		"PT_INTERP",
 		"PT_LOAD",
 		"PT_LOOS",
 		"PT_LOPROC",
+		"PT_MIPS_ABIFLAGS",
+		"PT_MIPS_OPTIONS",
+		"PT_MIPS_REGINFO",
+		"PT_MIPS_RTPROC",
 		"PT_NOTE",
 		"PT_NULL",
+		"PT_OPENBSD_BOOTDATA",
+		"PT_OPENBSD_RANDOMIZE",
+		"PT_OPENBSD_WXNEEDED",
+		"PT_PAX_FLAGS",
 		"PT_PHDR",
+		"PT_S390_PGSTE",
 		"PT_SHLIB",
+		"PT_SUNWSTACK",
+		"PT_SUNW_EH_FRAME",
 		"PT_TLS",
 		"Prog",
 		"Prog32",
@@ -2445,6 +2554,9 @@
 		"SectionHeader",
 		"Sym",
 	},
+	"embed": []string{
+		"FS",
+	},
 	"encoding": []string{
 		"BinaryMarshaler",
 		"BinaryUnmarshaler",
@@ -2680,6 +2792,7 @@
 		"FlagSet",
 		"Float64",
 		"Float64Var",
+		"Func",
 		"Getter",
 		"Int",
 		"Int64",
@@ -2853,6 +2966,18 @@
 		"Package",
 		"ToolDir",
 	},
+	"go/build/constraint": []string{
+		"AndExpr",
+		"Expr",
+		"IsGoBuild",
+		"IsPlusBuild",
+		"NotExpr",
+		"OrExpr",
+		"Parse",
+		"PlusBuildLines",
+		"SyntaxError",
+		"TagExpr",
+	},
 	"go/constant": []string{
 		"BinaryOp",
 		"BitLen",
@@ -3273,6 +3398,7 @@
 		"Must",
 		"New",
 		"OK",
+		"ParseFS",
 		"ParseFiles",
 		"ParseGlob",
 		"Srcset",
@@ -3432,6 +3558,7 @@
 		"Copy",
 		"CopyBuffer",
 		"CopyN",
+		"Discard",
 		"EOF",
 		"ErrClosedPipe",
 		"ErrNoProgress",
@@ -3443,12 +3570,15 @@
 		"MultiReader",
 		"MultiWriter",
 		"NewSectionReader",
+		"NopCloser",
 		"Pipe",
 		"PipeReader",
 		"PipeWriter",
+		"ReadAll",
 		"ReadAtLeast",
 		"ReadCloser",
 		"ReadFull",
+		"ReadSeekCloser",
 		"ReadSeeker",
 		"ReadWriteCloser",
 		"ReadWriteSeeker",
@@ -3472,6 +3602,49 @@
 		"WriterAt",
 		"WriterTo",
 	},
+	"io/fs": []string{
+		"DirEntry",
+		"ErrClosed",
+		"ErrExist",
+		"ErrInvalid",
+		"ErrNotExist",
+		"ErrPermission",
+		"FS",
+		"File",
+		"FileInfo",
+		"FileMode",
+		"Glob",
+		"GlobFS",
+		"ModeAppend",
+		"ModeCharDevice",
+		"ModeDevice",
+		"ModeDir",
+		"ModeExclusive",
+		"ModeIrregular",
+		"ModeNamedPipe",
+		"ModePerm",
+		"ModeSetgid",
+		"ModeSetuid",
+		"ModeSocket",
+		"ModeSticky",
+		"ModeSymlink",
+		"ModeTemporary",
+		"ModeType",
+		"PathError",
+		"ReadDir",
+		"ReadDirFS",
+		"ReadDirFile",
+		"ReadFile",
+		"ReadFileFS",
+		"SkipDir",
+		"Stat",
+		"StatFS",
+		"Sub",
+		"SubFS",
+		"ValidPath",
+		"WalkDir",
+		"WalkDirFunc",
+	},
 	"io/ioutil": []string{
 		"Discard",
 		"NopCloser",
@@ -3483,6 +3656,7 @@
 		"WriteFile",
 	},
 	"log": []string{
+		"Default",
 		"Fatal",
 		"Fatalf",
 		"Fatalln",
@@ -3819,6 +3993,7 @@
 		"DialUDP",
 		"DialUnix",
 		"Dialer",
+		"ErrClosed",
 		"ErrWriteToConnected",
 		"Error",
 		"FileConn",
@@ -3938,6 +4113,7 @@
 		"ErrUseLastResponse",
 		"ErrWriteAfterFlush",
 		"Error",
+		"FS",
 		"File",
 		"FileServer",
 		"FileSystem",
@@ -4234,7 +4410,10 @@
 		"Chtimes",
 		"Clearenv",
 		"Create",
+		"CreateTemp",
 		"DevNull",
+		"DirEntry",
+		"DirFS",
 		"Environ",
 		"ErrClosed",
 		"ErrDeadlineExceeded",
@@ -4243,6 +4422,7 @@
 		"ErrNoDeadline",
 		"ErrNotExist",
 		"ErrPermission",
+		"ErrProcessDone",
 		"Executable",
 		"Exit",
 		"Expand",
@@ -4276,6 +4456,7 @@
 		"Lstat",
 		"Mkdir",
 		"MkdirAll",
+		"MkdirTemp",
 		"ModeAppend",
 		"ModeCharDevice",
 		"ModeDevice",
@@ -4310,6 +4491,8 @@
 		"ProcAttr",
 		"Process",
 		"ProcessState",
+		"ReadDir",
+		"ReadFile",
 		"Readlink",
 		"Remove",
 		"RemoveAll",
@@ -4333,6 +4516,7 @@
 		"UserCacheDir",
 		"UserConfigDir",
 		"UserHomeDir",
+		"WriteFile",
 	},
 	"os/exec": []string{
 		"Cmd",
@@ -4347,6 +4531,7 @@
 		"Ignore",
 		"Ignored",
 		"Notify",
+		"NotifyContext",
 		"Reset",
 		"Stop",
 	},
@@ -4397,6 +4582,7 @@
 		"ToSlash",
 		"VolumeName",
 		"Walk",
+		"WalkDir",
 		"WalkFunc",
 	},
 	"plugin": []string{
@@ -4629,6 +4815,19 @@
 		"Stack",
 		"WriteHeapDump",
 	},
+	"runtime/metrics": []string{
+		"All",
+		"Description",
+		"Float64Histogram",
+		"KindBad",
+		"KindFloat64",
+		"KindFloat64Histogram",
+		"KindUint64",
+		"Read",
+		"Sample",
+		"Value",
+		"ValueKind",
+	},
 	"runtime/pprof": []string{
 		"Do",
 		"ForLabels",
@@ -5012,6 +5211,8 @@
 		"AddrinfoW",
 		"Adjtime",
 		"Adjtimex",
+		"AllThreadsSyscall",
+		"AllThreadsSyscall6",
 		"AttachLsf",
 		"B0",
 		"B1000000",
@@ -10010,13 +10211,20 @@
 		"TB",
 		"Verbose",
 	},
+	"testing/fstest": []string{
+		"MapFS",
+		"MapFile",
+		"TestFS",
+	},
 	"testing/iotest": []string{
 		"DataErrReader",
+		"ErrReader",
 		"ErrTimeout",
 		"HalfReader",
 		"NewReadLogger",
 		"NewWriteLogger",
 		"OneByteReader",
+		"TestReader",
 		"TimeoutReader",
 		"TruncateWriter",
 	},
@@ -10076,6 +10284,7 @@
 		"JSEscaper",
 		"Must",
 		"New",
+		"ParseFS",
 		"ParseFiles",
 		"ParseGlob",
 		"Template",
@@ -10087,12 +10296,14 @@
 		"BranchNode",
 		"ChainNode",
 		"CommandNode",
+		"CommentNode",
 		"DotNode",
 		"FieldNode",
 		"IdentifierNode",
 		"IfNode",
 		"IsEmptyTree",
 		"ListNode",
+		"Mode",
 		"New",
 		"NewIdentifier",
 		"NilNode",
@@ -10101,6 +10312,7 @@
 		"NodeBool",
 		"NodeChain",
 		"NodeCommand",
+		"NodeComment",
 		"NodeDot",
 		"NodeField",
 		"NodeIdentifier",
@@ -10118,6 +10330,7 @@
 		"NodeWith",
 		"NumberNode",
 		"Parse",
+		"ParseComments",
 		"PipeNode",
 		"Pos",
 		"RangeNode",
@@ -10230,6 +10443,7 @@
 		"Chakma",
 		"Cham",
 		"Cherokee",
+		"Chorasmian",
 		"Co",
 		"Common",
 		"Coptic",
@@ -10243,6 +10457,7 @@
 		"Devanagari",
 		"Diacritic",
 		"Digit",
+		"Dives_Akuru",
 		"Dogra",
 		"Duployan",
 		"Egyptian_Hieroglyphs",
@@ -10300,6 +10515,7 @@
 		"Katakana",
 		"Kayah_Li",
 		"Kharoshthi",
+		"Khitan_Small_Script",
 		"Khmer",
 		"Khojki",
 		"Khudawadi",
@@ -10472,6 +10688,7 @@
 		"Wancho",
 		"Warang_Citi",
 		"White_Space",
+		"Yezidi",
 		"Yi",
 		"Z",
 		"Zanabazar_Square",
diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go
index 529a420..148fbda 100644
--- a/internal/lsp/cache/errors.go
+++ b/internal/lsp/cache/errors.go
@@ -14,6 +14,8 @@
 	"strconv"
 	"strings"
 
+	errors "golang.org/x/xerrors"
+
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/internal/analysisinternal"
@@ -23,7 +25,6 @@
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/span"
 	"golang.org/x/tools/internal/typesinternal"
-	errors "golang.org/x/xerrors"
 )
 
 func sourceDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, severity protocol.DiagnosticSeverity, e interface{}) ([]*source.Diagnostic, error) {
@@ -167,7 +168,7 @@
 
 func typesCodeHref(snapshot *snapshot, code typesinternal.ErrorCode) string {
 	target := snapshot.View().Options().LinkTarget
-	return fmt.Sprintf("%s/golang.org/x/tools/internal/typesinternal#%s", target, code.String())
+	return fmt.Sprintf("https://%s/golang.org/x/tools/internal/typesinternal#%s", target, code.String())
 }
 
 func suggestedAnalysisFixes(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic) ([]source.SuggestedFix, error) {
diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go
index 9112bfd..2e1917a 100644
--- a/internal/lsp/cache/mod.go
+++ b/internal/lsp/cache/mod.go
@@ -60,13 +60,26 @@
 			Converter: span.NewContentConverter(modFH.URI().Filename(), contents),
 			Content:   contents,
 		}
-		file, err := modfile.Parse(modFH.URI().Filename(), contents, nil)
-
+		file, parseErr := modfile.Parse(modFH.URI().Filename(), contents, nil)
 		// Attempt to convert the error to a standardized parse error.
 		var parseErrors []*source.Diagnostic
-		if err != nil {
-			if parseErr := extractErrorWithPosition(ctx, err.Error(), s); parseErr != nil {
-				parseErrors = []*source.Diagnostic{parseErr}
+		if parseErr != nil {
+			mfErrList, ok := parseErr.(modfile.ErrorList)
+			if !ok {
+				return &parseModData{err: fmt.Errorf("unexpected parse error type %v", parseErr)}
+			}
+			for _, mfErr := range mfErrList {
+				rng, err := rangeFromPositions(m, mfErr.Pos, mfErr.Pos)
+				if err != nil {
+					return &parseModData{err: err}
+				}
+				parseErrors = []*source.Diagnostic{{
+					URI:      modFH.URI(),
+					Range:    rng,
+					Severity: protocol.SeverityError,
+					Source:   source.ParseError,
+					Message:  mfErr.Err.Error(),
+				}}
 			}
 		}
 		return &parseModData{
@@ -76,7 +89,7 @@
 				File:        file,
 				ParseErrors: parseErrors,
 			},
-			err: err,
+			err: parseErr,
 		}
 	}, nil)
 
@@ -214,46 +227,67 @@
 
 // extractGoCommandError tries to parse errors that come from the go command
 // and shape them into go.mod diagnostics.
-func (s *snapshot) extractGoCommandErrors(ctx context.Context, snapshot source.Snapshot, goCmdError string) []*source.Diagnostic {
-	var srcErrs []*source.Diagnostic
-	if srcErr := s.parseModError(ctx, goCmdError); srcErr != nil {
-		srcErrs = append(srcErrs, srcErr...)
+func (s *snapshot) extractGoCommandErrors(ctx context.Context, goCmdError string) ([]*source.Diagnostic, error) {
+	diagLocations := map[*source.ParsedModule]span.Span{}
+	backupDiagLocations := map[*source.ParsedModule]span.Span{}
+
+	// The go command emits parse errors for completely invalid go.mod files.
+	// Those are reported by our own diagnostics and can be ignored here.
+	// As of writing, we are not aware of any other errors that include
+	// file/position information, so don't even try to find it.
+	if strings.Contains(goCmdError, "errors parsing go.mod") {
+		return nil, nil
 	}
-	if srcErr := extractErrorWithPosition(ctx, goCmdError, s); srcErr != nil {
-		srcErrs = append(srcErrs, srcErr)
-	} else {
-		for _, uri := range s.ModFiles() {
-			fh, err := s.GetFile(ctx, uri)
-			if err != nil {
-				continue
-			}
-			if srcErr := s.matchErrorToModule(ctx, fh, goCmdError); srcErr != nil {
-				srcErrs = append(srcErrs, srcErr)
-			}
+
+	// Match the error against all the mod files in the workspace.
+	for _, uri := range s.ModFiles() {
+		fh, err := s.GetFile(ctx, uri)
+		if err != nil {
+			return nil, err
+		}
+		pm, err := s.ParseMod(ctx, fh)
+		if err != nil {
+			return nil, err
+		}
+		spn, found, err := s.matchErrorToModule(ctx, pm, goCmdError)
+		if err != nil {
+			return nil, err
+		}
+		if found {
+			diagLocations[pm] = spn
+		} else {
+			backupDiagLocations[pm] = spn
 		}
 	}
-	return srcErrs
+
+	// If we didn't find any good matches, assign diagnostics to all go.mod files.
+	if len(diagLocations) == 0 {
+		diagLocations = backupDiagLocations
+	}
+
+	var srcErrs []*source.Diagnostic
+	for pm, spn := range diagLocations {
+		diag, err := s.goCommandDiagnostic(pm, spn, goCmdError)
+		if err != nil {
+			return nil, err
+		}
+		srcErrs = append(srcErrs, diag)
+	}
+	return srcErrs, nil
 }
 
 var moduleVersionInErrorRe = regexp.MustCompile(`[:\s]([+-._~0-9A-Za-z]+)@([+-._~0-9A-Za-z]+)[:\s]`)
 
-// matchErrorToModule attempts to match module version in error messages.
+// matchErrorToModule matches a go command error message to a go.mod file.
 // Some examples:
 //
 //    example.com@v1.2.2: reading example.com/@v/v1.2.2.mod: no such file or directory
 //    go: github.com/cockroachdb/apd/v2@v2.0.72: reading github.com/cockroachdb/apd/go.mod at revision v2.0.72: unknown revision v2.0.72
 //    go: example.com@v1.2.3 requires\n\trandom.org@v1.2.3: parsing go.mod:\n\tmodule declares its path as: bob.org\n\tbut was required as: random.org
 //
-// We search for module@version, starting from the end to find the most
-// relevant module, e.g. random.org@v1.2.3 above. Then we associate the error
-// with a directive that references any of the modules mentioned.
-func (s *snapshot) matchErrorToModule(ctx context.Context, fh source.FileHandle, goCmdError string) *source.Diagnostic {
-	pm, err := s.ParseMod(ctx, fh)
-	if err != nil {
-		return nil
-	}
-
-	var innermost *module.Version
+// It returns the location of a reference to the one of the modules and true
+// if one exists. If none is found it returns a fallback location and false.
+func (s *snapshot) matchErrorToModule(ctx context.Context, pm *source.ParsedModule, goCmdError string) (span.Span, bool, error) {
 	var reference *modfile.Line
 	matches := moduleVersionInErrorRe.FindAllStringSubmatch(goCmdError, -1)
 
@@ -267,9 +301,6 @@
 		if err := module.Check(ver.Path, ver.Version); err != nil {
 			continue
 		}
-		if innermost == nil {
-			innermost = &ver
-		}
 		reference = findModuleReference(pm.File, ver)
 		if reference != nil {
 			break
@@ -278,52 +309,112 @@
 
 	if reference == nil {
 		// No match for the module path was found in the go.mod file.
-		// Show the error on the module declaration, if one exists.
+		// Show the error on the module declaration, if one exists, or
+		// just the first line of the file.
 		if pm.File.Module == nil {
-			return nil
+			return span.New(pm.URI, span.NewPoint(1, 1, 0), span.Point{}), false, nil
 		}
-		reference = pm.File.Module.Syntax
+		spn, err := spanFromPositions(pm.Mapper, pm.File.Module.Syntax.Start, pm.File.Module.Syntax.End)
+		return spn, false, err
 	}
 
-	rng, err := rangeFromPositions(pm.Mapper, reference.Start, reference.End)
+	spn, err := spanFromPositions(pm.Mapper, reference.Start, reference.End)
+	return spn, true, err
+}
+
+// goCommandDiagnostic creates a diagnostic for a given go command error.
+func (s *snapshot) goCommandDiagnostic(pm *source.ParsedModule, spn span.Span, goCmdError string) (*source.Diagnostic, error) {
+	rng, err := pm.Mapper.Range(spn)
 	if err != nil {
-		return nil
+		return nil, err
 	}
-	disabledByGOPROXY := strings.Contains(goCmdError, "disabled by GOPROXY=off")
-	shouldAddDep := strings.Contains(goCmdError, "to add it")
-	if innermost != nil && (disabledByGOPROXY || shouldAddDep) {
+
+	matches := moduleVersionInErrorRe.FindAllStringSubmatch(goCmdError, -1)
+	var innermost *module.Version
+	for i := len(matches) - 1; i >= 0; i-- {
+		ver := module.Version{Path: matches[i][1], Version: matches[i][2]}
+		// Any module versions that come from the workspace module should not
+		// be shown to the user.
+		if source.IsWorkspaceModuleVersion(ver.Version) {
+			continue
+		}
+		if err := module.Check(ver.Path, ver.Version); err != nil {
+			continue
+		}
+		innermost = &ver
+		break
+	}
+
+	switch {
+	case strings.Contains(goCmdError, "inconsistent vendoring"):
+		cmd, err := command.NewVendorCommand("Run go mod vendor", command.URIArg{URI: protocol.URIFromSpanURI(pm.URI)})
+		if err != nil {
+			return nil, err
+		}
+		return &source.Diagnostic{
+			URI:      pm.URI,
+			Range:    rng,
+			Severity: protocol.SeverityError,
+			Source:   source.ListError,
+			Message: `Inconsistent vendoring detected. Please re-run "go mod vendor".
+See https://github.com/golang/go/issues/39164 for more detail on this issue.`,
+			SuggestedFixes: []source.SuggestedFix{source.SuggestedFixFromCommand(cmd)},
+		}, nil
+
+	case strings.Contains(goCmdError, "updates to go.sum needed"), strings.Contains(goCmdError, "missing go.sum entry"):
+		var args []protocol.DocumentURI
+		for _, uri := range s.ModFiles() {
+			args = append(args, protocol.URIFromSpanURI(uri))
+		}
+		tidyCmd, err := command.NewTidyCommand("Run go mod tidy", command.URIArgs{URIs: args})
+		if err != nil {
+			return nil, err
+		}
+		updateCmd, err := command.NewUpdateGoSumCommand("Update go.sum", command.URIArgs{URIs: args})
+		if err != nil {
+			return nil, err
+		}
+		msg := "go.sum is out of sync with go.mod. Please update it by applying the quick fix."
+		if innermost != nil {
+			msg = fmt.Sprintf("go.sum is out of sync with go.mod: entry for %v is missing. Please updating it by applying the quick fix.", innermost)
+		}
+		return &source.Diagnostic{
+			URI:      pm.URI,
+			Range:    rng,
+			Severity: protocol.SeverityError,
+			Source:   source.ListError,
+			Message:  msg,
+			SuggestedFixes: []source.SuggestedFix{
+				source.SuggestedFixFromCommand(tidyCmd),
+				source.SuggestedFixFromCommand(updateCmd),
+			},
+		}, nil
+	case strings.Contains(goCmdError, "disabled by GOPROXY=off") && innermost != nil:
 		title := fmt.Sprintf("Download %v@%v", innermost.Path, innermost.Version)
 		cmd, err := command.NewAddDependencyCommand(title, command.DependencyArgs{
-			URI:        protocol.URIFromSpanURI(fh.URI()),
+			URI:        protocol.URIFromSpanURI(pm.URI),
 			AddRequire: false,
 			GoCmdArgs:  []string{fmt.Sprintf("%v@%v", innermost.Path, innermost.Version)},
 		})
 		if err != nil {
-			return nil
-		}
-		msg := goCmdError
-		if disabledByGOPROXY {
-			msg = fmt.Sprintf("%v@%v has not been downloaded", innermost.Path, innermost.Version)
+			return nil, err
 		}
 		return &source.Diagnostic{
-			URI:            fh.URI(),
+			URI:            pm.URI,
 			Range:          rng,
 			Severity:       protocol.SeverityError,
-			Message:        msg,
+			Message:        fmt.Sprintf("%v@%v has not been downloaded", innermost.Path, innermost.Version),
 			Source:         source.ListError,
 			SuggestedFixes: []source.SuggestedFix{source.SuggestedFixFromCommand(cmd)},
-		}
-	}
-	diagSource := source.ListError
-	if fh != nil {
-		diagSource = source.ParseError
-	}
-	return &source.Diagnostic{
-		URI:      fh.URI(),
-		Range:    rng,
-		Severity: protocol.SeverityError,
-		Source:   diagSource,
-		Message:  goCmdError,
+		}, nil
+	default:
+		return &source.Diagnostic{
+			URI:      pm.URI,
+			Range:    rng,
+			Severity: protocol.SeverityError,
+			Source:   source.ListError,
+			Message:  goCmdError,
+		}, nil
 	}
 }
 
@@ -345,56 +436,3 @@
 	}
 	return nil
 }
-
-// errorPositionRe matches errors messages of the form <filename>:<line>:<col>,
-// where the <col> is optional.
-var errorPositionRe = regexp.MustCompile(`(?P<pos>.*:([\d]+)(:([\d]+))?): (?P<msg>.+)`)
-
-// extractErrorWithPosition returns a structured error with position
-// information for the given unstructured error. If a file handle is provided,
-// the error position will be on that file. This is useful for parse errors,
-// where we already know the file with the error.
-func extractErrorWithPosition(ctx context.Context, goCmdError string, src source.FileSource) *source.Diagnostic {
-	matches := errorPositionRe.FindStringSubmatch(strings.TrimSpace(goCmdError))
-	if len(matches) == 0 {
-		return nil
-	}
-	var pos, msg string
-	for i, name := range errorPositionRe.SubexpNames() {
-		if name == "pos" {
-			pos = matches[i]
-		}
-		if name == "msg" {
-			msg = matches[i]
-		}
-	}
-	spn := span.Parse(pos)
-	fh, err := src.GetFile(ctx, spn.URI())
-	if err != nil {
-		return nil
-	}
-	content, err := fh.Read()
-	if err != nil {
-		return nil
-	}
-	m := &protocol.ColumnMapper{
-		URI:       spn.URI(),
-		Converter: span.NewContentConverter(spn.URI().Filename(), content),
-		Content:   content,
-	}
-	rng, err := m.Range(spn)
-	if err != nil {
-		return nil
-	}
-	diagSource := source.ListError
-	if fh != nil {
-		diagSource = source.ParseError
-	}
-	return &source.Diagnostic{
-		URI:      spn.URI(),
-		Range:    rng,
-		Severity: protocol.SeverityError,
-		Source:   diagSource,
-		Message:  msg,
-	}
-}
diff --git a/internal/lsp/cache/mod_tidy.go b/internal/lsp/cache/mod_tidy.go
index 1b7ef52..6891a39 100644
--- a/internal/lsp/cache/mod_tidy.go
+++ b/internal/lsp/cache/mod_tidy.go
@@ -151,74 +151,6 @@
 
 	return mth.tidy(ctx, s)
 }
-func (s *snapshot) parseModError(ctx context.Context, errText string) []*source.Diagnostic {
-	// Match on common error messages. This is really hacky, but I'm not sure
-	// of any better way. This can be removed when golang/go#39164 is resolved.
-	isInconsistentVendor := strings.Contains(errText, "inconsistent vendoring")
-	isGoSumUpdates := strings.Contains(errText, "updates to go.sum needed") || strings.Contains(errText, "missing go.sum entry")
-
-	if !isInconsistentVendor && !isGoSumUpdates {
-		return nil
-	}
-
-	switch {
-	case isInconsistentVendor:
-		uri := s.ModFiles()[0]
-		args := command.URIArg{URI: protocol.URIFromSpanURI(uri)}
-		rng, err := s.uriToModDecl(ctx, uri)
-		if err != nil {
-			return nil
-		}
-		cmd, err := command.NewVendorCommand("Run go mod vendor", args)
-		if err != nil {
-			return nil
-		}
-		return []*source.Diagnostic{{
-			URI:      uri,
-			Range:    rng,
-			Severity: protocol.SeverityError,
-			Source:   source.ListError,
-			Message: `Inconsistent vendoring detected. Please re-run "go mod vendor".
-See https://github.com/golang/go/issues/39164 for more detail on this issue.`,
-			SuggestedFixes: []source.SuggestedFix{source.SuggestedFixFromCommand(cmd)},
-		}}
-
-	case isGoSumUpdates:
-		var args []protocol.DocumentURI
-		for _, uri := range s.ModFiles() {
-			args = append(args, protocol.URIFromSpanURI(uri))
-		}
-		var diagnostics []*source.Diagnostic
-		for _, uri := range s.ModFiles() {
-			rng, err := s.uriToModDecl(ctx, uri)
-			if err != nil {
-				return nil
-			}
-			tidyCmd, err := command.NewTidyCommand("Run go mod tidy", command.URIArgs{URIs: args})
-			if err != nil {
-				return nil
-			}
-			updateCmd, err := command.NewUpdateGoSumCommand("Update go.sum", command.URIArgs{URIs: args})
-			if err != nil {
-				return nil
-			}
-			diagnostics = append(diagnostics, &source.Diagnostic{
-				URI:      uri,
-				Range:    rng,
-				Severity: protocol.SeverityError,
-				Source:   source.ListError,
-				Message:  `go.sum is out of sync with go.mod. Please update it by applying the quick fix.`,
-				SuggestedFixes: []source.SuggestedFix{
-					source.SuggestedFixFromCommand(tidyCmd),
-					source.SuggestedFixFromCommand(updateCmd),
-				},
-			})
-		}
-		return diagnostics
-	default:
-		return nil
-	}
-}
 
 func (s *snapshot) uriToModDecl(ctx context.Context, uri span.URI) (protocol.Range, error) {
 	fh, err := s.GetFile(ctx, uri)
@@ -546,6 +478,14 @@
 }
 
 func rangeFromPositions(m *protocol.ColumnMapper, s, e modfile.Position) (protocol.Range, error) {
+	spn, err := spanFromPositions(m, s, e)
+	if err != nil {
+		return protocol.Range{}, err
+	}
+	return m.Range(spn)
+}
+
+func spanFromPositions(m *protocol.ColumnMapper, s, e modfile.Position) (span.Span, error) {
 	toPoint := func(offset int) (span.Point, error) {
 		l, c, err := m.Converter.ToPosition(offset)
 		if err != nil {
@@ -555,11 +495,11 @@
 	}
 	start, err := toPoint(s.Byte)
 	if err != nil {
-		return protocol.Range{}, err
+		return span.Span{}, err
 	}
 	end, err := toPoint(e.Byte)
 	if err != nil {
-		return protocol.Range{}, err
+		return span.Span{}, err
 	}
-	return m.Range(span.New(m.URI, start, end))
+	return span.New(m.URI, start, end), nil
 }
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 0f44cab..ddfefc7 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -1022,22 +1022,22 @@
 }
 
 func (s *snapshot) awaitLoaded(ctx context.Context) error {
-	err := s.awaitLoadedAllErrors(ctx)
+	loadErr := s.awaitLoadedAllErrors(ctx)
 
 	// If we still have absolutely no metadata, check if the view failed to
 	// initialize and return any errors.
 	// TODO(rstambler): Should we clear the error after we return it?
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	if len(s.metadata) == 0 {
-		return err
+	if len(s.metadata) == 0 && loadErr != nil {
+		return loadErr.MainError
 	}
 	return nil
 }
 
 func (s *snapshot) GetCriticalError(ctx context.Context) *source.CriticalError {
 	loadErr := s.awaitLoadedAllErrors(ctx)
-	if errors.Is(loadErr, context.Canceled) {
+	if loadErr != nil && errors.Is(loadErr.MainError, context.Canceled) {
 		return nil
 	}
 
@@ -1060,14 +1060,10 @@
 		return nil
 	}
 
-	if strings.Contains(loadErr.Error(), "cannot find main module") {
+	if strings.Contains(loadErr.MainError.Error(), "cannot find main module") {
 		return s.workspaceLayoutError(ctx)
 	}
-	criticalErr := &source.CriticalError{
-		MainError: loadErr,
-		DiagList:  s.extractGoCommandErrors(ctx, s, loadErr.Error()),
-	}
-	return criticalErr
+	return loadErr
 }
 
 const adHocPackagesWarning = `You are outside of a module and outside of $GOPATH/src.
@@ -1095,27 +1091,32 @@
 	return false
 }
 
-func (s *snapshot) awaitLoadedAllErrors(ctx context.Context) error {
+func (s *snapshot) awaitLoadedAllErrors(ctx context.Context) *source.CriticalError {
 	// Do not return results until the snapshot's view has been initialized.
 	s.AwaitInitialized(ctx)
 
 	if ctx.Err() != nil {
-		return ctx.Err()
+		return &source.CriticalError{MainError: ctx.Err()}
 	}
 
 	if err := s.reloadWorkspace(ctx); err != nil {
-		return err
+		diags, _ := s.extractGoCommandErrors(ctx, err.Error())
+		return &source.CriticalError{
+			MainError: err,
+			DiagList:  diags,
+		}
 	}
 	if err := s.reloadOrphanedFiles(ctx); err != nil {
-		return err
+		diags, _ := s.extractGoCommandErrors(ctx, err.Error())
+		return &source.CriticalError{
+			MainError: err,
+			DiagList:  diags,
+		}
 	}
 	// TODO(rstambler): Should we be more careful about returning the
 	// initialization error? Is it possible for the initialization error to be
 	// corrected without a successful reinitialization?
-	if s.initializedErr == nil {
-		return nil
-	}
-	return s.initializedErr.MainError
+	return s.initializedErr
 }
 
 func (s *snapshot) AwaitInitialized(ctx context.Context) {
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index e71a84c..64a9e7e 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -573,15 +573,15 @@
 		}
 		if err != nil {
 			event.Error(ctx, "initial workspace load failed", err)
-			if modDiagnostics != nil {
-				s.initializedErr = &source.CriticalError{
-					MainError: errors.Errorf("errors loading modules: %v: %w", err, modDiagnostics),
-					DiagList:  modDiagnostics,
-				}
-			} else {
-				s.initializedErr = &source.CriticalError{
-					MainError: err,
-				}
+			extractedDiags, _ := s.extractGoCommandErrors(ctx, err.Error())
+			s.initializedErr = &source.CriticalError{
+				MainError: err,
+				DiagList:  append(modDiagnostics, extractedDiags...),
+			}
+		} else if len(modDiagnostics) != 0 {
+			s.initializedErr = &source.CriticalError{
+				MainError: fmt.Errorf("error loading module names"),
+				DiagList:  modDiagnostics,
 			}
 		} else {
 			// Clear out the initialization error, in case it had been set
diff --git a/internal/lsp/cache/view_test.go b/internal/lsp/cache/view_test.go
index fb788f4..cb57182 100644
--- a/internal/lsp/cache/view_test.go
+++ b/internal/lsp/cache/view_test.go
@@ -8,12 +8,9 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"strings"
 	"testing"
 
-	"golang.org/x/mod/modfile"
 	"golang.org/x/tools/internal/lsp/fake"
-	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/span"
 )
@@ -106,88 +103,6 @@
 	}
 }
 
-// This tests the logic used to extract positions from parse and other Go
-// command errors.
-func TestExtractPositionFromError(t *testing.T) {
-	workspace := `
--- a/go.mod --
-modul a.com
--- b/go.mod --
-module b.com
-
-go 1.12.hello
--- c/go.mod --
-module c.com
-
-require a.com master
-`
-	dir, err := fake.Tempdir(workspace)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(dir)
-
-	tests := []struct {
-		filename string
-		wantRng  protocol.Range
-	}{
-		{
-			filename: "a/go.mod",
-			wantRng:  protocol.Range{},
-		},
-		{
-			filename: "b/go.mod",
-			wantRng: protocol.Range{
-				Start: protocol.Position{Line: 2},
-				End:   protocol.Position{Line: 2},
-			},
-		},
-		{
-			filename: "c/go.mod",
-			wantRng: protocol.Range{
-				Start: protocol.Position{Line: 2},
-				End:   protocol.Position{Line: 2},
-			},
-		},
-	}
-	for _, test := range tests {
-		ctx := context.Background()
-		rel := fake.RelativeTo(dir)
-		uri := span.URIFromPath(rel.AbsPath(test.filename))
-		if source.DetectLanguage("", uri.Filename()) != source.Mod {
-			t.Fatalf("expected only go.mod files")
-		}
-		// Try directly parsing the given, invalid go.mod file. Then, extract a
-		// position from the error message.
-		src := &osFileSource{}
-		modFH, err := src.GetFile(ctx, uri)
-		if err != nil {
-			t.Fatal(err)
-		}
-		content, err := modFH.Read()
-		if err != nil {
-			t.Fatal(err)
-		}
-		_, parseErr := modfile.Parse(uri.Filename(), content, nil)
-		if parseErr == nil {
-			t.Fatalf("%s: expected an unparseable go.mod file", uri.Filename())
-		}
-		srcErr := extractErrorWithPosition(ctx, parseErr.Error(), src)
-		if srcErr == nil {
-			t.Fatalf("unable to extract positions from %v", parseErr.Error())
-		}
-		if srcErr.URI != uri {
-			t.Errorf("unexpected URI: got %s, wanted %s", srcErr.URI, uri)
-		}
-		if protocol.CompareRange(test.wantRng, srcErr.Range) != 0 {
-			t.Errorf("unexpected range: got %s, wanted %s", srcErr.Range, test.wantRng)
-		}
-		if !strings.HasSuffix(parseErr.Error(), srcErr.Message) {
-			t.Errorf("unexpected message: got %s, wanted %s", srcErr.Message, parseErr)
-		}
-	}
-}
-
 func TestInVendor(t *testing.T) {
 	for _, tt := range []struct {
 		path     string
diff --git a/internal/lsp/command/command_gen.go b/internal/lsp/command/command_gen.go
index aca505c..1871c9d 100644
--- a/internal/lsp/command/command_gen.go
+++ b/internal/lsp/command/command_gen.go
@@ -4,6 +4,7 @@
 
 // Don't include this file during code generation, or it will break the build
 // if existing interface methods have been modified.
+//go:build !generate
 // +build !generate
 
 package command
diff --git a/internal/lsp/command/generate/generate.go b/internal/lsp/command/gen/gen.go
similarity index 96%
rename from internal/lsp/command/generate/generate.go
rename to internal/lsp/command/gen/gen.go
index d76ff6f..3934f1a 100644
--- a/internal/lsp/command/generate/generate.go
+++ b/internal/lsp/command/gen/gen.go
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package generate is used to generate command bindings from the gopls command
+// Package gen is used to generate command bindings from the gopls command
 // interface.
-package generate
+package gen
 
 import (
 	"bytes"
@@ -22,6 +22,7 @@
 
 // Don't include this file during code generation, or it will break the build
 // if existing interface methods have been modified.
+//go:build !generate
 // +build !generate
 
 package command
diff --git a/internal/lsp/command/generate.go b/internal/lsp/command/generate.go
index 49d72b5..14628c7 100644
--- a/internal/lsp/command/generate.go
+++ b/internal/lsp/command/generate.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 package main
@@ -11,11 +12,11 @@
 	"io/ioutil"
 	"os"
 
-	"golang.org/x/tools/internal/lsp/command/generate"
+	"golang.org/x/tools/internal/lsp/command/gen"
 )
 
 func main() {
-	content, err := generate.Generate()
+	content, err := gen.Generate()
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%v\n", err)
 		os.Exit(1)
diff --git a/internal/lsp/command/interface_test.go b/internal/lsp/command/interface_test.go
index d58545e..9ea30b4 100644
--- a/internal/lsp/command/interface_test.go
+++ b/internal/lsp/command/interface_test.go
@@ -9,7 +9,7 @@
 	"io/ioutil"
 	"testing"
 
-	"golang.org/x/tools/internal/lsp/command/generate"
+	"golang.org/x/tools/internal/lsp/command/gen"
 	"golang.org/x/tools/internal/testenv"
 )
 
@@ -21,7 +21,7 @@
 		t.Fatal(err)
 	}
 
-	generated, err := generate.Generate()
+	generated, err := gen.Generate()
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 96410d9..58a13ec 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -747,17 +747,26 @@
 
 // OrganizeImports requests and performs the source.organizeImports codeAction.
 func (e *Editor) OrganizeImports(ctx context.Context, path string) error {
-	return e.codeAction(ctx, path, nil, nil, protocol.SourceOrganizeImports)
+	_, err := e.codeAction(ctx, path, nil, nil, protocol.SourceOrganizeImports)
+	return err
 }
 
 // RefactorRewrite requests and performs the source.refactorRewrite codeAction.
 func (e *Editor) RefactorRewrite(ctx context.Context, path string, rng *protocol.Range) error {
-	return e.codeAction(ctx, path, rng, nil, protocol.RefactorRewrite)
+	applied, err := e.codeAction(ctx, path, rng, nil, protocol.RefactorRewrite)
+	if applied == 0 {
+		return errors.Errorf("no refactorings were applied")
+	}
+	return err
 }
 
 // ApplyQuickFixes requests and performs the quickfix codeAction.
 func (e *Editor) ApplyQuickFixes(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic) error {
-	return e.codeAction(ctx, path, rng, diagnostics, protocol.QuickFix, protocol.SourceFixAll)
+	applied, err := e.codeAction(ctx, path, rng, diagnostics, protocol.QuickFix, protocol.SourceFixAll)
+	if applied == 0 {
+		return errors.Errorf("no quick fixes were applied")
+	}
+	return err
 }
 
 // GetQuickFixes returns the available quick fix code actions.
@@ -765,14 +774,15 @@
 	return e.getCodeActions(ctx, path, rng, diagnostics, protocol.QuickFix, protocol.SourceFixAll)
 }
 
-func (e *Editor) codeAction(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) error {
+func (e *Editor) codeAction(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) (int, error) {
 	actions, err := e.getCodeActions(ctx, path, rng, diagnostics, only...)
 	if err != nil {
-		return err
+		return 0, err
 	}
+	applied := 0
 	for _, action := range actions {
 		if action.Title == "" {
-			return errors.Errorf("empty title for code action")
+			return 0, errors.Errorf("empty title for code action")
 		}
 		var match bool
 		for _, o := range only {
@@ -784,6 +794,7 @@
 		if !match {
 			continue
 		}
+		applied++
 		for _, change := range action.Edit.DocumentChanges {
 			path := e.sandbox.Workdir.URIToPath(change.TextDocument.URI)
 			if int32(e.buffers[path].version) != change.TextDocument.Version {
@@ -792,7 +803,7 @@
 			}
 			edits := convertEdits(change.Edits)
 			if err := e.EditBuffer(ctx, path, edits); err != nil {
-				return errors.Errorf("editing buffer %q: %w", path, err)
+				return 0, errors.Errorf("editing buffer %q: %w", path, err)
 			}
 		}
 		// Execute any commands. The specification says that commands are
@@ -802,15 +813,15 @@
 				Command:   action.Command.Command,
 				Arguments: action.Command.Arguments,
 			}); err != nil {
-				return err
+				return 0, err
 			}
 		}
 		// Some commands may edit files on disk.
 		if err := e.sandbox.Workdir.CheckForFileChanges(ctx); err != nil {
-			return err
+			return 0, err
 		}
 	}
-	return nil
+	return applied, nil
 }
 
 func (e *Editor) getCodeActions(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) ([]protocol.CodeAction, error) {
diff --git a/internal/lsp/lsprpc/autostart_posix.go b/internal/lsp/lsprpc/autostart_posix.go
index 9ad3f1d..45089b8 100644
--- a/internal/lsp/lsprpc/autostart_posix.go
+++ b/internal/lsp/lsprpc/autostart_posix.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
 // +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package lsprpc
diff --git a/internal/span/token111.go b/internal/span/token111.go
index bf7a540..c41e94b 100644
--- a/internal/span/token111.go
+++ b/internal/span/token111.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.12
 // +build !go1.12
 
 package span
diff --git a/internal/span/token112.go b/internal/span/token112.go
index 017aec9..4c4dea1 100644
--- a/internal/span/token112.go
+++ b/internal/span/token112.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.12
 // +build go1.12
 
 package span
diff --git a/internal/span/uri_test.go b/internal/span/uri_test.go
index 260f948..bcbad87 100644
--- a/internal/span/uri_test.go
+++ b/internal/span/uri_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !windows
 // +build !windows
 
 package span_test
diff --git a/internal/span/uri_windows_test.go b/internal/span/uri_windows_test.go
index 2a2632e..e50b58f 100644
--- a/internal/span/uri_windows_test.go
+++ b/internal/span/uri_windows_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build windows
 // +build windows
 
 package span_test
diff --git a/internal/testenv/testenv_112.go b/internal/testenv/testenv_112.go
index b25846c..4b6e57d 100644
--- a/internal/testenv/testenv_112.go
+++ b/internal/testenv/testenv_112.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.12
 // +build go1.12
 
 package testenv
diff --git a/playground/socket/socket.go b/playground/socket/socket.go
index 5e385eb..cdc6653 100644
--- a/playground/socket/socket.go
+++ b/playground/socket/socket.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !appengine
 // +build !appengine
 
 // Package socket implements an WebSocket-based playground backend.
diff --git a/refactor/eg/eg_test.go b/refactor/eg/eg_test.go
index 158f909..a788361 100644
--- a/refactor/eg/eg_test.go
+++ b/refactor/eg/eg_test.go
@@ -4,6 +4,7 @@
 
 // No testdata on Android.
 
+//go:build !android
 // +build !android
 
 package eg_test
diff --git a/refactor/eg/testdata/A.template b/refactor/eg/testdata/A.template
index f611961..6a23f12 100644
--- a/refactor/eg/testdata/A.template
+++ b/refactor/eg/testdata/A.template
@@ -1,5 +1,3 @@
-// +build ignore
-
 package template
 
 // Basic test of type-aware expression refactoring.
diff --git a/refactor/eg/testdata/A1.go b/refactor/eg/testdata/A1.go
index 9e65eb3..c64fd80 100644
--- a/refactor/eg/testdata/A1.go
+++ b/refactor/eg/testdata/A1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package A1
 
 import (
diff --git a/refactor/eg/testdata/A1.golden b/refactor/eg/testdata/A1.golden
index 7eb2934..a8aeb06 100644
--- a/refactor/eg/testdata/A1.golden
+++ b/refactor/eg/testdata/A1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package A1
 
 import (
diff --git a/refactor/eg/testdata/A2.go b/refactor/eg/testdata/A2.go
index 3ae29ad..2fab790 100644
--- a/refactor/eg/testdata/A2.go
+++ b/refactor/eg/testdata/A2.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package A2
 
 // This refactoring causes addition of "errors" import.
diff --git a/refactor/eg/testdata/A2.golden b/refactor/eg/testdata/A2.golden
index b6e3a6d..0e4ca44 100644
--- a/refactor/eg/testdata/A2.golden
+++ b/refactor/eg/testdata/A2.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package A2
 
 // This refactoring causes addition of "errors" import.
diff --git a/refactor/eg/testdata/B1.go b/refactor/eg/testdata/B1.go
index 8b52546..1e09c90 100644
--- a/refactor/eg/testdata/B1.go
+++ b/refactor/eg/testdata/B1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package B1
 
 import "time"
diff --git a/refactor/eg/testdata/B1.golden b/refactor/eg/testdata/B1.golden
index 4d4da21..b2ed30b 100644
--- a/refactor/eg/testdata/B1.golden
+++ b/refactor/eg/testdata/B1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package B1
 
 import "time"
diff --git a/refactor/eg/testdata/C1.go b/refactor/eg/testdata/C1.go
index 523b388..fb565a3 100644
--- a/refactor/eg/testdata/C1.go
+++ b/refactor/eg/testdata/C1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package C1
 
 import "strings"
diff --git a/refactor/eg/testdata/C1.golden b/refactor/eg/testdata/C1.golden
index ae7759d..d3b0b71 100644
--- a/refactor/eg/testdata/C1.golden
+++ b/refactor/eg/testdata/C1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package C1
 
 import "strings"
diff --git a/refactor/eg/testdata/D1.go b/refactor/eg/testdata/D1.go
index ae0a806..03a434c 100644
--- a/refactor/eg/testdata/D1.go
+++ b/refactor/eg/testdata/D1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package D1
 
 import "fmt"
diff --git a/refactor/eg/testdata/D1.golden b/refactor/eg/testdata/D1.golden
index 2932652..88d4a9e 100644
--- a/refactor/eg/testdata/D1.golden
+++ b/refactor/eg/testdata/D1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package D1
 
 import "fmt"
diff --git a/refactor/eg/testdata/E1.go b/refactor/eg/testdata/E1.go
index 3ea1793..54054c8 100644
--- a/refactor/eg/testdata/E1.go
+++ b/refactor/eg/testdata/E1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package E1
 
 import "log"
diff --git a/refactor/eg/testdata/E1.golden b/refactor/eg/testdata/E1.golden
index 796364f..ec10b41 100644
--- a/refactor/eg/testdata/E1.golden
+++ b/refactor/eg/testdata/E1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package E1
 
 import (
diff --git a/refactor/eg/testdata/F.template b/refactor/eg/testdata/F.template
index 21e1bd2..df73beb 100644
--- a/refactor/eg/testdata/F.template
+++ b/refactor/eg/testdata/F.template
@@ -1,8 +1,8 @@
 package templates
 
-// Test 
+// Test
 
 import "sync"
 
 func before(s sync.RWMutex) { s.Lock() }
-func after(s sync.RWMutex) { s.RLock() }
\ No newline at end of file
+func after(s sync.RWMutex)  { s.RLock() }
diff --git a/refactor/eg/testdata/F1.go b/refactor/eg/testdata/F1.go
index 2258abd..da9c9de 100644
--- a/refactor/eg/testdata/F1.go
+++ b/refactor/eg/testdata/F1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package F1
 
 import "sync"
diff --git a/refactor/eg/testdata/F1.golden b/refactor/eg/testdata/F1.golden
index 5ffda69..ea5d0cd 100644
--- a/refactor/eg/testdata/F1.golden
+++ b/refactor/eg/testdata/F1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package F1
 
 import "sync"
diff --git a/refactor/eg/testdata/G.template b/refactor/eg/testdata/G.template
index 69d84fe..ab368ce 100644
--- a/refactor/eg/testdata/G.template
+++ b/refactor/eg/testdata/G.template
@@ -7,4 +7,3 @@
 
 func before(from, to token.Pos) ast.BadExpr { return ast.BadExpr{From: from, To: to} }
 func after(from, to token.Pos) ast.BadExpr  { return ast.BadExpr{from, to} }
-     
\ No newline at end of file
diff --git a/refactor/eg/testdata/G1.go b/refactor/eg/testdata/G1.go
index 07aaff9..0fb9ab9 100644
--- a/refactor/eg/testdata/G1.go
+++ b/refactor/eg/testdata/G1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package G1
 
 import "go/ast"
diff --git a/refactor/eg/testdata/G1.golden b/refactor/eg/testdata/G1.golden
index c93c53f..ba3704c 100644
--- a/refactor/eg/testdata/G1.golden
+++ b/refactor/eg/testdata/G1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package G1
 
 import "go/ast"
diff --git a/refactor/eg/testdata/H1.go b/refactor/eg/testdata/H1.go
index ef4291c..e151ac8 100644
--- a/refactor/eg/testdata/H1.go
+++ b/refactor/eg/testdata/H1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package H1
 
 import "go/ast"
diff --git a/refactor/eg/testdata/H1.golden b/refactor/eg/testdata/H1.golden
index a1e5961..da2658a 100644
--- a/refactor/eg/testdata/H1.golden
+++ b/refactor/eg/testdata/H1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package H1
 
 import "go/ast"
diff --git a/refactor/eg/testdata/I.template b/refactor/eg/testdata/I.template
index d03e774..b8e8f93 100644
--- a/refactor/eg/testdata/I.template
+++ b/refactor/eg/testdata/I.template
@@ -1,5 +1,3 @@
-// +build ignore
-
 package templates
 
 import (
diff --git a/refactor/eg/testdata/I1.go b/refactor/eg/testdata/I1.go
index d1762eb..ef3fe8b 100644
--- a/refactor/eg/testdata/I1.go
+++ b/refactor/eg/testdata/I1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package I1
 
 import "fmt"
diff --git a/refactor/eg/testdata/I1.golden b/refactor/eg/testdata/I1.golden
index f33b3e1..d0246ae 100644
--- a/refactor/eg/testdata/I1.golden
+++ b/refactor/eg/testdata/I1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package I1
 
 import (
diff --git a/refactor/eg/testdata/J.template b/refactor/eg/testdata/J.template
index 6f82cdf..b3b1f18 100644
--- a/refactor/eg/testdata/J.template
+++ b/refactor/eg/testdata/J.template
@@ -1,5 +1,3 @@
-// +build ignore
-
 package templates
 
 import ()
diff --git a/refactor/eg/testdata/J1.go b/refactor/eg/testdata/J1.go
index 2fbeee8..532ca13 100644
--- a/refactor/eg/testdata/J1.go
+++ b/refactor/eg/testdata/J1.go
@@ -1,5 +1,3 @@
-// +build ignore
-
 package I1
 
 import "fmt"
diff --git a/refactor/eg/testdata/J1.golden b/refactor/eg/testdata/J1.golden
index bb2f11c..911ef87 100644
--- a/refactor/eg/testdata/J1.golden
+++ b/refactor/eg/testdata/J1.golden
@@ -1,5 +1,3 @@
-// +build ignore
-
 package I1
 
 import "fmt"
diff --git a/refactor/importgraph/graph_test.go b/refactor/importgraph/graph_test.go
index e342323..2ab54e2 100644
--- a/refactor/importgraph/graph_test.go
+++ b/refactor/importgraph/graph_test.go
@@ -4,6 +4,7 @@
 
 // Incomplete std lib sources on Android.
 
+//go:build !android
 // +build !android
 
 package importgraph_test
diff --git a/txtar/archive_test.go b/txtar/archive_test.go
index 1f79f8d..7ac5ee9 100644
--- a/txtar/archive_test.go
+++ b/txtar/archive_test.go
@@ -70,7 +70,7 @@
 					{"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")},
 					{"file 2", []byte("File 2 text.\n")},
 					{"empty", []byte{}},
-					{"noNL", []byte("hello world\n")},
+					{"noNL", []byte("hello world")},
 				},
 			},
 			wanted: `comment1
@@ -90,7 +90,7 @@
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			result := Format(tt.input)
-			if !reflect.DeepEqual(string(result), tt.wanted) {
+			if string(result) != tt.wanted {
 				t.Errorf("Wrong output. \nGot:\n%s\nWant:\n%s\n", string(result), tt.wanted)
 			}
 		})