src/goEnvironmentStatus: correctly interpret the chosen go path

In order to retrieve the file path to the go binary chosen
from the file browser, we should use fsPath, instead of path.

While we are here, we also address two additional issues:

- Enhance the validation of `go version` result by checking
the returned GoVersion object is valid. Binary that implements
`version` but outputs a version string that's not recognizable
will be rejected.

- Make chooseGoEnvironment handle failed getDefaultGoOption call
so users can still have QuickPick items to clear the incorrect
value stored in the Memento or choose a different version.

Fixes golang/vscode-go#868
Fixes golang/vscode-go#864

Change-Id: Ib0a3762ff3851c5a88ee5bde0fdc84c865060f6e
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/267898
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index e7242ba..b96fd03 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -18,7 +18,14 @@
 import { addGoStatus, goEnvStatusbarItem, outputChannel, removeGoStatus } from './goStatus';
 import { getFromGlobalState, getFromWorkspaceState, updateGlobalState, updateWorkspaceState } from './stateUtils';
 import { getBinPath, getGoConfig, getGoVersion, getTempFilePath, GoVersion, rmdirRecursive } from './util';
-import { correctBinname, executableFileExists, getBinPathFromEnvVar, getCurrentGoRoot, pathExists } from './utils/pathUtils';
+import {
+	correctBinname,
+	executableFileExists,
+	fixDriveCasingInWindows,
+	getBinPathFromEnvVar,
+	getCurrentGoRoot,
+	pathExists
+} from './utils/pathUtils';
 
 export class GoEnvironmentOption {
 	public static fromQuickPickItem({ description, label }: vscode.QuickPickItem): GoEnvironmentOption {
@@ -77,7 +84,7 @@
 
 	// create quick pick items
 	const uninstalledQuickPicks = uninstalledOptions.map((op) => op.toQuickPickItem());
-	const defaultQuickPick = defaultOption.toQuickPickItem();
+	const defaultQuickPick = defaultOption ? [ defaultOption.toQuickPickItem() ] : [];
 	const goSDKQuickPicks = goSDKOptions.map((op) => op.toQuickPickItem());
 
 	// dedup options by eliminating duplicate paths (description)
@@ -87,7 +94,7 @@
 		description: 'Select the go binary to use',
 	};
 	// TODO(hyangah): Add separators after clearOption if github.com/microsoft/vscode#74967 is resolved.
-	const options = [filePickerOption, clearOption, defaultQuickPick, ...goSDKQuickPicks, ...uninstalledQuickPicks]
+	const options = [filePickerOption, clearOption, ...defaultQuickPick, ...goSDKQuickPicks, ...uninstalledQuickPicks]
 		.reduce((opts, nextOption) => {
 			if (opts.find((op) => op.description === nextOption.description || op.label === nextOption.label)) {
 				return opts;
@@ -103,10 +110,7 @@
 
 	// update currently selected go
 	try {
-		const changed = await setSelectedGo(GoEnvironmentOption.fromQuickPickItem(selection));
-		if (changed) {
-			vscode.window.showInformationMessage(`Switched to ${selection.label}`);
-		}
+		await setSelectedGo(GoEnvironmentOption.fromQuickPickItem(selection));
 	} catch (e) {
 		vscode.window.showErrorMessage(e.message);
 	}
@@ -142,18 +146,19 @@
 		if (!newGoUris || newGoUris.length !== 1) {
 			return false;
 		}
-		const newGoUri = newGoUris[0];
+		const newGoBin = fixDriveCasingInWindows(newGoUris[0].fsPath);
+		const oldGoBin = fixDriveCasingInWindows(path.join(defaultUri.fsPath, correctBinname('go')));
 
-		if (defaultUri === newGoUri) {
+		if (newGoBin === oldGoBin) {
 			return false;
 		}
-		if (!executableFileExists(newGoUri.path)) {
-			vscode.window.showErrorMessage(`${newGoUri.path} is not an executable`);
+		if (!executableFileExists(newGoBin)) {
+			vscode.window.showErrorMessage(`${newGoBin} is not an executable`);
 			return false;
 		}
-		const newGo = await getGoVersion(newGoUri.path);
-		if (!newGo) {
-			vscode.window.showErrorMessage(`failed to get "${newGoUri.path} version", invalid Go binary`);
+		const newGo = await getGoVersion(newGoBin);
+		if (!newGo || !newGo.isValid() ) {
+			vscode.window.showErrorMessage(`failed to get "${newGoBin} version", invalid Go binary`);
 			return false;
 		}
 		await updateWorkspaceState('selectedGo', new GoEnvironmentOption(newGo.binaryPath, formatGoVersion(newGo)));
@@ -422,11 +427,11 @@
 	);
 }
 
-export async function getDefaultGoOption(): Promise<GoEnvironmentOption> {
+export async function getDefaultGoOption(): Promise<GoEnvironmentOption|undefined> {
 	// make goroot default to go.goroot
 	const goroot = getCurrentGoRoot();
 	if (!goroot) {
-		throw new Error('No Go command could be found.');
+		return undefined;
 	}
 
 	// set Go version and command