gopls/internal/lsp/mod: reorder vulncheck quick fixes

Nudge developers towards recommended actions by listing them first.
More specifically,
  0) Other diagnostics if any (e.g. errors in go.mod)
  1) Run govulncheck
  2) Upgrade to a specific version
  3) Upgrade to latest
  4) Reset govulncheck result

Fixes golang/vscode-go#2576

Change-Id: Ib83f8dab5a1c5bea600fe1ec61701218c7a65ac1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/461555
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagnostics.go
index 25860ea..fdab833 100644
--- a/gopls/internal/lsp/mod/diagnostics.go
+++ b/gopls/internal/lsp/mod/diagnostics.go
@@ -527,34 +527,37 @@
 	if len(actions) <= 1 {
 		return actions // return early if no sorting necessary
 	}
-	var others []protocol.CodeAction
+	var versionedUpgrade, latestUpgrade, resetAction protocol.CodeAction
+	var chosenVersionedUpgrade string
+	var selected []protocol.CodeAction
 
 	seen := make(map[string]bool)
 
-	set := make(map[string]protocol.CodeAction)
 	for _, action := range actions {
 		if strings.HasPrefix(action.Title, upgradeCodeActionPrefix) {
-			set[action.Command.Title] = action
+			if v := getUpgradeVersion(action); v == "latest" && latestUpgrade.Title == "" {
+				latestUpgrade = action
+			} else if versionedUpgrade.Title == "" || semver.Compare(v, chosenVersionedUpgrade) > 0 {
+				chosenVersionedUpgrade = v
+				versionedUpgrade = action
+			}
+		} else if strings.HasPrefix(action.Title, "Reset govulncheck") {
+			resetAction = action
 		} else if !seen[action.Command.Title] {
 			seen[action.Command.Title] = true
-			others = append(others, action)
+			selected = append(selected, action)
 		}
 	}
-	var upgrades []protocol.CodeAction
-	for _, action := range set {
-		upgrades = append(upgrades, action)
+	if versionedUpgrade.Title != "" {
+		selected = append(selected, versionedUpgrade)
 	}
-	// Sort results by version number, latest first.
-	// There should be no duplicates at this point.
-	sort.Slice(upgrades, func(i, j int) bool {
-		vi, vj := getUpgradeVersion(upgrades[i]), getUpgradeVersion(upgrades[j])
-		return vi == "latest" || (vj != "latest" && semver.Compare(vi, vj) > 0)
-	})
-	// Choose at most one specific version and the latest.
-	if getUpgradeVersion(upgrades[0]) == "latest" {
-		return append(upgrades[:2], others...)
+	if latestUpgrade.Title != "" {
+		selected = append(selected, latestUpgrade)
 	}
-	return append(upgrades[:1], others...)
+	if resetAction.Title != "" {
+		selected = append(selected, resetAction)
+	}
+	return selected
 }
 
 func getUpgradeVersion(p protocol.CodeAction) string {
diff --git a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/misc/vuln_test.go
index bbf99b3..e311356 100644
--- a/gopls/internal/regtest/misc/vuln_test.go
+++ b/gopls/internal/regtest/misc/vuln_test.go
@@ -503,16 +503,16 @@
 						msg:      "golang.org/amod has known vulnerabilities GO-2022-01, GO-2022-03.",
 						severity: protocol.SeverityInformation,
 						codeActions: []string{
-							"Upgrade to latest",
-							"Upgrade to v1.0.6",
 							"Run govulncheck to verify",
+							"Upgrade to v1.0.6",
+							"Upgrade to latest",
 						},
 					},
 				},
 				codeActions: []string{
-					"Upgrade to latest",
-					"Upgrade to v1.0.6",
 					"Run govulncheck to verify",
+					"Upgrade to v1.0.6",
+					"Upgrade to latest",
 				},
 				hover: []string{"GO-2022-01", "Fixed in v1.0.4.", "GO-2022-03"},
 			},
@@ -655,8 +655,8 @@
 						msg:      "golang.org/amod has a vulnerability used in the code: GO-2022-01.",
 						severity: protocol.SeverityWarning,
 						codeActions: []string{
-							"Upgrade to latest",
 							"Upgrade to v1.0.4",
+							"Upgrade to latest",
 							"Reset govulncheck result",
 						},
 						relatedInfo: []vulnRelatedInfo{
@@ -668,8 +668,8 @@
 						msg:      "golang.org/amod has a vulnerability GO-2022-03 that is not used in the code.",
 						severity: protocol.SeverityInformation,
 						codeActions: []string{
-							"Upgrade to latest",
 							"Upgrade to v1.0.6",
+							"Upgrade to latest",
 							"Reset govulncheck result",
 						},
 						relatedInfo: []vulnRelatedInfo{
@@ -679,8 +679,8 @@
 					},
 				},
 				codeActions: []string{
-					"Upgrade to latest",
 					"Upgrade to v1.0.6",
+					"Upgrade to latest",
 					"Reset govulncheck result",
 				},
 				hover: []string{"GO-2022-01", "Fixed in v1.0.4.", "GO-2022-03"},