gopls: deprecate support for Go 1.16 and 1.17, update warnings

Update our version table to reflect the existence of gopls@v0.12.0, and
deprecate support for Go 1.16 and 1.17.

Fixes golang/go#60341

Change-Id: Id061aafacb4099f57d464b5a7453bc1f98fda80a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/496881
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/gopls/README.md b/gopls/README.md
index 56d1592..396f86c 100644
--- a/gopls/README.md
+++ b/gopls/README.md
@@ -93,6 +93,7 @@
 | ----------- | --------------------------------------------------- |
 | Go 1.12     | [gopls@v0.7.5](https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.5) |
 | Go 1.15     | [gopls@v0.9.5](https://github.com/golang/tools/releases/tag/gopls%2Fv0.9.5) |
+| Go 1.17     | [gopls@v0.11.0](https://github.com/golang/tools/releases/tag/gopls%2Fv0.11.0) |
 
 Our extended support is enforced via [continuous integration with older Go
 versions](doc/contributing.md#ci). This legacy Go CI may not block releases:
diff --git a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go
index 04fe713..7486f24 100644
--- a/gopls/internal/lsp/general.go
+++ b/gopls/internal/lsp/general.go
@@ -8,6 +8,7 @@
 	"context"
 	"encoding/json"
 	"fmt"
+	"go/build"
 	"log"
 	"os"
 	"path"
@@ -239,14 +240,16 @@
 
 // GoVersionTable maps Go versions to the gopls version in which support will
 // be deprecated, and the final gopls version supporting them without warnings.
-// Keep this in sync with gopls/README.md
+// Keep this in sync with gopls/README.md.
 //
 // Must be sorted in ascending order of Go version.
 //
 // Mutable for testing.
 var GoVersionTable = []GoVersionSupport{
 	{12, "", "v0.7.5"},
-	{15, "v0.11.0", "v0.9.5"},
+	{15, "", "v0.9.5"},
+	{16, "v0.13.0", "v0.11.0"},
+	{17, "v0.13.0", "v0.11.0"},
 }
 
 // GoVersionSupport holds information about end-of-life Go version support.
@@ -262,11 +265,13 @@
 	return GoVersionTable[len(GoVersionTable)-1].GoVersion + 1
 }
 
-// versionMessage returns the warning/error message to display if the user is
-// on the given Go version, if any. The goVersion variable is the X in Go 1.X.
+// versionMessage returns the warning/error message to display if the user has
+// the given Go version, if any. The goVersion variable is the X in Go 1.X. If
+// fromBuild is set, the Go version is the version used to build gopls.
+// Otherwise, it is the go command version.
 //
 // If goVersion is invalid (< 0), it returns "", 0.
-func versionMessage(goVersion int) (string, protocol.MessageType) {
+func versionMessage(goVersion int, fromBuild bool) (string, protocol.MessageType) {
 	if goVersion < 0 {
 		return "", 0
 	}
@@ -276,7 +281,11 @@
 			var msgBuilder strings.Builder
 
 			mType := protocol.Error
-			fmt.Fprintf(&msgBuilder, "Found Go version 1.%d", goVersion)
+			if fromBuild {
+				fmt.Fprintf(&msgBuilder, "Gopls was built with Go version 1.%d", goVersion)
+			} else {
+				fmt.Fprintf(&msgBuilder, "Found Go version 1.%d", goVersion)
+			}
 			if v.DeprecatedVersion != "" {
 				// not deprecated yet, just a warning
 				fmt.Fprintf(&msgBuilder, ", which will be unsupported by gopls %s. ", v.DeprecatedVersion)
@@ -299,15 +308,15 @@
 //
 // It should be called after views change.
 func (s *Server) checkViewGoVersions() {
-	oldestVersion := -1
+	oldestVersion, fromBuild := go1Point(), true
 	for _, view := range s.session.Views() {
 		viewVersion := view.GoVersion()
 		if oldestVersion == -1 || viewVersion < oldestVersion {
-			oldestVersion = viewVersion
+			oldestVersion, fromBuild = viewVersion, false
 		}
 	}
 
-	if msg, mType := versionMessage(oldestVersion); msg != "" {
+	if msg, mType := versionMessage(oldestVersion, fromBuild); msg != "" {
 		s.eventuallyShowMessage(context.Background(), &protocol.ShowMessageParams{
 			Type:    mType,
 			Message: msg,
@@ -315,6 +324,21 @@
 	}
 }
 
+// go1Point returns the x in Go 1.x. If an error occurs extracting the go
+// version, it returns -1.
+//
+// Copied from the testenv package.
+func go1Point() int {
+	for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {
+		var version int
+		if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil {
+			continue
+		}
+		return version
+	}
+	return -1
+}
+
 func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) error {
 	originalViews := len(s.session.Views())
 	viewErrors := make(map[span.URI]error)
diff --git a/gopls/internal/lsp/general_test.go b/gopls/internal/lsp/general_test.go
index a0312ba..6bc0dc1 100644
--- a/gopls/internal/lsp/general_test.go
+++ b/gopls/internal/lsp/general_test.go
@@ -14,18 +14,22 @@
 func TestVersionMessage(t *testing.T) {
 	tests := []struct {
 		goVersion    int
+		fromBuild    bool
 		wantContains []string // string fragments that we expect to see
 		wantType     protocol.MessageType
 	}{
-		{-1, nil, 0},
-		{12, []string{"1.12", "not supported", "upgrade to Go 1.16", "install gopls v0.7.5"}, protocol.Error},
-		{13, []string{"1.13", "will be unsupported by gopls v0.11.0", "upgrade to Go 1.16", "install gopls v0.9.5"}, protocol.Warning},
-		{15, []string{"1.15", "will be unsupported by gopls v0.11.0", "upgrade to Go 1.16", "install gopls v0.9.5"}, protocol.Warning},
-		{16, nil, 0},
+		{-1, false, nil, 0},
+		{12, false, []string{"1.12", "not supported", "upgrade to Go 1.18", "install gopls v0.7.5"}, protocol.Error},
+		{13, false, []string{"1.13", "not supported", "upgrade to Go 1.18", "install gopls v0.9.5"}, protocol.Error},
+		{15, false, []string{"1.15", "not supported", "upgrade to Go 1.18", "install gopls v0.9.5"}, protocol.Error},
+		{15, true, []string{"Gopls was built with Go version 1.15", "not supported", "upgrade to Go 1.18", "install gopls v0.9.5"}, protocol.Error},
+		{16, false, []string{"1.16", "will be unsupported by gopls v0.13.0", "upgrade to Go 1.18", "install gopls v0.11.0"}, protocol.Warning},
+		{17, false, []string{"1.17", "will be unsupported by gopls v0.13.0", "upgrade to Go 1.18", "install gopls v0.11.0"}, protocol.Warning},
+		{17, true, []string{"Gopls was built with Go version 1.17", "will be unsupported by gopls v0.13.0", "upgrade to Go 1.18", "install gopls v0.11.0"}, protocol.Warning},
 	}
 
 	for _, test := range tests {
-		gotMsg, gotType := versionMessage(test.goVersion)
+		gotMsg, gotType := versionMessage(test.goVersion, test.fromBuild)
 
 		if len(test.wantContains) == 0 && gotMsg != "" {
 			t.Errorf("versionMessage(%d) = %q, want \"\"", test.goVersion, gotMsg)