src/debugAdapter: make filepaths in errors absolute

Errors from delve often contain relative paths, which may not be
relative to the workspace directory resulting in bad links. Try to
make the paths absolute before displaying to the user.

Fixes golang/vscode-go#456

Change-Id: I44c715db7b05c7dbb252a10bb1b5b4dd2f4efc21
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/252978
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index 3ade4b9..35dbe09 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -32,12 +32,13 @@
 import { parseEnvFiles } from '../utils/envUtils';
 import {
 	envPath,
+	expandFilePathInOutput,
 	fixDriveCasingInWindows,
 	getBinPathWithPreferredGopathGoroot,
 	getCurrentGoWorkspaceFromGOPATH,
 	getInferredGopath,
-} from '../utils/goPath';
-import { killProcessTree } from '../utils/processUtils';
+} from '../utils/pathUtils';
+import {killProcessTree} from '../utils/processUtils';
 
 const fsAccess = util.promisify(fs.access);
 const fsUnlink = util.promisify(fs.unlink);
@@ -1763,6 +1764,9 @@
 			this.sendEvent(new OutputEvent(str, 'stdout'));
 		};
 		this.delve.onstderr = (str: string) => {
+			if (localPath.length > 0) {
+				str = expandFilePathInOutput(str, localPath);
+			}
 			this.sendEvent(new OutputEvent(str, 'stderr'));
 		};
 		this.delve.onclose = (code) => {
diff --git a/src/debugAdapter2/goDlvDebug.ts b/src/debugAdapter2/goDlvDebug.ts
index 28676fc..c397050 100644
--- a/src/debugAdapter2/goDlvDebug.ts
+++ b/src/debugAdapter2/goDlvDebug.ts
@@ -20,7 +20,7 @@
 	TerminatedEvent
 } from 'vscode-debugadapter';
 import { DebugProtocol } from 'vscode-debugprotocol';
-import { envPath, getBinPathWithPreferredGopathGoroot } from '../utils/goPath';
+import { envPath, expandFilePathInOutput, getBinPathWithPreferredGopathGoroot } from '../utils/pathUtils';
 import { killProcessTree } from '../utils/processUtils';
 
 import { DAPClient } from './dapClient';
@@ -684,13 +684,15 @@
 
 		log(`Running: ${dlvPath} ${dlvArgs.join(' ')}`);
 
+		const dir = parseProgramArgSync(launchArgs).dirname;
 		this.debugProcess = spawn(dlvPath, dlvArgs, {
-			cwd: parseProgramArgSync(launchArgs).dirname,
+			cwd: dir,
 			env
 		});
 
 		this.debugProcess.stderr.on('data', (chunk) => {
-			const str = chunk.toString();
+			let str = chunk.toString();
+			str = expandFilePathInOutput(str, dir);
 			this.emit('stderr', str);
 		});
 
diff --git a/src/diffUtils.ts b/src/diffUtils.ts
index e0895a1..e147668 100644
--- a/src/diffUtils.ts
+++ b/src/diffUtils.ts
@@ -5,7 +5,7 @@
 
 import jsDiff = require('diff');
 import { Position, Range, TextEditorEdit, Uri, WorkspaceEdit } from 'vscode';
-import { getBinPathFromEnvVar } from './utils/goPath';
+import { getBinPathFromEnvVar } from './utils/pathUtils';
 
 let diffToolAvailable: boolean | null = null;
 
diff --git a/src/goBrowsePackage.ts b/src/goBrowsePackage.ts
index 79097a3..f52bf79 100644
--- a/src/goBrowsePackage.ts
+++ b/src/goBrowsePackage.ts
@@ -10,7 +10,7 @@
 import vscode = require('vscode');
 import { getAllPackages } from './goPackages';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
-import { envPath, getCurrentGoRoot } from './utils/goPath';
+import { envPath, getCurrentGoRoot } from './utils/pathUtils';
 
 export function browsePackages() {
 	let workDir = '';
diff --git a/src/goBuild.ts b/src/goBuild.ts
index a265e54..219a3e5 100644
--- a/src/goBuild.ts
+++ b/src/goBuild.ts
@@ -21,7 +21,7 @@
 	ICheckResult,
 	runTool
 } from './util';
