src/debugAdapter: fix strict type errors
For golang/vscode-go#57.
Change-Id: Iff105c0ce4b03e61e9fa8a05567fa99f71d1d217
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/403055
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index 840d148..f6867f4 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -357,7 +357,7 @@
logger.error(logArgsToString(args));
}
-export function findPathSeparator(filePath: string) {
+export function findPathSeparator(filePath: string | undefined) {
return filePath && filePath.includes('\\') ? '\\' : '/';
}
@@ -407,21 +407,21 @@
export class Delve {
public program: string;
- public remotePath: string;
- public loadConfig: LoadConfig;
+ public remotePath?: string;
+ public loadConfig?: LoadConfig;
public connection: Promise<RPCConnection | null>; // null if connection isn't necessary (e.g. noDebug mode)
- public onstdout: (str: string) => void;
- public onstderr: (str: string) => void;
- public onclose: (code: number) => void;
- public noDebug: boolean;
+ public onstdout?: (str: string) => void;
+ public onstderr?: (str: string) => void;
+ public onclose?: (code: number) => void;
+ public noDebug?: boolean;
public isApiV1: boolean;
public dlvEnv: any;
public stackTraceDepth: number;
- public isRemoteDebugging: boolean;
- public goroot: string;
+ public isRemoteDebugging?: boolean;
+ public goroot?: string;
public delveConnectionClosed = false;
private localDebugeePath: string | undefined;
- private debugProcess: ChildProcess;
+ private debugProcess?: ChildProcess;
private request: 'attach' | 'launch';
constructor(launchArgs: LaunchRequestArguments | AttachRequestArguments, program: string) {
@@ -452,15 +452,18 @@
if (mode === 'remote') {
log(`Start remote debugging: connecting ${launchArgs.host}:${launchArgs.port}`);
- this.debugProcess = null;
+ this.debugProcess = undefined;
this.isRemoteDebugging = true;
this.goroot = await queryGOROOT(dlvCwd, process.env);
serverRunning = true; // assume server is running when in remote mode
+ if (!launchArgs.port || !launchArgs.host) {
+ return reject('Unable to connect, missing host or port from launchArgs.');
+ }
connectClient(launchArgs.port, launchArgs.host, this.onclose);
return;
}
this.isRemoteDebugging = false;
- let env: NodeJS.ProcessEnv;
+ let env: NodeJS.ProcessEnv | undefined;
if (launchArgs.request === 'launch') {
let isProgramDirectory = false;
// Validations on the program
@@ -570,13 +573,13 @@
log(`Running: ${output} ${run.join(' ')}`);
this.debugProcess = spawn(output, run, runOptions);
- this.debugProcess.stderr.on('data', (chunk) => {
+ this.debugProcess.stderr?.on('data', (chunk) => {
const str = chunk.toString();
if (this.onstderr) {
this.onstderr(str);
}
});
- this.debugProcess.stdout.on('data', (chunk) => {
+ this.debugProcess.stdout?.on('data', (chunk) => {
const str = chunk.toString();
if (this.onstdout) {
this.onstdout(str);
@@ -584,9 +587,9 @@
});
this.debugProcess.on('close', (code) => {
if (code) {
- logError(`Process exiting with code: ${code} signal: ${this.debugProcess.killed}`);
+ logError(`Process exiting with code: ${code} signal: ${this.debugProcess?.killed}`);
} else {
- log(`Process exiting normally ${this.debugProcess.killed}`);
+ log(`Process exiting normally ${this.debugProcess?.killed}`);
}
if (this.onclose) {
this.onclose(code);
@@ -674,7 +677,7 @@
if (launchArgs.dlvFlags && launchArgs.dlvFlags.length > 0) {
dlvArgs.push(...launchArgs.dlvFlags);
}
- dlvArgs.push('--headless=true', '--listen=' + launchArgs.host + ':' + launchArgs.port.toString());
+ dlvArgs.push('--headless=true', '--listen=' + launchArgs.host + ':' + launchArgs.port?.toString());
if (!this.isApiV1) {
dlvArgs.push('--api-version=2');
}
@@ -716,19 +719,22 @@
}, 200);
}
- this.debugProcess.stderr.on('data', (chunk) => {
+ this.debugProcess.stderr?.on('data', (chunk) => {
const str = chunk.toString();
if (this.onstderr) {
this.onstderr(str);
}
});
- this.debugProcess.stdout.on('data', (chunk) => {
+ this.debugProcess.stdout?.on('data', (chunk) => {
const str = chunk.toString();
if (this.onstdout) {
this.onstdout(str);
}
if (!serverRunning) {
serverRunning = true;
+ if (!launchArgs.port || !launchArgs.host) {
+ return reject('Unable to connect, missing host or port from launchArgs.');
+ }
connectClient(launchArgs.port, launchArgs.host, this.onclose);
}
});
@@ -745,13 +751,13 @@
});
}
- public call<T>(command: string, args: any[], callback: (err: Error, results: T) => void) {
+ public call<T>(command: string, args: any[], callback: (err: Error, results?: T) => void) {
this.connection.then(
(conn) => {
- conn.call('RPCServer.' + command, args, callback);
+ conn?.call('RPCServer.' + command, args, callback);
},
(err) => {
- callback(err, null);
+ callback(err);
}
);
}
@@ -760,7 +766,7 @@
return new Promise<T>((resolve, reject) => {
this.connection.then(
(conn) => {
- conn.call<T>(`RPCServer.${command}`, args, (err, res) => {
+ conn?.call<T>(`RPCServer.${command}`, args, (err, res) => {
return err ? reject(err) : resolve(res);
});
},
@@ -802,9 +808,13 @@
*/
public async close(): Promise<void> {
const forceCleanup = async () => {
- log(`killing debugee (pid: ${this.debugProcess.pid})...`);
- await killProcessTree(this.debugProcess, log);
- await removeFile(this.localDebugeePath);
+ log(`killing debugee (pid: ${this.debugProcess?.pid})...`);
+ if (this.debugProcess) {
+ await killProcessTree(this.debugProcess, log);
+ }
+ if (this.localDebugeePath) {
+ await removeFile(this.localDebugeePath);
+ }
};
if (this.noDebug) {
@@ -827,7 +837,7 @@
(rpcConnection as any)['conn']['end']();
return resolve();
}
- const timeoutToken: NodeJS.Timer =
+ const timeoutToken =
isLocalDebugging &&
setTimeout(async () => {
log('Killing debug process manually as we could not halt delve in time');
@@ -835,27 +845,28 @@
resolve();
}, 1000);
- let haltErrMsg: string;
+ let haltErrMsg: string | undefined;
try {
log('HaltRequest');
await this.callPromise('Command', [{ name: 'halt' }]);
} catch (err) {
log('HaltResponse');
- haltErrMsg = err ? err.toString() : '';
- log(`Failed to halt - ${haltErrMsg}`);
+ log(`Failed to halt - ${err}`);
}
- clearTimeout(timeoutToken);
+ if (timeoutToken) {
+ clearTimeout(timeoutToken);
+ }
- const targetHasExited: boolean = haltErrMsg && haltErrMsg.endsWith('has exited with status 0');
- const shouldDetach: boolean = !haltErrMsg || targetHasExited;
- let shouldForceClean: boolean = !shouldDetach && isLocalDebugging;
+ const targetHasExited = !!haltErrMsg && haltErrMsg.endsWith('has exited with status 0');
+ const shouldDetach = !haltErrMsg || targetHasExited;
+ let shouldForceClean = !shouldDetach && isLocalDebugging;
if (shouldDetach) {
log('DetachRequest');
try {
await this.callPromise('Detach', [this.isApiV1 ? true : { Kill: isLocalDebugging }]);
} catch (err) {
log('DetachResponse');
- logError(`Failed to detach - ${err.toString() || ''}`);
+ logError(`Failed to detach - ${err}`);
shouldForceClean = isLocalDebugging;
}
}
@@ -878,10 +889,10 @@
// Editing breakpoints requires halting delve, skip sending Stop Event to VS Code in such cases
private skipStopEventOnce: boolean;
private overrideStopReason: string;
- private debugState: DebuggerState;
- private delve: Delve;
- private localPathSeparator: string;
- private remotePathSeparator: string;
+ private debugState?: DebuggerState;
+ private delve?: Delve;
+ private localPathSeparator?: string;
+ private remotePathSeparator?: string;
private stackFrameHandles: Handles<[number, number]>;
private packageInfo = new Map<string, string>();
private stopOnEntry: boolean;
@@ -892,7 +903,7 @@
private remoteToLocalPathMapping = new Map<string, string>();
// TODO(suzmue): Use delve's implementation of substitute-path.
- private substitutePath: { from: string; to: string }[];
+ private substitutePath?: { from: string; to: string }[];
private showGlobalVariables = false;
@@ -906,8 +917,6 @@
this.skipStopEventOnce = false;
this.overrideStopReason = '';
this.stopOnEntry = false;
- this.debugState = null;
- this.delve = null;
this.breakpoints = new Map<string, DebugBreakpoint[]>();
this.stackFrameHandles = new Handles<[number, number]>();
}
@@ -918,6 +927,7 @@
): void {
log('InitializeRequest');
// Set the capabilities that this debug adapter supports.
+ response.body = response.body ?? {};
response.body.supportsConditionalBreakpoints = true;
response.body.supportsConfigurationDoneRequest = true;
response.body.supportsSetVariable = true;
@@ -990,21 +1000,21 @@
// disconnect multiple times when a disconnect request is still running.
// The order of the execution may results in strange states that don't allow
// the delve connection to fully disconnect.
- if (this.delve.delveConnectionClosed) {
+ if (this.delve?.delveConnectionClosed) {
log("Skip disconnectRequestHelper as Delve's connection is already closed.");
return;
}
// For remote process, we have to issue a continue request
// before disconnecting.
- if (this.delve.isRemoteDebugging) {
+ if (this.delve?.isRemoteDebugging) {
if (!(await this.isDebuggeeRunning())) {
log("Issuing a continue command before closing Delve's connection as the debuggee is not running.");
this.continue();
}
}
log('Closing Delve.');
- await this.delve.close();
+ await this.delve?.close();
}
protected async configurationDoneRequest(
@@ -1074,7 +1084,8 @@
}
const fileName = getBaseName(localPath);
- const potentialMatchingRemoteFiles = this.remoteSourcesAndPackages.remoteSourceFilesNameGrouping.get(fileName);
+ const potentialMatchingRemoteFiles =
+ this.remoteSourcesAndPackages.remoteSourceFilesNameGrouping.get(fileName) ?? [];
const bestMatchingRemoteFile = this.findPathWithBestMatchingSuffix(localPath, potentialMatchingRemoteFiles);
if (!bestMatchingRemoteFile) {
return;
@@ -1085,8 +1096,8 @@
}
protected async toDebuggerPath(filePath: string): Promise<string> {
- if (this.substitutePath.length === 0) {
- if (this.delve.isRemoteDebugging) {
+ if (this.substitutePath?.length === 0) {
+ if (this.delve?.isRemoteDebugging) {
// The user trusts us to infer the remote path mapping!
await this.initializeRemotePackagesAndSources();
const matchedRemoteFile = this.inferRemotePathFromLocalPath(filePath);
@@ -1103,7 +1114,7 @@
filePath = normalizeSeparators(filePath);
let substitutedPath = filePath;
let substituteRule: { from: string; to: string };
- this.substitutePath.forEach((value) => {
+ this.substitutePath?.forEach((value) => {
if (filePath.startsWith(value.from)) {
if (substituteRule) {
log(
@@ -1117,7 +1128,7 @@
});
filePath = substitutedPath;
- return (filePath = filePath.replace(/\/|\\/g, this.remotePathSeparator));
+ return (filePath = filePath.replace(/\/|\\/g, this.remotePathSeparator ?? ''));
}
/**
@@ -1145,11 +1156,11 @@
const fileName = getBaseName(remotePath);
const globSync = glob.sync(fileName, {
matchBase: true,
- cwd: this.delve.program
+ cwd: this.delve?.program
});
const bestMatchingLocalPath = this.findPathWithBestMatchingSuffix(remotePath, globSync);
if (bestMatchingLocalPath) {
- const fullLocalPath = path.join(this.delve.program, bestMatchingLocalPath);
+ const fullLocalPath = path.join(this.delve?.program ?? '', bestMatchingLocalPath);
this.remoteToLocalPathMapping.set(remotePath, fullLocalPath);
return fullLocalPath;
}
@@ -1199,7 +1210,7 @@
.join(this.localPathSeparator);
// Scenario 1: The package is inside the current working directory.
- const localWorkspacePath = path.join(this.delve.program, relativeRemotePath);
+ const localWorkspacePath = path.join(this.delve?.program ?? '', relativeRemotePath);
if (this.fileSystem.existsSync(localWorkspacePath)) {
return localWorkspacePath;
}
@@ -1271,7 +1282,7 @@
const localGoPathSrcPath = path.join(
gopath,
'src',
- relativeRemotePath.split(this.remotePathSeparator).join(this.localPathSeparator)
+ relativeRemotePath.split(this.remotePathSeparator ?? '').join(this.localPathSeparator)
);
if (this.fileSystem.existsSync(localGoPathSrcPath)) {
return localGoPathSrcPath;
@@ -1283,9 +1294,9 @@
* have been initialized.
*/
protected toLocalPath(pathToConvert: string): string {
- if (this.substitutePath.length === 0) {
+ if (this.substitutePath?.length === 0) {
// User trusts use to infer the path
- if (this.delve.isRemoteDebugging) {
+ if (this.delve?.isRemoteDebugging) {
const inferredPath = this.inferLocalPathFromRemotePath(pathToConvert);
if (inferredPath) {
return inferredPath;
@@ -1298,8 +1309,8 @@
// If there is a substitutePath mapping, then we replace the path.
pathToConvert = normalizeSeparators(pathToConvert);
let substitutedPath = pathToConvert;
- let substituteRule: { from: string; to: string };
- this.substitutePath.forEach((value) => {
+ let substituteRule: { from: string; to: string } | undefined;
+ this.substitutePath?.forEach((value) => {
if (pathToConvert.startsWith(value.to)) {
if (substituteRule) {
log(
@@ -1330,11 +1341,14 @@
if (gopath && indexGoModCache > 0) {
return path.join(
gopath,
- pathToConvert.substr(indexGoModCache).split(this.remotePathSeparator).join(this.localPathSeparator)
+ pathToConvert
+ .substr(indexGoModCache)
+ .split(this.remotePathSeparator ?? '')
+ .join(this.localPathSeparator)
);
}
}
- return pathToConvert.split(this.remotePathSeparator).join(this.localPathSeparator);
+ return pathToConvert.split(this.remotePathSeparator ?? '').join(this.localPathSeparator);
}
protected async setBreakPointsRequest(
@@ -1354,7 +1368,7 @@
}
log(`Halting before setting breakpoints. SkipStopEventOnce is ${this.skipStopEventOnce}.`);
- this.delve.callPromise('Command', [{ name: 'halt' }]).then(
+ this.delve?.callPromise('Command', [{ name: 'halt' }]).then(
() => {
return this.setBreakPoints(response, args).then(() => {
// We do not want to continue if it was running a next request, since the
@@ -1366,7 +1380,7 @@
this.sendEvent(new OutputEvent(warning, 'stderr'));
return;
}
- return this.continue(true).then(null, (err) => {
+ return this.continue(true).then(undefined, (err) => {
this.logDelveError(err, 'Failed to continue delve after halting it to set breakpoints');
});
});
@@ -1397,7 +1411,7 @@
return this.sendResponse(response);
}
log('ThreadsRequest');
- this.delve.call<DebugGoroutine[] | ListGoroutinesOut>('ListGoroutines', [], (err, out) => {
+ this.delve?.call<DebugGoroutine[] | ListGoroutinesOut>('ListGoroutines', [], (err, out) => {
if (this.debugState && this.debugState.exited) {
// If the program exits very quickly, the initial threadsRequest will complete after it has exited.
// A TerminatedEvent has already been sent. Ignore the err returned in this case.
@@ -1411,7 +1425,7 @@
e: err.toString()
});
}
- const goroutines = this.delve.isApiV1 ? <DebugGoroutine[]>out : (<ListGoroutinesOut>out).Goroutines;
+ const goroutines = this.delve?.isApiV1 ? <DebugGoroutine[]>out : (<ListGoroutinesOut>out).Goroutines;
log('goroutines', goroutines);
const threads = goroutines.map(
(goroutine) =>
@@ -1447,12 +1461,12 @@
// delve does not support frame paging, so we ask for a large depth
const goroutineId = args.threadId;
- const stackTraceIn = { id: goroutineId, depth: this.delve.stackTraceDepth };
- if (!this.delve.isApiV1) {
- Object.assign(stackTraceIn, { full: false, cfg: this.delve.loadConfig });
+ const stackTraceIn = { id: goroutineId, depth: this.delve?.stackTraceDepth };
+ if (!this.delve?.isApiV1) {
+ Object.assign(stackTraceIn, { full: false, cfg: this.delve?.loadConfig });
}
- this.delve.call<DebugLocation[] | StacktraceOut>(
- this.delve.isApiV1 ? 'StacktraceGoroutine' : 'Stacktrace',
+ this.delve?.call<DebugLocation[] | StacktraceOut>(
+ this.delve?.isApiV1 ? 'StacktraceGoroutine' : 'Stacktrace',
[stackTraceIn],
async (err, out) => {
if (err) {
@@ -1463,13 +1477,13 @@
'Unable to produce stack trace: "{e}"',
{ e: err.toString() },
// Disable showUser pop-up since errors already show up under the CALL STACK pane
- null
+ undefined
);
}
- const locations = this.delve.isApiV1 ? <DebugLocation[]>out : (<StacktraceOut>out).Locations;
+ const locations = this.delve?.isApiV1 ? <DebugLocation[]>out : (<StacktraceOut>out).Locations;
log('locations', locations);
- if (this.delve.isRemoteDebugging) {
+ if (this.delve?.isRemoteDebugging) {
await this.initializeRemotePackagesAndSources();
}
@@ -1479,17 +1493,18 @@
uniqueStackFrameId,
location.function ? location.function.name : '<unknown>',
location.file === '<autogenerated>'
- ? null
+ ? undefined
: new Source(path.basename(location.file), this.toLocalPath(location.file)),
location.line,
0
);
});
- if (args.startFrame > 0) {
- stackFrames = stackFrames.slice(args.startFrame);
+ const { startFrame = 0, levels = 0 } = args;
+ if (startFrame > 0) {
+ stackFrames = stackFrames.slice(startFrame);
}
- if (args.levels > 0) {
- stackFrames = stackFrames.slice(0, args.levels);
+ if (levels > 0) {
+ stackFrames = stackFrames.slice(0, levels);
}
response.body = { stackFrames, totalFrames: locations.length };
this.sendResponse(response);
@@ -1506,9 +1521,9 @@
// in the error response.
const [goroutineId, frameId] = this.stackFrameHandles.get(args.frameId);
const listLocalVarsIn = { goroutineID: goroutineId, frame: frameId };
- this.delve.call<DebugVariable[] | ListVarsOut>(
+ this.delve?.call<DebugVariable[] | ListVarsOut>(
'ListLocalVars',
- this.delve.isApiV1 ? [listLocalVarsIn] : [{ scope: listLocalVarsIn, cfg: this.delve.loadConfig }],
+ this.delve?.isApiV1 ? [listLocalVarsIn] : [{ scope: listLocalVarsIn, cfg: this.delve?.loadConfig }],
(err, out) => {
if (err) {
this.logDelveError(err, 'Failed to get list local variables');
@@ -1516,15 +1531,15 @@
e: err.toString()
});
}
- const locals = this.delve.isApiV1 ? <DebugVariable[]>out : (<ListVarsOut>out).Variables;
+ const locals = this.delve?.isApiV1 ? <DebugVariable[]>out : (<ListVarsOut>out).Variables;
log('locals', locals);
this.addFullyQualifiedName(locals);
const listLocalFunctionArgsIn = { goroutineID: goroutineId, frame: frameId };
- this.delve.call<DebugVariable[] | ListFunctionArgsOut>(
+ this.delve?.call<DebugVariable[] | ListFunctionArgsOut>(
'ListFunctionArgs',
- this.delve.isApiV1
+ this.delve?.isApiV1
? [listLocalFunctionArgsIn]
- : [{ scope: listLocalFunctionArgsIn, cfg: this.delve.loadConfig }],
+ : [{ scope: listLocalFunctionArgsIn, cfg: this.delve?.loadConfig }],
(listFunctionErr, outArgs) => {
if (listFunctionErr) {
this.logDelveError(listFunctionErr, 'Failed to list function args');
@@ -1532,7 +1547,7 @@
e: listFunctionErr.toString()
});
}
- const vars = this.delve.isApiV1
+ const vars = this.delve?.isApiV1
? <DebugVariable[]>outArgs
: (<ListFunctionArgsOut>outArgs).Args;
log('functionArgs', vars);
@@ -1550,7 +1565,7 @@
indices.push(i);
shadowedVars.set(varName, indices);
} else {
- shadowedVars.get(varName).push(i);
+ shadowedVars.get(varName)?.push(i);
}
}
for (const svIndices of shadowedVars.values()) {
@@ -1602,9 +1617,9 @@
return;
}
const filter = `^${packageName}\\.`;
- this.delve.call<DebugVariable[] | ListVarsOut>(
+ this.delve?.call<DebugVariable[] | ListVarsOut>(
'ListPackageVars',
- this.delve.isApiV1 ? [filter] : [{ filter, cfg: this.delve.loadConfig }],
+ this.delve?.isApiV1 ? [filter] : [{ filter, cfg: this.delve?.loadConfig }],
(listPkgVarsErr, listPkgVarsOut) => {
if (listPkgVarsErr) {
this.logDelveError(listPkgVarsErr, 'Failed to list global vars');
@@ -1615,7 +1630,7 @@
{ e: listPkgVarsErr.toString() }
);
}
- const globals = this.delve.isApiV1
+ const globals = this.delve?.isApiV1
? <DebugVariable[]>listPkgVarsOut
: (<ListVarsOut>listPkgVarsOut).Variables;
let initdoneIndex = -1;
@@ -1667,7 +1682,7 @@
): void {
log('VariablesRequest');
const vari = this.variableHandles.get(args.variablesReference);
- let variablesPromise: Promise<DebugProtocol.Variable[]>;
+ let variablesPromise: Promise<DebugProtocol.Variable[]> | undefined;
const loadChildren = async (exp: string, v: DebugVariable) => {
// from https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables
if (
@@ -1676,7 +1691,7 @@
) {
await this.evaluateRequestImpl({ expression: exp }).then(
(result) => {
- const variable = this.delve.isApiV1 ? <DebugVariable>result : (<EvalOut>result).Variable;
+ const variable = this.delve?.isApiV1 ? <DebugVariable>result : (<EvalOut>result).Variable;
v.children = variable.children;
},
(err) => this.logDelveError(err, 'Failed to evaluate expression')
@@ -1718,11 +1733,11 @@
value: mapValue.result,
evaluateName: vari.fullyQualifiedName + '[' + mapKey.result + ']',
variablesReference: mapValue.variablesReference
- };
+ } as DebugProtocol.Variable;
});
}
})
- .filter((v) => v != null) // remove the null values created by combining keys and values
+ .filter((v): v is Promise<DebugProtocol.Variable> => !!v) // remove the null values created by combining keys and values
);
} else {
variablesPromise = Promise.all(
@@ -1762,7 +1777,7 @@
this.nextRequestRunning = true;
log('NextRequest');
- this.delve.call<DebuggerState | CommandOut>('Command', [{ name: 'next' }], (err, out) => {
+ this.delve?.call<DebuggerState | CommandOut>('Command', [{ name: 'next' }], (err, out) => {
if (closureEpoch === this.continueEpoch) {
this.nextRequestRunning = false;
}
@@ -1770,7 +1785,7 @@
if (err) {
this.logDelveError(err, 'Failed to next');
}
- const state = this.delve.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
+ const state = this.delve?.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
log('next state', state);
this.debugState = state;
this.handleReenterDebug('step');
@@ -1787,7 +1802,7 @@
this.nextRequestRunning = true;
log('StepInRequest');
- this.delve.call<DebuggerState | CommandOut>('Command', [{ name: 'step' }], (err, out) => {
+ this.delve?.call<DebuggerState | CommandOut>('Command', [{ name: 'step' }], (err, out) => {
if (closureEpoch === this.continueEpoch) {
this.nextRequestRunning = false;
}
@@ -1795,7 +1810,7 @@
if (err) {
this.logDelveError(err, 'Failed to step in');
}
- const state = this.delve.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
+ const state = this.delve?.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
log('stop state', state);
this.debugState = state;
this.handleReenterDebug('step');
@@ -1812,7 +1827,7 @@
this.nextRequestRunning = true;
log('StepOutRequest');
- this.delve.call<DebuggerState | CommandOut>('Command', [{ name: 'stepOut' }], (err, out) => {
+ this.delve?.call<DebuggerState | CommandOut>('Command', [{ name: 'stepOut' }], (err, out) => {
if (closureEpoch === this.continueEpoch) {
this.nextRequestRunning = false;
}
@@ -1820,7 +1835,7 @@
if (err) {
this.logDelveError(err, 'Failed to step out');
}
- const state = this.delve.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
+ const state = this.delve?.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
log('stepout state', state);
this.debugState = state;
this.handleReenterDebug('step');
@@ -1833,14 +1848,14 @@
protected pauseRequest(response: DebugProtocol.PauseResponse): void {
log('PauseRequest');
- this.delve.call<DebuggerState | CommandOut>('Command', [{ name: 'halt' }], (err, out) => {
+ this.delve?.call<DebuggerState | CommandOut>('Command', [{ name: 'halt' }], (err, out) => {
if (err) {
this.logDelveError(err, 'Failed to halt');
return this.sendErrorResponse(response, 2010, 'Unable to halt execution: "{e}"', {
e: err.toString()
});
}
- const state = this.delve.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
+ const state = this.delve?.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
log('pause state', state);
this.debugState = state;
this.handleReenterDebug('pause');
@@ -1860,7 +1875,7 @@
// Captures pattern that looks like the expression that starts with `call<space>`
// command call. This is supported only with APIv2.
const isCallCommand = args.expression.match(/^\s*call\s+\S+/);
- if (!this.delve.isApiV1 && isCallCommand) {
+ if (!this.delve?.isApiV1 && isCallCommand) {
this.evaluateCallImpl(args).then(
(out) => {
const state = (<CommandOut>out).State;
@@ -1892,7 +1907,7 @@
{
e: err.toString()
},
- args.context === 'watch' ? null : ErrorDestination.User
+ args.context === 'watch' ? undefined : ErrorDestination.User
);
}
);
@@ -1901,7 +1916,7 @@
// Now handle it as a conventional evaluateRequest.
this.evaluateRequestImpl(args).then(
(out) => {
- const variable = this.delve.isApiV1 ? <DebugVariable>out : (<EvalOut>out).Variable;
+ const variable = this.delve?.isApiV1 ? <DebugVariable>out : (<EvalOut>out).Variable;
// #2326: Set the fully qualified name for variable mapping
variable.fullyQualifiedName = variable.name;
response.body = this.convertDebugVariableToProtocolVariable(variable);
@@ -1919,7 +1934,7 @@
{
e: err.toString()
},
- args.context === 'watch' ? null : ErrorDestination.User
+ args.context === 'watch' ? undefined : ErrorDestination.User
);
}
);
@@ -1931,14 +1946,14 @@
): void {
log('SetVariableRequest');
const scope = {
- goroutineID: this.debugState.currentGoroutine.id
+ goroutineID: this.debugState?.currentGoroutine.id
};
const setSymbolArgs = {
Scope: scope,
Symbol: args.name,
Value: args.value
};
- this.delve.call(this.delve.isApiV1 ? 'SetSymbol' : 'Set', [setSymbolArgs], (err) => {
+ this.delve?.call(this.delve?.isApiV1 ? 'SetSymbol' : 'Set', [setSymbolArgs], (err) => {
if (err) {
const errMessage = `Failed to set variable: ${err.toString()}`;
this.logDelveError(err, 'Failed to set variable');
@@ -1951,8 +1966,8 @@
}
private getGOROOT(): string {
- if (this.delve && this.delve.goroot) {
- return this.delve.goroot;
+ if (this.delve && this.delve?.goroot) {
+ return this.delve?.goroot;
}
return process.env['GOROOT'] || '';
// this is a workaround to keep the tests in integration/goDebug.test.ts running.
@@ -1987,9 +2002,9 @@
args.host = '127.0.0.1';
}
- let localPath: string;
+ let localPath = '';
if (args.request === 'attach') {
- localPath = args.cwd;
+ localPath = args.cwd ?? '';
} else if (args.request === 'launch') {
localPath = args.program;
}
@@ -2003,17 +2018,17 @@
if (args.remotePath.length > 0) {
this.remotePathSeparator = findPathSeparator(args.remotePath);
- const llist = localPath.split(/\/|\\/).reverse();
+ const llist = localPath?.split(/\/|\\/).reverse();
const rlist = args.remotePath.split(/\/|\\/).reverse();
let i = 0;
- for (; i < llist.length; i++) {
+ for (; llist && i < llist.length; i++) {
if (llist[i] !== rlist[i] || llist[i] === 'src') {
break;
}
}
if (i) {
- localPath = llist.reverse().slice(0, -i).join(this.localPathSeparator) + this.localPathSeparator;
+ localPath = llist?.reverse().slice(0, -i).join(this.localPathSeparator) + this.localPathSeparator;
args.remotePath =
rlist.reverse().slice(0, -i).join(this.remotePathSeparator) + this.remotePathSeparator;
} else if (
@@ -2036,7 +2051,7 @@
if (!this.remotePathSeparator) {
this.remotePathSeparator = findPathSeparator(value.to);
}
- this.substitutePath.push({
+ this.substitutePath?.push({
from: normalizeSeparators(value.from),
to: normalizeSeparators(value.to)
});
@@ -2062,10 +2077,10 @@
this.sendEvent(new TerminatedEvent());
};
- this.delve.connection.then(
+ this.delve?.connection.then(
() => {
- if (!this.delve.noDebug) {
- this.delve.call<GetVersionOut>('GetVersion', [], (err, out) => {
+ if (!this.delve?.noDebug) {
+ this.delve?.call<GetVersionOut>('GetVersion', [], (err, out) => {
if (err) {
logError(err);
return this.sendErrorResponse(
@@ -2075,9 +2090,9 @@
{ e: err.toString() }
);
}
- const clientVersion = this.delve.isApiV1 ? 1 : 2;
- if (out.APIVersion !== clientVersion) {
- const errorMessage = `The remote server is running on delve v${out.APIVersion} API and the client is running v${clientVersion} API. Change the version used on the client by using the property "apiVersion" in your launch.json file.`;
+ const clientVersion = this.delve?.isApiV1 ? 1 : 2;
+ if (out?.APIVersion !== clientVersion) {
+ const errorMessage = `The remote server is running on delve v${out?.APIVersion} API and the client is running v${clientVersion} API. Change the version used on the client by using the property "apiVersion" in your launch.json file.`;
logError(errorMessage);
return this.sendErrorResponse(response, 3000, errorMessage);
}
@@ -2107,6 +2122,9 @@
}
if (!this.remoteSourcesAndPackages.initializingRemoteSourceFiles) {
+ if (!this.delve) {
+ return;
+ }
try {
await this.remoteSourcesAndPackages.initializeRemotePackagesAndSources(this.delve);
} catch (error) {
@@ -2132,26 +2150,26 @@
response: DebugProtocol.SetBreakpointsResponse,
args: DebugProtocol.SetBreakpointsArguments
): Promise<void> {
- const file = normalizePath(args.source.path);
+ const file = normalizePath(args.source.path ?? '');
if (!this.breakpoints.get(file)) {
this.breakpoints.set(file, []);
}
const remoteFile = await this.toDebuggerPath(file);
return Promise.all(
- this.breakpoints.get(file).map((existingBP) => {
+ this.breakpoints.get(file)?.map((existingBP) => {
log('Clearing: ' + existingBP.id);
- return this.delve.callPromise('ClearBreakpoint', [
- this.delve.isApiV1 ? existingBP.id : { Id: existingBP.id }
+ return this.delve?.callPromise('ClearBreakpoint', [
+ this.delve?.isApiV1 ? existingBP.id : { Id: existingBP.id }
]);
- })
+ }) ?? []
)
.then(() => {
log('All cleared');
let existingBreakpoints: DebugBreakpoint[] | undefined;
return Promise.all(
- args.breakpoints.map((breakpoint) => {
- if (this.delve.remotePath.length === 0) {
+ args.breakpoints?.map((breakpoint) => {
+ if (this.delve?.remotePath?.length === 0) {
log('Creating on: ' + file + ':' + breakpoint.line);
} else {
log('Creating on: ' + file + ' (' + remoteFile + ') :' + breakpoint.line);
@@ -2159,14 +2177,14 @@
const breakpointIn = <DebugBreakpoint>{};
breakpointIn.file = remoteFile;
breakpointIn.line = breakpoint.line;
- breakpointIn.loadArgs = this.delve.loadConfig;
- breakpointIn.loadLocals = this.delve.loadConfig;
+ breakpointIn.loadArgs = this.delve?.loadConfig;
+ breakpointIn.loadLocals = this.delve?.loadConfig;
breakpointIn.cond = breakpoint.condition;
return this.delve
- .callPromise('CreateBreakpoint', [
- this.delve.isApiV1 ? breakpointIn : { Breakpoint: breakpointIn }
+ ?.callPromise('CreateBreakpoint', [
+ this.delve?.isApiV1 ? breakpointIn : { Breakpoint: breakpointIn }
])
- .then(null, async (err) => {
+ .then(undefined, async (err) => {
// Delve does not seem to support error code at this time.
// TODO(quoct): Follow up with delve team.
if (err.toString().startsWith('Breakpoint exists at')) {
@@ -2175,14 +2193,14 @@
// Otherwise, we would not be able to clear the breakpoints.
if (!existingBreakpoints) {
try {
- const listBreakpointsResponse = await this.delve.callPromise<
+ const listBreakpointsResponse = await this.delve?.callPromise<
ListBreakpointsOut | DebugBreakpoint[]
- >('ListBreakpoints', this.delve.isApiV1 ? [] : [{}]);
- existingBreakpoints = this.delve.isApiV1
+ >('ListBreakpoints', this.delve?.isApiV1 ? [] : [{}]);
+ existingBreakpoints = this.delve?.isApiV1
? (listBreakpointsResponse as DebugBreakpoint[])
: (listBreakpointsResponse as ListBreakpointsOut).Breakpoints;
} catch (error) {
- log('Error listing breakpoints: ' + error.toString());
+ log('Error listing breakpoints: ' + error);
return null;
}
}
@@ -2198,17 +2216,17 @@
log(`Cannot match breakpoint ${breakpointIn} with existing breakpoints.`);
return null;
}
- return this.delve.isApiV1 ? matchedBreakpoint : { Breakpoint: matchedBreakpoint };
+ return this.delve?.isApiV1 ? matchedBreakpoint : { Breakpoint: matchedBreakpoint };
}
log('Error on CreateBreakpoint: ' + err.toString());
return null;
});
- })
+ }) ?? []
);
})
.then((newBreakpoints) => {
- let convertedBreakpoints: DebugBreakpoint[];
- if (!this.delve.isApiV1) {
+ let convertedBreakpoints: (DebugBreakpoint | null)[];
+ if (!this.delve?.isApiV1) {
// Unwrap breakpoints from v2 apicall
convertedBreakpoints = newBreakpoints.map((bp, i) => {
return bp ? (bp as CreateBreakpointOut).Breakpoint : null;
@@ -2222,12 +2240,12 @@
if (bp) {
return { verified: true, line: bp.line };
} else {
- return { verified: false, line: args.lines[i] };
+ return { verified: false, line: args.lines?.[i] };
}
});
this.breakpoints.set(
file,
- convertedBreakpoints.filter((x) => !!x)
+ convertedBreakpoints.filter((x): x is DebugBreakpoint => !!x)
);
return breakpoints;
})
@@ -2246,15 +2264,15 @@
);
}
- private async getPackageInfo(debugState: DebuggerState): Promise<string | void> {
- if (!debugState.currentThread || !debugState.currentThread.file) {
- return Promise.resolve(null);
+ private async getPackageInfo(debugState: DebuggerState | undefined): Promise<string | void> {
+ if (!debugState || !debugState.currentThread || !debugState.currentThread.file) {
+ return Promise.resolve();
}
- if (this.delve.isRemoteDebugging) {
+ if (this.delve?.isRemoteDebugging) {
await this.initializeRemotePackagesAndSources();
}
const dir = path.dirname(
- this.delve.remotePath.length || this.delve.isRemoteDebugging
+ this.delve?.remotePath?.length || this.delve?.isRemoteDebugging
? this.toLocalPath(debugState.currentThread.file)
: debugState.currentThread.file
);
@@ -2265,7 +2283,7 @@
execFile(
getBinPathWithPreferredGopathGoroot('go', []),
['list', '-f', '{{.Name}} {{.ImportPath}}'],
- { cwd: dir, env: this.delve.dlvEnv },
+ { cwd: dir, env: this.delve?.dlvEnv },
(err, stdout, stderr) => {
if (err || stderr || !stdout) {
logError(`go list failed on ${dir}: ${stderr || err}`);
@@ -2440,7 +2458,7 @@
log(`handleReenterDebug(${reason}).`);
this.cleanupHandles();
- if (this.debugState.exited) {
+ if (this.debugState?.exited) {
this.sendEvent(new TerminatedEvent());
log('TerminatedEvent');
} else {
@@ -2452,12 +2470,12 @@
// TODO(polina): validate the assumption in this code that the first goroutine
// is the current one. So far it appears to me that this is always the main goroutine
// with id 1.
- this.delve.call<DebugGoroutine[] | ListGoroutinesOut>('ListGoroutines', [{ count: 1 }], (err, out) => {
+ this.delve?.call<DebugGoroutine[] | ListGoroutinesOut>('ListGoroutines', [{ count: 1 }], (err, out) => {
if (err) {
this.logDelveError(err, 'Failed to get threads');
}
- const goroutines = this.delve.isApiV1 ? <DebugGoroutine[]>out : (<ListGoroutinesOut>out).Goroutines;
- if (!this.debugState.currentGoroutine && goroutines.length > 0) {
+ const goroutines = this.delve?.isApiV1 ? <DebugGoroutine[]>out : (<ListGoroutinesOut>out).Goroutines;
+ if (this.debugState && !this.debugState?.currentGoroutine && goroutines.length > 0) {
this.debugState.currentGoroutine = goroutines[0];
}
@@ -2474,7 +2492,7 @@
this.overrideStopReason = '';
}
- const stoppedEvent = new StoppedEvent(reason, this.debugState.currentGoroutine.id);
+ const stoppedEvent = new StoppedEvent(reason, this.debugState?.currentGoroutine.id);
(<any>stoppedEvent.body).allThreadsStopped = true;
this.sendEvent(stoppedEvent);
log('StoppedEvent("' + reason + '")');
@@ -2495,8 +2513,8 @@
return false;
}
try {
- this.debugState = await this.delve.getDebugState();
- return this.debugState.Running;
+ this.debugState = await this.delve?.getDebugState();
+ return !!this.debugState?.Running;
} catch (error) {
this.logDelveError(error, 'Failed to get state');
// Fall back to the internal tracking.
@@ -2521,7 +2539,7 @@
if (closureEpoch === this.continueEpoch) {
this.continueRequestRunning = false;
}
- const state = this.delve.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
+ const state = this.delve?.isApiV1 ? <DebuggerState>out : (<CommandOut>out).State;
log('continue state', state);
this.debugState = state;
@@ -2543,7 +2561,7 @@
};
// If called when setting breakpoint internally, we want the error to bubble up.
- let errorCallback = null;
+ let errorCallback = (_: unknown): any => void 0;
if (!calledWhenSettingBreakpoint) {
errorCallback = (err: any) => {
if (err) {
@@ -2554,11 +2572,14 @@
};
}
- return this.delve.callPromise('Command', [{ name: 'continue' }]).then(callback, errorCallback);
+ if (this.delve) {
+ return this.delve.callPromise('Command', [{ name: 'continue' }]).then(callback, errorCallback);
+ }
+ return Promise.resolve();
}
// evaluateCallImpl expects args.expression starts with the 'call ' command.
- private evaluateCallImpl(args: DebugProtocol.EvaluateArguments): Thenable<DebuggerState | CommandOut> {
+ private evaluateCallImpl(args: DebugProtocol.EvaluateArguments): Thenable<DebuggerState | CommandOut | void> {
const callExpr = args.expression.trimLeft().slice('call '.length);
// if args.frameID is 'not specified', expression is evaluated in the global scope, according to DAP.
// default to the topmost stack frame of the current goroutine
@@ -2570,11 +2591,11 @@
// See https://github.com/go-delve/delve/blob/328cf87808822693dc611591519689dcd42696a3/service/api/types.go#L321-L350
// for the command args for function call.
const returnValue = this.delve
- .callPromise<DebuggerState | CommandOut>('Command', [
+ ?.callPromise<DebuggerState | CommandOut>('Command', [
{
name: 'call',
goroutineID: goroutineId,
- returnInfoLoadConfig: this.delve.loadConfig,
+ returnInfoLoadConfig: this.delve?.loadConfig,
expr: callExpr,
unsafe: false
}
@@ -2591,10 +2612,10 @@
return Promise.reject(err);
}
);
- return returnValue;
+ return returnValue ?? Promise.resolve();
}
- private evaluateRequestImpl(args: DebugProtocol.EvaluateArguments): Thenable<EvalOut | DebugVariable> {
+ private evaluateRequestImpl(args: DebugProtocol.EvaluateArguments): Thenable<EvalOut | DebugVariable | void> {
// default to the topmost stack frame of the current goroutine
let goroutineId = -1;
let frameId = 0;
@@ -2613,11 +2634,11 @@
const apiV2Args = {
Expr: args.expression,
Scope: scope,
- Cfg: this.delve.loadConfig
+ Cfg: this.delve?.loadConfig
};
- const evalSymbolArgs = this.delve.isApiV1 ? apiV1Args : apiV2Args;
+ const evalSymbolArgs = this.delve?.isApiV1 ? apiV1Args : apiV2Args;
const returnValue = this.delve
- .callPromise<EvalOut | DebugVariable>(this.delve.isApiV1 ? 'EvalSymbol' : 'Eval', [evalSymbolArgs])
+ ?.callPromise<EvalOut | DebugVariable>(this.delve?.isApiV1 ? 'EvalSymbol' : 'Eval', [evalSymbolArgs])
.then(
(val) => val,
(err) => {
@@ -2630,7 +2651,7 @@
return Promise.reject(err);
}
);
- return returnValue;
+ return returnValue ?? Promise.resolve();
}
private addFullyQualifiedName(variables: DebugVariable[]) {
@@ -2669,32 +2690,32 @@
// Debugger may be stopped at this point but we still can (and need) to obtain state and stacktrace
let goroutineId = 0;
try {
- this.debugState = await this.delve.getDebugState();
+ this.debugState = await this.delve?.getDebugState();
// In some fault scenarios there may not be a currentGoroutine available from the debugger state
// Use the current thread
- if (!this.debugState.currentGoroutine) {
- goroutineId = this.debugState.currentThread.goroutineID;
+ if (!this.debugState?.currentGoroutine) {
+ goroutineId = this.debugState?.currentThread.goroutineID ?? 0;
} else {
- goroutineId = this.debugState.currentGoroutine.id;
+ goroutineId = this.debugState?.currentGoroutine.id ?? 0;
}
} catch (error) {
logError('dumpStacktrace - Failed to get debugger state ' + error);
}
// Get goroutine stacktrace
- const stackTraceIn = { id: goroutineId, depth: this.delve.stackTraceDepth };
- if (!this.delve.isApiV1) {
- Object.assign(stackTraceIn, { full: false, cfg: this.delve.loadConfig });
+ const stackTraceIn = { id: goroutineId, depth: this.delve?.stackTraceDepth };
+ if (!this.delve?.isApiV1) {
+ Object.assign(stackTraceIn, { full: false, cfg: this.delve?.loadConfig });
}
- this.delve.call<DebugLocation[] | StacktraceOut>(
- this.delve.isApiV1 ? 'StacktraceGoroutine' : 'Stacktrace',
+ this.delve?.call<DebugLocation[] | StacktraceOut>(
+ this.delve?.isApiV1 ? 'StacktraceGoroutine' : 'Stacktrace',
[stackTraceIn],
(err, out) => {
if (err) {
logError('dumpStacktrace: Failed to produce stack trace' + err);
return;
}
- const locations = this.delve.isApiV1 ? <DebugLocation[]>out : (<StacktraceOut>out).Locations;
+ const locations = this.delve?.isApiV1 ? <DebugLocation[]>out : (<StacktraceOut>out).Locations;
log('locations', locations);
const stackFrames = locations.map((location, frameId) => {
const uniqueStackFrameId = this.stackFrameHandles.create([goroutineId, frameId]);
@@ -2702,7 +2723,7 @@
uniqueStackFrameId,
location.function ? location.function.name : '<unknown>',
location.file === '<autogenerated>'
- ? null
+ ? undefined
: new Source(path.basename(location.file), this.toLocalPath(location.file)),
location.line,
0
@@ -2773,10 +2794,10 @@
if (!this.remoteSourceFilesNameGrouping.has(fileName)) {
this.remoteSourceFilesNameGrouping.set(fileName, []);
}
- this.remoteSourceFilesNameGrouping.get(fileName).push(sourceFile);
+ this.remoteSourceFilesNameGrouping.get(fileName)?.push(sourceFile);
});
} catch (error) {
- logError(`Failed to initialize remote sources and packages: ${error && error.message}`);
+ logError(`Failed to initialize remote sources and packages: ${(error as Error).message}`);
} finally {
this.emit(RemoteSourcesAndPackages.INITIALIZED);
this.initializedRemoteSourceFiles = true;
@@ -2797,7 +2818,7 @@
await fsUnlink(filePath);
}
} catch (e) {
- logError(`Potentially failed remove file: ${filePath} - ${e.toString() || ''}`);
+ logError(`Potentially failed remove file: ${filePath} - ${e}`);
}
}
diff --git a/src/utils/envUtils.ts b/src/utils/envUtils.ts
index 81b6f55..c872a88 100644
--- a/src/utils/envUtils.ts
+++ b/src/utils/envUtils.ts
@@ -43,7 +43,7 @@
}
}
-export function parseEnvFiles(envFiles: string[] | string): { [key: string]: string } {
+export function parseEnvFiles(envFiles: string[] | string | undefined): { [key: string]: string } {
const fileEnvs = [];
if (typeof envFiles === 'string') {
fileEnvs.push(parseEnvFile(envFiles));
diff --git a/src/utils/pathUtils.ts b/src/utils/pathUtils.ts
index 419cbf2..d514f8b 100644
--- a/src/utils/pathUtils.ts
+++ b/src/utils/pathUtils.ts
@@ -210,7 +210,7 @@
* @param gopath string Current Gopath. Can be ; or : separated (as per os) to support multiple paths
* @param currentFileDirPath string
*/
-export function getCurrentGoWorkspaceFromGOPATH(gopath: string, currentFileDirPath: string): string {
+export function getCurrentGoWorkspaceFromGOPATH(gopath: string | undefined, currentFileDirPath: string): string {
if (!gopath) {
return '';
}