goroot: remove references to GOROOT env variable

The extension currently makes use of an environment variable, "GOROOT",
which is set to match go.root from the user's settings. This change
removes all references to that environment variable and replaces it with
a helper function `getCurrentGoRoot` which uses the go config section of
the package.json

Updates golang/vscode-go#146

Change-Id: Ia4280cd6195e8ff559869fd8c3d8a4cf602ac548
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/237819
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/goBrowsePackage.ts b/src/goBrowsePackage.ts
index c2b041a..f27b107 100644
--- a/src/goBrowsePackage.ts
+++ b/src/goBrowsePackage.ts
@@ -9,7 +9,7 @@
 import path = require('path');
 import vscode = require('vscode');
 import { getAllPackages } from './goPackages';
-import { envPath } from './goPath';
+import { envPath, getCurrentGoRoot } from './goPath';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
 
 export function browsePackages() {
@@ -40,7 +40,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		return vscode.window.showErrorMessage(
-			`Failed to run "go list" to fetch packages as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to fetch packages as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 	}
 
diff --git a/src/goGetPackage.ts b/src/goGetPackage.ts
index 80dda27..272a995 100644
--- a/src/goGetPackage.ts
+++ b/src/goGetPackage.ts
@@ -8,7 +8,7 @@
 import cp = require('child_process');
 import vscode = require('vscode');
 import { buildCode } from './goBuild';
-import { envPath } from './goPath';
+import { envPath, getCurrentGoRoot } from './goPath';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
 
@@ -26,7 +26,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		return vscode.window.showErrorMessage(
-			`Failed to run "go get" to get package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go get" to get package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 	}
 
diff --git a/src/goImplementations.ts b/src/goImplementations.ts
index 4ee9e29..da39a51 100644
--- a/src/goImplementations.ts
+++ b/src/goImplementations.ts
@@ -10,14 +10,14 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
-import { envPath } from './goPath';
+import { envPath, getCurrentGoRoot } from './goPath';
 import {
 	byteOffsetAt,
 	canonicalizeGOPATHPrefix,
 	getBinPath,
 	getGoConfig,
 	getWorkspaceFolderPath,
-	killTree
+	killTree,
 } from './util';
 
 interface GoListOutput {
@@ -57,7 +57,7 @@
 		const goRuntimePath = getBinPath('go');
 		if (!goRuntimePath) {
 			vscode.window.showErrorMessage(
-				`Failed to run "go list" to get the scope to find implementations as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+				`Failed to run "go list" to get the scope to find implementations as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 			);
 			return;
 		}
diff --git a/src/goImport.ts b/src/goImport.ts
index fade834..5a9df7e 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -11,7 +11,7 @@
 import { promptForMissingTool } from './goInstallTools';
 import { documentSymbols, GoOutlineImportsOptions } from './goOutline';
 import { getImportablePackages } from './goPackages';
-import { envPath } from './goPath';
+import { envPath, getCurrentGoRoot } from './goPath';
 import { getBinPath, getImportPath, parseFilePrelude } from './util';
 
 const missingToolMsg = 'Missing tool: ';
@@ -180,7 +180,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		vscode.window.showErrorMessage(
-			`Failed to run "go list" to find the package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to find the package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
diff --git a/src/goInstall.ts b/src/goInstall.ts
index 4f351aa..859da2a 100644
--- a/src/goInstall.ts
+++ b/src/goInstall.ts
@@ -8,7 +8,7 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { isModSupported } from './goModules';
-import { envPath, getCurrentGoWorkspaceFromGOPATH } from './goPath';
+import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './goPath';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getGoConfig, getModuleCache } from './util';
 
@@ -28,7 +28,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		vscode.window.showErrorMessage(
-			`Failed to run "go install" to install the package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go install" to install the package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index eb641af..a72ad34 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -14,7 +14,7 @@
 import { toolInstallationEnvironment } from './goEnv';
 import { getLanguageServerToolPath } from './goLanguageServer';
 import { restartLanguageServer } from './goMain';
-import { envPath, getToolFromToolPath } from './goPath';
+import { envPath, getCurrentGoRoot, getToolFromToolPath, setCurrentGoRoot } from './goPath';
 import { hideGoStatus, outputChannel, showGoStatus } from './goStatus';
 import {
 	containsTool,
@@ -33,8 +33,7 @@
 	getGoVersion,
 	getTempFilePath,
 	GoVersion,
-	resolvePath,
-	rmdirRecursive
+	rmdirRecursive,
 } from './util';
 
 // declinedUpdates tracks the tools that the user has declined to update.
@@ -336,12 +335,7 @@
 }
 
 export function updateGoVarsFromConfig(): Promise<void> {
-	const goroot = getGoConfig()['goroot'];
-	if (goroot) {
-		process.env['GOROOT'] = resolvePath(goroot);
-	}
-
-	if (process.env['GOPATH'] && process.env['GOROOT'] && process.env['GOPROXY'] && process.env['GOBIN']) {
+	if (getCurrentGoRoot() && process.env['GOPATH'] && process.env['GOPROXY'] && process.env['GOBIN']) {
 		return Promise.resolve();
 	}
 
@@ -349,7 +343,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		vscode.window.showErrorMessage(
-			`Failed to run "go env" to find GOPATH as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go env" to find GOPATH as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
@@ -381,8 +375,8 @@
 			if (!process.env['GOPATH'] && envOutput[0].trim()) {
 				process.env['GOPATH'] = envOutput[0].trim();
 			}
-			if (!process.env['GOROOT'] && envOutput[1] && envOutput[1].trim()) {
-				process.env['GOROOT'] = envOutput[1].trim();
+			if (!getCurrentGoRoot() && envOutput[1] && envOutput[1].trim()) {
+				setCurrentGoRoot(envOutput[1].trim());
 			}
 			if (!process.env['GOPROXY'] && envOutput[2] && envOutput[2].trim()) {
 				process.env['GOPROXY'] = envOutput[2].trim();
diff --git a/src/goMain.ts b/src/goMain.ts
index 2ea23c4..0f7b50c 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -34,7 +34,7 @@
 import { GO_MODE } from './goMode';
 import { addTags, removeTags } from './goModifytags';
 import { GO111MODULE, isModSupported } from './goModules';
-import { clearCacheForTools, fileExists } from './goPath';
+import { clearCacheForTools, fileExists, getCurrentGoRoot, setCurrentGoRoot } from './goPath';
 import { playgroundCommand } from './goPlayground';
 import { GoReferencesCodeLensProvider } from './goReferencesCodelens';
 import { GoRunTestCodeLensProvider } from './goRunTestCodelens';
@@ -48,8 +48,16 @@
 } from './stateUtils';
 import { cancelRunningTests, showTestOutput } from './testUtils';
 import {
-	cleanupTempDir, getBinPath, getCurrentGoPath, getExtensionCommands, getGoConfig,
-	getGoVersion, getToolsGopath, getWorkspaceFolderPath, handleDiagnosticErrors, isGoPathSet
+	cleanupTempDir,
+	getBinPath,
+	getCurrentGoPath,
+	getExtensionCommands,
+	getGoConfig,
+	getGoVersion,
+	getToolsGopath,
+	getWorkspaceFolderPath,
+	handleDiagnosticErrors,
+	isGoPathSet,
 } from './util';
 
 export let buildDiagnosticCollection: vscode.DiagnosticCollection;
@@ -64,6 +72,10 @@
 export function activate(ctx: vscode.ExtensionContext): void {
 	setGlobalState(ctx.globalState);
 	setWorkspaceState(ctx.workspaceState);
+	const configGOROOT = getGoConfig()['goroot'];
+	if (!!configGOROOT) {
+		setCurrentGoRoot(configGOROOT);
+	}
 
 	updateGoVarsFromConfig().then(async () => {
 		const updateToolsCmdText = 'Update tools';
@@ -77,7 +89,7 @@
 			toolsGoInfo[toolsGopath] = { goroot: null, version: null };
 		}
 		const prevGoroot = toolsGoInfo[toolsGopath].goroot;
-		const currentGoroot: string = process.env['GOROOT'] && process.env['GOROOT'].toLowerCase();
+		const currentGoroot: string = getCurrentGoRoot().toLowerCase();
 		if (prevGoroot && prevGoroot.toLowerCase() !== currentGoroot) {
 			vscode.window
 				.showInformationMessage(
@@ -99,7 +111,7 @@
 					if (prevVersion) {
 						vscode.window
 							.showInformationMessage(
-								'Your Go version is different than before, few Go tools may need re-compiling',
+								'Your Go version is different than before, a few Go tools may need re-compiling',
 								updateToolsCmdText
 							)
 							.then((selected) => {
@@ -212,7 +224,7 @@
 			outputChannel.appendLine('GOBIN: ' + process.env['GOBIN']);
 			outputChannel.appendLine('toolsGopath: ' + getToolsGopath());
 			outputChannel.appendLine('gopath: ' + getCurrentGoPath());
-			outputChannel.appendLine('GOROOT: ' + process.env['GOROOT']);
+			outputChannel.appendLine('GOROOT: ' + getCurrentGoRoot());
 			outputChannel.appendLine('PATH: ' + process.env['PATH']);
 			outputChannel.appendLine('');
 
diff --git a/src/goModules.ts b/src/goModules.ts
index 1714066..6b4cbcf 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -8,7 +8,7 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { installTools } from './goInstallTools';
-import { envPath, fixDriveCasingInWindows } from './goPath';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './goPath';
 import { getTool } from './goTools';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getGoConfig, getGoVersion, getModuleCache } from './util';
@@ -19,7 +19,7 @@
 	const goExecutable = getBinPath('go');
 	if (!goExecutable) {
 		console.warn(
-			`Failed to run "go env GOMOD" to find mod file as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go env GOMOD" to find mod file as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
@@ -161,7 +161,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		console.warn(
-			`Failed to run "go list" to find current package as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to find current package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
 		return;
 	}
diff --git a/src/goPackages.ts b/src/goPackages.ts
index 8965435..5c7fce7 100644
--- a/src/goPackages.ts
+++ b/src/goPackages.ts
@@ -8,7 +8,7 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { envPath, fixDriveCasingInWindows, getCurrentGoWorkspaceFromGOPATH } from './goPath';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './goPath';
 import { getBinPath, getCurrentGoPath, getGoVersion, isVendorSupported } from './util';
 
 type GopkgsDone = (res: Map<string, PackageInfo>) => void;
@@ -72,7 +72,7 @@
 				);
 				return resolve(pkgs);
 			}
-			const goroot = process.env['GOROOT'];
+			const goroot = getCurrentGoRoot();
 			const output = chunks.join('');
 			if (output.indexOf(';') === -1) {
 				// User might be using the old gopkgs tool, prompt to update
@@ -262,7 +262,7 @@
 	const goRuntimePath = getBinPath('go');
 	if (!goRuntimePath) {
 		console.warn(
-			`Failed to run "go list" to find packages as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+			`Failed to run "go list" to find packages as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) PATH(${envPath})`
 		);
 		return;
 	}
diff --git a/src/goPath.ts b/src/goPath.ts
index 2f4402a..c069171 100644
--- a/src/goPath.ts
+++ b/src/goPath.ts
@@ -31,7 +31,11 @@
 	return null;
 }
 
-export function getBinPathWithPreferredGopath(toolName: string, preferredGopaths: string[], alternateTool?: string) {
+export function getBinPathWithPreferredGopath(
+	toolName: string,
+	preferredGopaths: string[],
+	alternateTool?: string
+) {
 	if (binPathCache[toolName]) {
 		return binPathCache[toolName];
 	}
@@ -60,7 +64,7 @@
 	}
 
 	// Check GOROOT (go, gofmt, godoc would be found here)
-	const pathFromGoRoot = getBinPathFromEnvVar(binname, process.env['GOROOT'], true);
+	const pathFromGoRoot = getBinPathFromEnvVar(binname, getCurrentGoRoot(), true);
 	if (pathFromGoRoot) {
 		binPathCache[toolName] = pathFromGoRoot;
 		return pathFromGoRoot;
@@ -87,6 +91,18 @@
 	return toolName;
 }
 
+/**
+ * Returns the goroot path if it exists, otherwise returns an empty string
+ */
+let currentGoRoot = '';
+export function getCurrentGoRoot(): string {
+	return currentGoRoot || process.env['GOROOT'] || '';
+}
+
+export function setCurrentGoRoot(goroot: string) {
+	currentGoRoot = goroot;
+}
+
 function correctBinname(toolName: string) {
 	if (process.platform === 'win32') {
 		return toolName + '.exe';
diff --git a/src/goSymbol.ts b/src/goSymbol.ts
index ae8cf8c..b97e018 100644
--- a/src/goSymbol.ts
+++ b/src/goSymbol.ts
@@ -9,6 +9,7 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
+import { getCurrentGoRoot } from './goPath';
 import { getBinPath, getGoConfig, getWorkspaceFolderPath, killTree } from './util';
 
 // Keep in sync with github.com/acroca/go-symbols'
@@ -93,7 +94,7 @@
 	calls.push(callGoSymbols([...baseArgs, workspacePath, query], token));
 
 	if (gotoSymbolConfig.includeGoroot) {
-		const goRoot = process.env['GOROOT'];
+		const goRoot = getCurrentGoRoot();
 		const gorootCall = callGoSymbols([...baseArgs, goRoot, query], token);
 		calls.push(gorootCall);
 	}
diff --git a/src/testUtils.ts b/src/testUtils.ts
index d1a97f4..092dc45 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -12,7 +12,7 @@
 import { getCurrentPackage } from './goModules';
 import { GoDocumentSymbolProvider } from './goOutline';
 import { getNonVendorPackages } from './goPackages';
-import { envPath, getCurrentGoWorkspaceFromGOPATH, parseEnvFile } from './goPath';
+import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH, parseEnvFile } from './goPath';
 import {
 	getBinPath,
 	getCurrentGoPath,
@@ -20,7 +20,7 @@
 	getTempFilePath,
 	killTree,
 	LineBuffer,
-	resolvePath
+	resolvePath,
 } from './util';
 
 const outputChannel = vscode.window.createOutputChannel('Go Tests');
@@ -262,7 +262,7 @@
 
 		if (!goRuntimePath) {
 			vscode.window.showErrorMessage(
-				`Failed to run "go test" as the "go" binary cannot be found in either GOROOT(${process.env['GOROOT']}) or PATH(${envPath})`
+				`Failed to run "go test" as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 			);
 			return Promise.resolve();
 		}
diff --git a/src/util.ts b/src/util.ts
index 0561001..4de9508 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -19,8 +19,9 @@
 	envPath,
 	fixDriveCasingInWindows,
 	getBinPathWithPreferredGopath,
+	getCurrentGoRoot,
 	getInferredGopath,
-	resolveHomeDir
+	resolveHomeDir,
 } from './goPath';
 import { outputChannel } from './goStatus';
 import { extensionId } from './telemetry';
@@ -307,7 +308,7 @@
 	};
 
 	if (!goRuntimePath) {
-		warn(`unable to locate "go" binary in GOROOT (${process.env['GOROOT']}) or PATH (${envPath})`);
+		warn(`unable to locate "go" binary in GOROOT (${getCurrentGoRoot()}) or PATH (${envPath})`);
 		return;
 	}
 	if (cachedGoVersion) {
@@ -438,7 +439,7 @@
 	return getBinPathWithPreferredGopath(
 		tool,
 		tool === 'go' ? [] : [getToolsGopath(), getCurrentGoPath()],
-		resolvePath(alternateToolPath)
+		resolvePath(alternateToolPath),
 	);
 }
 
diff --git a/test/integration/goDebug.test.ts b/test/integration/goDebug.test.ts
index 23a9afa..618087f 100644
--- a/test/integration/goDebug.test.ts
+++ b/test/integration/goDebug.test.ts
@@ -3,8 +3,11 @@
 import * as path from 'path';
 import * as sinon from 'sinon';
 import {
-	Delve, escapeGoModPath, GoDebugSession,
-	PackageBuildInfo, RemoteSourcesAndPackages
+	Delve,
+	escapeGoModPath,
+	GoDebugSession,
+	PackageBuildInfo,
+	RemoteSourcesAndPackages,
 } from '../../src/debugAdapter/goDebug';
 
 suite('Path Manipulation Tests', () => {
@@ -13,19 +16,17 @@
 	});
 });
 
-suite('GoDebugSession Tests', () => {
+suite('GoDebugSession Tests', async () => {
 	const workspaceFolder = '/usr/workspacefolder';
 	const delve: Delve = {} as Delve;
 	let goDebugSession: GoDebugSession;
 	let remoteSourcesAndPackages: RemoteSourcesAndPackages;
 	let fileSystem: typeof fs;
 
-	let previousGoPath: string;
-	let previousGoRoot: string;
+	let previousEnv: any;
 
 	setup(() => {
-		previousGoPath = process.env.GOPATH;
-		previousGoRoot = process.env.GOROOT;
+		previousEnv = Object.assign({}, process.env);
 
 		process.env.GOPATH = '/usr/gopath';
 		process.env.GOROOT = '/usr/goroot';
@@ -39,8 +40,7 @@
 	});
 
 	teardown(() => {
-		process.env.GOPATH = previousGoPath;
-		process.env.GOROOT = previousGoRoot;
+		process.env = previousEnv;
 		sinon.restore();
 	});