-import { getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
+import { getCurrentGoWorkspaceFromGOPATH } from './utils/pathUtils';
 
 /**
  * Builds current package or workspace.
diff --git a/src/goDeclaration.ts b/src/goDeclaration.ts
index 0d13500..034413c 100644
--- a/src/goDeclaration.ts
+++ b/src/goDeclaration.ts
@@ -22,7 +22,7 @@
 	isPositionInString,
 	runGodoc
 } from './util';
-import { getCurrentGoRoot } from './utils/goPath';
+import { getCurrentGoRoot } from './utils/pathUtils';
 import { killProcessTree } from './utils/processUtils';
 
 const missingToolMsg = 'Missing tool: ';
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index b2f05b0..ed13d42 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -17,7 +17,7 @@
 import { hideGoStatus, outputChannel, showGoStatus } from './goStatus';
 import { getFromGlobalState, getFromWorkspaceState, updateGlobalState, updateWorkspaceState } from './stateUtils';
 import { getBinPath, getGoConfig, getGoVersion, getTempFilePath, GoVersion, rmdirRecursive } from './util';
-import { correctBinname, getBinPathFromEnvVar, getCurrentGoRoot, pathExists } from './utils/goPath';
+import { correctBinname, getBinPathFromEnvVar, getCurrentGoRoot, pathExists } from './utils/pathUtils';
 
 export class GoEnvironmentOption {
 	public static fromQuickPickItem({ description, label }: vscode.QuickPickItem): GoEnvironmentOption {
diff --git a/src/goGetPackage.ts b/src/goGetPackage.ts
index bffefeb..86e5dc9 100644
--- a/src/goGetPackage.ts
+++ b/src/goGetPackage.ts
@@ -10,7 +10,7 @@
 import { buildCode } from './goBuild';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
-import { envPath, getCurrentGoRoot } from './utils/goPath';
+import { envPath, getCurrentGoRoot } from './utils/pathUtils';
 
 export function goGetPackage() {
 	const editor = vscode.window.activeTextEditor;
diff --git a/src/goImplementations.ts b/src/goImplementations.ts
index 05e44bc..36f9e36 100644
--- a/src/goImplementations.ts
+++ b/src/goImplementations.ts
@@ -17,7 +17,7 @@
 	getGoConfig,
 	getWorkspaceFolderPath
 } from './util';
-import { envPath, getCurrentGoRoot } from './utils/goPath';
+import { envPath, getCurrentGoRoot } from './utils/pathUtils';
 import {killProcessTree} from './utils/processUtils';
 
 interface GoListOutput {
diff --git a/src/goImport.ts b/src/goImport.ts
index 82fa2a9..b4db3a5 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -12,7 +12,7 @@
 import { documentSymbols, GoOutlineImportsOptions } from './goOutline';
 import { getImportablePackages } from './goPackages';
 import { getBinPath, getImportPath, parseFilePrelude } from './util';
-import { envPath, getCurrentGoRoot } from './utils/goPath';
+import { envPath, getCurrentGoRoot } from './utils/pathUtils';
 
 const missingToolMsg = 'Missing tool: ';
 
diff --git a/src/goInstall.ts b/src/goInstall.ts
index d2131ac..52b0223 100644
--- a/src/goInstall.ts
+++ b/src/goInstall.ts
@@ -10,7 +10,7 @@
 import { isModSupported } from './goModules';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getGoConfig, getModuleCache } from './util';
-import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
+import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/pathUtils';
 
 export async function installCurrentPackage(): Promise<void> {
 	const editor = vscode.window.activeTextEditor;
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index f4a86f5..56de8c9 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -35,7 +35,7 @@
 	GoVersion,
 	rmdirRecursive,
 } from './util';
-import { envPath, getCurrentGoRoot, getToolFromToolPath, setCurrentGoRoot } from './utils/goPath';
+import { envPath, getCurrentGoRoot, getToolFromToolPath, setCurrentGoRoot } from './utils/pathUtils';
 
 // declinedUpdates tracks the tools that the user has declined to update.
 const declinedUpdates: Tool[] = [];
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index a45f27a..dd90e59 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -49,7 +49,7 @@
 import { GoTypeDefinitionProvider } from './goTypeDefinition';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getCurrentGoPath, getGoConfig } from './util';
-import { getToolFromToolPath } from './utils/goPath';
+import { getToolFromToolPath } from './utils/pathUtils';
 
 interface LanguageServerConfig {
 	serverName: string;
diff --git a/src/goMain.ts b/src/goMain.ts
index 860a6ed..4bc8a79 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -61,7 +61,7 @@
 	isGoPathSet,
 	resolvePath,
 } from './util';
-import { clearCacheForTools, envPath, fileExists, getCurrentGoRoot, setCurrentGoRoot } from './utils/goPath';
+import { clearCacheForTools, envPath, fileExists, getCurrentGoRoot, setCurrentGoRoot } from './utils/pathUtils';
 
 export let buildDiagnosticCollection: vscode.DiagnosticCollection;
 export let lintDiagnosticCollection: vscode.DiagnosticCollection;
diff --git a/src/goModules.ts b/src/goModules.ts
index 82077ed..0fa9d81 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -11,7 +11,7 @@
 import { getTool } from './goTools';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getGoConfig, getGoVersion, getModuleCache } from './util';
-import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './utils/goPath';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './utils/pathUtils';
 
 export let GO111MODULE: string;
 
diff --git a/src/goPackages.ts b/src/goPackages.ts
index 20eefef..c5ffd8e 100644
--- a/src/goPackages.ts
+++ b/src/goPackages.ts
@@ -9,7 +9,7 @@
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import { getBinPath, getCurrentGoPath, getGoVersion, isVendorSupported } from './util';
-import { envPath, fixDriveCasingInWindows, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/pathUtils';
 
 type GopkgsDone = (res: Map<string, PackageInfo>) => void;
 interface Cache {
diff --git a/src/goSuggest.ts b/src/goSuggest.ts
index 80f3278..20ea2cb 100644
--- a/src/goSuggest.ts
+++ b/src/goSuggest.ts
@@ -27,7 +27,7 @@
 	parseFilePrelude,
 	runGodoc
 } from './util';
-import { getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
+import { getCurrentGoWorkspaceFromGOPATH } from './utils/pathUtils';
 
 function vscodeKindFromGoCodeClass(kind: string, type: string): vscode.CompletionItemKind {
 	switch (kind) {
diff --git a/src/goSymbol.ts b/src/goSymbol.ts
index 9c3bb15..4e4948d 100644
--- a/src/goSymbol.ts
+++ b/src/goSymbol.ts
@@ -10,7 +10,7 @@
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import { getBinPath, getGoConfig, getWorkspaceFolderPath} from './util';
-import { getCurrentGoRoot } from './utils/goPath';
+import { getCurrentGoRoot } from './utils/pathUtils';
 import {killProcessTree} from './utils/processUtils';
 
 // Keep in sync with github.com/acroca/go-symbols'
diff --git a/src/testUtils.ts b/src/testUtils.ts
index 1fbfc3b..90adc56 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -21,7 +21,7 @@
 	resolvePath
 } from './util';
 import { parseEnvFile } from './utils/envUtils';
-import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
+import { envPath, expandFilePathInOutput, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/pathUtils';
 import { killProcessTree } from './utils/processUtils';
 
 const testOutputChannel = vscode.window.createOutputChannel('Go Tests');
@@ -494,17 +494,6 @@
 	});
 }
 
-function expandFilePathInOutput(output: string, cwd: string): string {
-	const lines = output.split('\n');
-	for (let i = 0; i < lines.length; i++) {
-		const matches = lines[i].match(/\s*(\S+\.go):(\d+):/);
-		if (matches && matches[1] && !path.isAbsolute(matches[1])) {
-			lines[i] = lines[i].replace(matches[1], path.join(cwd, matches[1]));
-		}
-	}
-	return lines.join('\n');
-}
-
 /**
  * Get the test target arguments.
  *
diff --git a/src/util.ts b/src/util.ts
index 051f017..b7152be 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -24,7 +24,7 @@
 	getCurrentGoRoot,
 	getInferredGopath,
 	resolveHomeDir,
-} from './utils/goPath';
+} from './utils/pathUtils';
 import { killProcessTree } from './utils/processUtils';
 
 let userNameHash: number = 0;
diff --git a/src/utils/goPath.ts b/src/utils/pathUtils.ts
similarity index 92%
rename from src/utils/goPath.ts
rename to src/utils/pathUtils.ts
index 3c69d6e..ab3be8f 100644
--- a/src/utils/goPath.ts
+++ b/src/utils/pathUtils.ts
@@ -227,3 +227,19 @@
 	}
 	return tool;
 }
+
+/**
+ * Returns output with relative filepaths expanded using the provided directory
+ * @param output
+ * @param cwd
+ */
+export function expandFilePathInOutput(output: string, cwd: string): string {
+	const lines = output.split('\n');
+	for (let i = 0; i < lines.length; i++) {
+		const matches = lines[i].match(/\s*(\S+\.go):(\d+):/);
+		if (matches && matches[1] && !path.isAbsolute(matches[1])) {
+			lines[i] = lines[i].replace(matches[1], path.join(cwd, matches[1]));
+		}
+	}
+	return lines.join('\n');
+}
diff --git a/test/integration/install.test.ts b/test/integration/install.test.ts
index e61f606..9a3732e 100644
--- a/test/integration/install.test.ts
+++ b/test/integration/install.test.ts
@@ -17,7 +17,7 @@
 import { installTools } from '../../src/goInstallTools';
 import { allToolsInformation, getTool, getToolAtVersion } from '../../src/goTools';
 import { getBinPath, getGoVersion, rmdirRecursive } from '../../src/util';
-import { correctBinname } from '../../src/utils/goPath';
+import { correctBinname } from '../../src/utils/pathUtils';
 
 suite('Installation Tests', function () {
 	// Disable timeout when we are running slow tests.
diff --git a/test/integration/statusbar.test.ts b/test/integration/statusbar.test.ts
index 8396db6..10d1dcf 100644
--- a/test/integration/statusbar.test.ts
+++ b/test/integration/statusbar.test.ts
@@ -25,7 +25,7 @@
 import { updateGoVarsFromConfig } from '../../src/goInstallTools';
 import { getWorkspaceState, setWorkspaceState } from '../../src/stateUtils';
 import ourutil = require('../../src/util');
-import { getCurrentGoRoot } from '../../src/utils/goPath';
+import { getCurrentGoRoot } from '../../src/utils/pathUtils';
 import { MockMemento } from '../mocks/MockMemento';
 
 describe('#initGoStatusBar()', function () {