src/language: fix strict type errors

For golang/vscode-go#57.

Change-Id: Iec4ee1a78692b913da6cc9f3ec9413367647da73
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/401618
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/goTest/explore.ts b/src/goTest/explore.ts
index 67facfa..8477d50 100644
--- a/src/goTest/explore.ts
+++ b/src/goTest/explore.ts
@@ -272,8 +272,6 @@
 		const found = find(this.ctrl.items);
 		if (found) {
 			dispose(this.resolver, found);
-		}
-		if (found?.parent) {
 			disposeIfEmpty(this.resolver, found.parent);
 		}
 	}
diff --git a/src/goTest/run.ts b/src/goTest/run.ts
index 0fdb9f2..75f425d 100644
--- a/src/goTest/run.ts
+++ b/src/goTest/run.ts
@@ -675,11 +675,11 @@
 		}
 
 		let current: Location | undefined;
+		if (!test.uri) return messages;
+		const dir = Uri.joinPath(test.uri, '..');
 		for (const line of output) {
 			const m = line.match(/^\s*(?<file>.*\.go):(?<line>\d+): ?(?<message>.*\n)$/);
 			if (m?.groups) {
-				if (!test.uri) return [];
-				const dir = Uri.joinPath(test.uri, '..');
 				const file = Uri.joinPath(dir, m.groups.file);
 				const ln = Number(m.groups.line) - 1; // VSCode uses 0-based line numbering (internally)
 				current = new Location(file, new Position(ln, 0));
diff --git a/src/goTest/utils.ts b/src/goTest/utils.ts
index de62641..6a4c6f6 100644
--- a/src/goTest/utils.ts
+++ b/src/goTest/utils.ts
@@ -101,7 +101,8 @@
 
 // Dispose of the item if it has no children, recursively. This facilitates
 // cleaning up package/file trees that contain no tests.
-export function disposeIfEmpty(resolver: GoTestResolver, item: vscode.TestItem) {
+export function disposeIfEmpty(resolver: GoTestResolver, item: vscode.TestItem | undefined) {
+	if (!item) return;
 	// Don't dispose of empty top-level items
 	const { kind } = GoTest.parseId(item.id);
 	if (kind === 'module' || kind === 'workspace' || (kind === 'package' && !item.parent)) {
@@ -113,9 +114,7 @@
 	}
 
 	dispose(resolver, item);
-	if (item.parent) {
-		disposeIfEmpty(resolver, item.parent);
-	}
+	disposeIfEmpty(resolver, item.parent);
 }
 
 // The 'name' group captures the module name, and the unnamed group ignores any comment that might follow the name.
diff --git a/src/language/goLanguageServer.ts b/src/language/goLanguageServer.ts
index 51f2903..3c8007c 100644
--- a/src/language/goLanguageServer.ts
+++ b/src/language/goLanguageServer.ts
@@ -68,7 +68,7 @@
 	serverName: string;
 	path: string;
 	version?: { version: string; goVersion?: string };
-	modtime: Date;
+	modtime?: Date;
 	enabled: boolean;
 	flags: string[];
 	env: any;
@@ -84,14 +84,14 @@
 // new configurations.
 // TODO: refactor it. These can be encapsulated in a single LanguageServer class
 // that keeps track of the state of the active language server instance.
-export let languageClient: LanguageClient;
-let languageServerDisposable: vscode.Disposable;
+export let languageClient: LanguageClient | undefined;
+let languageServerDisposable: vscode.Disposable | undefined;
 export let latestConfig: LanguageServerConfig;
-export let serverOutputChannel: vscode.OutputChannel;
+export let serverOutputChannel: vscode.OutputChannel | undefined;
 export let languageServerIsRunning = false;
 
 // serverInfo is the information from the server received during initialization.
-export let serverInfo: ServerInfo = undefined;
+export let serverInfo: ServerInfo | undefined;
 
 interface ServerInfo {
 	Name: string;
@@ -100,7 +100,7 @@
 	Commands?: string[];
 }
 
-let legacyLanguageService: LegacyLanguageService = undefined;
+let legacyLanguageService: LegacyLanguageService | undefined;
 
 const languageServerStartMutex = new Mutex();
 
@@ -165,7 +165,7 @@
 		}
 	}
 	const schemes = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.scheme);
-	if (schemes?.length > 0 && !schemes.includes('file') && !schemes.includes('untitled')) {
+	if (schemes && schemes.length > 0 && !schemes.includes('file') && !schemes.includes('untitled')) {
 		outputChannel.appendLine(
 			`None of the folders in this workspace ${schemes.join(
 				','
@@ -288,7 +288,7 @@
 export async function promptAboutGoplsOptOut() {
 	// Check if the configuration is set in the workspace.
 	const useLanguageServer = getGoConfig().inspect('useLanguageServer');
-	const workspace = useLanguageServer.workspaceFolderValue === false || useLanguageServer.workspaceValue === false;
+	const workspace = useLanguageServer?.workspaceFolderValue === false || useLanguageServer?.workspaceValue === false;
 
 	let cfg = getGoplsOptOutConfig(workspace);
 	const promptFn = async (): Promise<GoplsOptOutConfig> => {
@@ -401,10 +401,10 @@
 	legacyLanguageService?.dispose();
 	legacyLanguageService = undefined;
 
-	languageServerDisposable = languageClient.start();
-	ctx.subscriptions.push(languageServerDisposable);
-	await languageClient.onReady();
-	serverInfo = toServerInfo(languageClient.initializeResult);
+	languageServerDisposable = languageClient?.start();
+	languageServerDisposable && ctx.subscriptions.push(languageServerDisposable);
+	await languageClient?.onReady();
+	serverInfo = toServerInfo(languageClient?.initializeResult);
 
 	console.log(`Server: ${JSON.stringify(serverInfo, null, 2)}`);
 	return true;
@@ -572,7 +572,7 @@
 							'Show Trace'
 						);
 						if (answer === 'Show Trace') {
-							serverOutputChannel.show();
+							serverOutputChannel?.show();
 						}
 						return null;
 					}
@@ -596,10 +596,10 @@
 				): Promise<vscode.CodeLens[]> => {
 					const codeLens = await next(doc, token);
 					if (!codeLens || codeLens.length === 0) {
-						return codeLens;
+						return codeLens ?? [];
 					}
 					return codeLens.reduce((lenses: vscode.CodeLens[], lens: vscode.CodeLens) => {
-						switch (lens.command.title) {
+						switch (lens.command?.title) {
 							case 'run test': {
 								return [...lenses, ...createTestCodeLens(lens)];
 							}
@@ -753,7 +753,7 @@
 // and selects only those the user explicitly specifies in their settings.
 // This returns a new object created based on the filtered properties of workspaceConfig.
 // Exported for testing.
-export function filterGoplsDefaultConfigValues(workspaceConfig: any, resource: vscode.Uri): any {
+export function filterGoplsDefaultConfigValues(workspaceConfig: any, resource?: vscode.Uri): any {
 	if (!workspaceConfig) {
 		workspaceConfig = {};
 	}
@@ -816,8 +816,8 @@
 async function adjustGoplsWorkspaceConfiguration(
 	cfg: LanguageServerConfig,
 	workspaceConfig: any,
-	section: string,
-	resource: vscode.Uri
+	section?: string,
+	resource?: vscode.Uri
 ): Promise<any> {
 	// We process only gopls config
 	if (section !== 'gopls') {
@@ -843,19 +843,20 @@
 	// CodeLens argument signature in gopls is [fileName: string, testFunctions: string[], benchFunctions: string[]],
 	// so this needs to be deconstructured here
 	// Note that there will always only be one test function name in this context
-	if (lens.command.arguments.length < 2 || lens.command.arguments[1].length < 1) {
+	if ((lens.command?.arguments?.length ?? 0) < 2 || (lens.command?.arguments?.[1].length ?? 0) < 1) {
 		return [lens];
 	}
 	return [
 		new vscode.CodeLens(lens.range, {
+			title: '',
 			...lens.command,
 			command: 'go.test.cursor',
-			arguments: [{ functionName: lens.command.arguments[1][0] }]
+			arguments: [{ functionName: lens.command?.arguments?.[1][0] }]
 		}),
 		new vscode.CodeLens(lens.range, {
 			title: 'debug test',
 			command: 'go.debug.cursor',
-			arguments: [{ functionName: lens.command.arguments[1][0] }]
+			arguments: [{ functionName: lens.command?.arguments?.[1][0] }]
 		})
 	];
 }
@@ -864,19 +865,20 @@
 	// CodeLens argument signature in gopls is [fileName: string, testFunctions: string[], benchFunctions: string[]],
 	// so this needs to be deconstructured here
 	// Note that there will always only be one benchmark function name in this context
-	if (lens.command.arguments.length < 3 || lens.command.arguments[2].length < 1) {
+	if ((lens.command?.arguments?.length ?? 0) < 3 || (lens.command?.arguments?.[2].length ?? 0) < 1) {
 		return [lens];
 	}
 	return [
 		new vscode.CodeLens(lens.range, {
+			title: '',
 			...lens.command,
 			command: 'go.benchmark.cursor',
-			arguments: [{ functionName: lens.command.arguments[2][0] }]
+			arguments: [{ functionName: lens.command?.arguments?.[2][0] }]
 		}),
 		new vscode.CodeLens(lens.range, {
 			title: 'debug benchmark',
 			command: 'go.debug.cursor',
-			arguments: [{ functionName: lens.command.arguments[2][0] }]
+			arguments: [{ functionName: lens.command?.arguments?.[2][0] }]
 		})
 	];
 }
@@ -904,15 +906,15 @@
 }
 
 export function buildLanguageServerConfig(goConfig: vscode.WorkspaceConfiguration): LanguageServerConfig {
-	let formatter: GoDocumentFormattingEditProvider;
+	let formatter: GoDocumentFormattingEditProvider | undefined;
 	if (usingCustomFormatTool(goConfig)) {
 		formatter = new GoDocumentFormattingEditProvider();
 	}
 	const cfg: LanguageServerConfig = {
 		serverName: '',
 		path: '',
-		version: null, // compute version lazily
-		modtime: null,
+		version: undefined, // compute version lazily
+		modtime: undefined,
 		enabled: goConfig['useLanguageServer'] === true,
 		flags: goConfig['languageServerFlags'] || [],
 		features: {
@@ -932,7 +934,7 @@
 		return cfg;
 	}
 	cfg.path = languageServerPath;
-	cfg.serverName = getToolFromToolPath(cfg.path);
+	cfg.serverName = getToolFromToolPath(cfg.path) ?? '';
 
 	if (!cfg.enabled) {
 		return cfg;
@@ -958,7 +960,7 @@
  * Return the absolute path to the correct binary. If the required tool is not available,
  * prompt the user to install it. Only gopls is officially supported.
  */
-export function getLanguageServerToolPath(): string {
+export function getLanguageServerToolPath(): string | undefined {
 	const goConfig = getGoConfig();
 	// Check that all workspace folders are configured with the same GOPATH.
 	if (!allFoldersHaveSameGopath()) {
@@ -999,7 +1001,7 @@
 
 function gopathsPerFolder(): string[] {
 	const result: string[] = [];
-	for (const folder of vscode.workspace.workspaceFolders) {
+	for (const folder of vscode.workspace.workspaceFolders ?? []) {
 		result.push(getCurrentGoPath(folder.uri));
 	}
 	return result;
@@ -1009,7 +1011,7 @@
 	tool: Tool,
 	cfg: LanguageServerConfig,
 	mustCheck?: boolean
-): Promise<semver.SemVer> {
+): Promise<semver.SemVer | null | undefined> {
 	// Only support updating gopls for now.
 	if (tool.name !== 'gopls' || (!mustCheck && (cfg.checkForUpdates === 'off' || extensionInfo.isInCloudIDE))) {
 		return null;
@@ -1056,7 +1058,7 @@
 	// If the user has a pseudoversion, get the timestamp for the latest gopls version and compare.
 	if (usersTime) {
 		let latestTime = cfg.checkForUpdates
-			? await getTimestampForVersion(tool, latestVersion)
+			? await getTimestampForVersion(tool, latestVersion!)
 			: tool.latestVersionTimestamp;
 		if (!latestTime) {
 			latestTime = tool.latestVersionTimestamp;
@@ -1070,7 +1072,7 @@
 		includePrerelease: true,
 		loose: true
 	});
-	return semver.lt(usersVersionSemver, latestVersion) ? latestVersion : null;
+	return semver.lt(usersVersionSemver!, latestVersion!) ? latestVersion : null;
 }
 
 /**
@@ -1082,7 +1084,7 @@
  * 				configuration.
  * @returns		true if the tool was updated
  */
-async function suggestUpdateGopls(tool: Tool, cfg: LanguageServerConfig): Promise<boolean> {
+async function suggestUpdateGopls(tool: Tool, cfg: LanguageServerConfig): Promise<boolean | undefined> {
 	const forceUpdatedGoplsKey = 'forceUpdateForGoplsOnDefault';
 	// forceUpdated is true when the process of updating has been succesfully completed.
 	const forceUpdated = getFromGlobalState(forceUpdatedGoplsKey, false);
@@ -1111,7 +1113,7 @@
 // parseTimestampFromPseudoversion returns the timestamp for the given
 // pseudoversion. The timestamp is the center component, and it has the
 // format "YYYYMMDDHHmmss".
-function parseTimestampFromPseudoversion(version: string): moment.Moment {
+function parseTimestampFromPseudoversion(version: string): moment.Moment | null {
 	const split = version.split('-');
 	if (split.length < 2) {
 		return null;
@@ -1253,7 +1255,7 @@
 async function goProxyRequest(tool: Tool, endpoint: string): Promise<any> {
 	// Get the user's value of GOPROXY.
 	// If it is not set, we cannot make the request.
-	const output: string = process.env['GOPROXY'];
+	const output = process.env['GOPROXY'];
 	if (!output || !output.trim()) {
 		return null;
 	}
@@ -1309,7 +1311,7 @@
 	}
 
 	// Show the user the output channel content to alert them to the issue.
-	serverOutputChannel.show();
+	serverOutputChannel?.show();
 
 	if (latestConfig.serverName !== 'gopls') {
 		return;
@@ -1336,7 +1338,7 @@
 
 	// If the user has invalid values for "go.languageServerFlags", we may get
 	// this error. Prompt them to double check their flags.
-	let selected: string;
+	let selected: string | undefined;
 	if (failureReason === GoplsFailureModes.INCORRECT_COMMAND_USAGE) {
 		const languageServerFlags = getGoConfig()['languageServerFlags'] as string[];
 		if (languageServerFlags && languageServerFlags.length > 0) {
@@ -1445,8 +1447,8 @@
 		return;
 	}
 	// likely show() is asynchronous, despite the documentation
-	serverOutputChannel.show();
-	let found: vscode.TextDocument;
+	serverOutputChannel?.show();
+	let found: vscode.TextDocument | undefined;
 	for (const doc of vscode.workspace.textDocuments) {
 		if (doc.fileName.indexOf('extension-output-') !== -1) {
 			// despite show() above, this might not get the output we want, so check
@@ -1472,12 +1474,12 @@
 }
 
 async function collectGoplsLog(): Promise<{ sanitizedLog?: string; failureReason?: string }> {
-	serverOutputChannel.show();
+	serverOutputChannel?.show();
 	// Find the logs in the output channel. There is no way to read
 	// an output channel directly, but we can find the open text
 	// document, since we just surfaced the output channel to the user.
 	// See https://github.com/microsoft/vscode/issues/65108.
-	let logs: string;
+	let logs: string | undefined;
 	for (let i = 0; i < 10; i++) {
 		// try a couple of times until successfully finding the channel.
 		for (const doc of vscode.workspace.textDocuments) {
@@ -1572,5 +1574,5 @@
 
 function languageServerUsingDefault(cfg: vscode.WorkspaceConfiguration): boolean {
 	const useLanguageServer = cfg.inspect<boolean>('useLanguageServer');
-	return useLanguageServer.globalValue === undefined && useLanguageServer.workspaceValue === undefined;
+	return useLanguageServer?.globalValue === undefined && useLanguageServer?.workspaceValue === undefined;
 }
diff --git a/src/language/legacy/goCodeAction.ts b/src/language/legacy/goCodeAction.ts
index 488a5a9..c08d1f0 100644
--- a/src/language/legacy/goCodeAction.ts
+++ b/src/language/legacy/goCodeAction.ts
@@ -20,7 +20,7 @@
 		const promises = context.diagnostics.map((diag) => {
 			// When a name is not found but could refer to a package, offer to add import
 			if (diag.message.indexOf('undefined: ') === 0) {
-				const [, name] = /^undefined: (\S*)/.exec(diag.message);
+				const [, name] = /^undefined: (\S*)/.exec(diag.message) ?? [];
 				return listPackages().then((packages) => {
 					const commands = packages
 						.filter((pkg) => pkg === name || pkg.endsWith('/' + name))
diff --git a/src/language/legacy/goDeclaration.ts b/src/language/legacy/goDeclaration.ts
index 2a3f202..6b10364 100644
--- a/src/language/legacy/goDeclaration.ts
+++ b/src/language/legacy/goDeclaration.ts
@@ -29,12 +29,12 @@
 const missingToolMsg = 'Missing tool: ';
 
 export interface GoDefinitionInformation {
-	file: string;
+	file?: string;
 	line: number;
 	column: number;
-	doc: string;
+	doc?: string;
 	declarationlines: string[];
-	name: string;
+	name?: string;
 	toolUsed: string;
 }
 
@@ -63,10 +63,10 @@
 export function definitionLocation(
 	document: vscode.TextDocument,
 	position: vscode.Position,
-	goConfig: vscode.WorkspaceConfiguration,
+	goConfig: vscode.WorkspaceConfiguration | undefined,
 	includeDocs: boolean,
 	token: vscode.CancellationToken
-): Promise<GoDefinitionInformation> {
+): Promise<GoDefinitionInformation | null> {
 	const adjustedPos = adjustWordPosition(document, position);
 	if (!adjustedPos[0]) {
 		return Promise.resolve(null);
@@ -113,7 +113,7 @@
 		word.match(/^\d+.?\d+$/) ||
 		goKeywords.indexOf(word) > 0
 	) {
-		return [false, null, null];
+		return [false, null!, null!];
 	}
 	if (position.isEqual(wordRange.end) && position.isAfter(wordRange.start)) {
 		position = position.translate(0, -1);
@@ -128,7 +128,7 @@
 	token: vscode.CancellationToken,
 	// eslint-disable-next-line @typescript-eslint/no-unused-vars
 	useReceivers = true
-): Promise<GoDefinitionInformation> {
+): Promise<GoDefinitionInformation | null> {
 	const godefTool = 'godef';
 	const godefPath = getBinPath(godefTool);
 	if (!path.isAbsolute(godefPath)) {
@@ -137,12 +137,12 @@
 	const offset = byteOffsetAt(input.document, input.position);
 	const env = toolExecutionEnvironment();
 	env['GOROOT'] = getCurrentGoRoot();
-	let p: cp.ChildProcess;
+	let p: cp.ChildProcess | null | undefined;
 	if (token) {
-		token.onCancellationRequested(() => killProcessTree(p));
+		token.onCancellationRequested(() => p && killProcessTree(p));
 	}
 
-	return new Promise<GoDefinitionInformation>((resolve, reject) => {
+	return new Promise((resolve, reject) => {
 		// Spawn `godef` process
 		const args = ['-t', '-i', '-f', input.document.fileName, '-o', offset.toString()];
 		// if (useReceivers) {
@@ -189,8 +189,8 @@
 					column: +col - 1,
 					declarationlines: lines.slice(1),
 					toolUsed: 'godef',
-					doc: null,
-					name: null
+					doc: undefined,
+					name: undefined
 				};
 				if (!input.includeDocs || godefImportDefinitionRegex.test(definitionInformation.declarationlines[0])) {
 					return resolve(definitionInformation);
@@ -212,7 +212,7 @@
 			}
 		});
 		if (p.pid) {
-			p.stdin.end(input.document.getText());
+			p.stdin?.end(input.document.getText());
 		}
 	});
 }
@@ -221,19 +221,19 @@
 	input: GoDefinitionInput,
 	token: vscode.CancellationToken,
 	useTags: boolean
-): Promise<GoDefinitionInformation> {
+): Promise<GoDefinitionInformation | null> {
 	const gogetdoc = getBinPath('gogetdoc');
 	if (!path.isAbsolute(gogetdoc)) {
 		return Promise.reject(missingToolMsg + 'gogetdoc');
 	}
 	const offset = byteOffsetAt(input.document, input.position);
 	const env = toolExecutionEnvironment();
-	let p: cp.ChildProcess;
+	let p: cp.ChildProcess | null | undefined;
 	if (token) {
-		token.onCancellationRequested(() => killProcessTree(p));
+		token.onCancellationRequested(() => p && killProcessTree(p));
 	}
 
-	return new Promise<GoDefinitionInformation>((resolve, reject) => {
+	return new Promise((resolve, reject) => {
 		const gogetdocFlagsWithoutTags = [
 			'-u',
 			'-json',
@@ -266,7 +266,7 @@
 				const goGetDocOutput = <GoGetDocOuput>JSON.parse(stdout.toString());
 				const match = /(.*):(\d+):(\d+)/.exec(goGetDocOutput.pos);
 				const definitionInfo: GoDefinitionInformation = {
-					file: null,
+					file: undefined,
 					line: 0,
 					column: 0,
 					toolUsed: 'gogetdoc',
@@ -286,7 +286,7 @@
 			}
 		});
 		if (p.pid) {
-			p.stdin.end(getFileArchive(input.document));
+			p.stdin?.end(getFileArchive(input.document));
 		}
 	});
 }
@@ -321,13 +321,13 @@
 					const guruOutput = <GuruDefinitionOuput>JSON.parse(stdout.toString());
 					const match = /(.*):(\d+):(\d+)/.exec(guruOutput.objpos);
 					const definitionInfo: GoDefinitionInformation = {
-						file: null,
+						file: undefined,
 						line: 0,
 						column: 0,
 						toolUsed: 'guru',
 						declarationlines: [guruOutput.desc],
-						doc: null,
-						name: null
+						doc: undefined,
+						name: undefined
 					};
 					if (!match) {
 						return resolve(definitionInfo);
@@ -343,12 +343,12 @@
 			}
 		);
 		if (p.pid) {
-			p.stdin.end(getFileArchive(input.document));
+			p.stdin?.end(getFileArchive(input.document));
 		}
 	});
 }
 
-export function parseMissingError(err: any): [boolean, string] {
+export function parseMissingError(err: any): [boolean, string | null] {
 	if (err) {
 		// Prompt for missing tool is located here so that the
 		// prompts dont show up on hover or signature help
@@ -360,7 +360,7 @@
 }
 
 export class GoDefinitionProvider implements vscode.DefinitionProvider {
-	private goConfig: vscode.WorkspaceConfiguration = null;
+	private goConfig: vscode.WorkspaceConfiguration | undefined;
 
 	constructor(goConfig?: vscode.WorkspaceConfiguration) {
 		this.goConfig = goConfig;
@@ -370,10 +370,10 @@
 		document: vscode.TextDocument,
 		position: vscode.Position,
 		token: vscode.CancellationToken
-	): Thenable<vscode.Location> {
+	): Thenable<vscode.Location | null> {
 		return definitionLocation(document, position, this.goConfig, false, token).then(
 			(definitionInfo) => {
-				if (definitionInfo === null || definitionInfo.file === null) {
+				if (!definitionInfo || !definitionInfo.file) {
 					return null;
 				}
 				const definitionResource = vscode.Uri.file(definitionInfo.file);
@@ -382,7 +382,7 @@
 			},
 			(err) => {
 				const miss = parseMissingError(err);
-				if (miss[0]) {
+				if (miss[0] && miss[1]) {
 					promptForMissingTool(miss[1]);
 				} else if (err) {
 					return Promise.reject(err);
diff --git a/src/language/legacy/goExtraInfo.ts b/src/language/legacy/goExtraInfo.ts
index 1c5c84c..a5d162e 100644
--- a/src/language/legacy/goExtraInfo.ts
+++ b/src/language/legacy/goExtraInfo.ts
@@ -19,7 +19,7 @@
 		this.goConfig = goConfig;
 	}
 
-	public provideHover(document: TextDocument, position: Position, token: CancellationToken): Thenable<Hover> {
+	public provideHover(document: TextDocument, position: Position, token: CancellationToken): Thenable<Hover | null> {
 		if (!this.goConfig) {
 			this.goConfig = getGoConfig(document.uri);
 		}
diff --git a/src/language/legacy/goImplementations.ts b/src/language/legacy/goImplementations.ts
index 68aa8b0..7414f75 100644
--- a/src/language/legacy/goImplementations.ts
+++ b/src/language/legacy/goImplementations.ts
@@ -41,7 +41,7 @@
 		document: vscode.TextDocument,
 		position: vscode.Position,
 		token: vscode.CancellationToken
-	): Thenable<vscode.Definition> {
+	): Thenable<vscode.Definition | null> | undefined {
 		// To keep `guru implements` fast we want to restrict the scope of the search to current workspace
 		// If no workspace is open, then no-op
 		const root = getWorkspaceFolderPath(document.uri);
@@ -58,7 +58,7 @@
 			return;
 		}
 
-		return new Promise<vscode.Definition>((resolve, reject) => {
+		return new Promise((resolve, reject) => {
 			if (token.isCancellationRequested) {
 				return resolve(null);
 			}
diff --git a/src/language/legacy/goLiveErrors.ts b/src/language/legacy/goLiveErrors.ts
index b70c671..3e24235 100644
--- a/src/language/legacy/goLiveErrors.ts
+++ b/src/language/legacy/goLiveErrors.ts
@@ -22,7 +22,7 @@
 	enabled: boolean;
 }
 
-let runner: NodeJS.Timer;
+let runner: NodeJS.Timer | null;
 
 export function goLiveErrorsEnabled() {
 	const goConfig = getGoConfig();
@@ -104,7 +104,7 @@
 					return;
 				}
 				// extract the line, column and error message from the gotype output
-				const [, file, line, column, message] = /^(.+):(\d+):(\d+):\s+(.+)/.exec(error);
+				const [, file, line, column, message] = /^(.+):(\d+):(\d+):\s+(.+)/.exec(error) ?? [];
 				// get canonical file path
 				const canonicalFilePath = vscode.Uri.file(file).toString();
 				const range = new vscode.Range(+line - 1, +column, +line - 1, +column);
@@ -122,6 +122,6 @@
 		}
 	});
 	if (p.pid) {
-		p.stdin.end(fileContents);
+		p.stdin?.end(fileContents);
 	}
 }
diff --git a/src/language/legacy/goOutline.ts b/src/language/legacy/goOutline.ts
index 73d65d4..6c7e162 100644
--- a/src/language/legacy/goOutline.ts
+++ b/src/language/legacy/goOutline.ts
@@ -60,22 +60,24 @@
 
 export async function documentSymbols(
 	options: GoOutlineOptions,
-	token: vscode.CancellationToken
+	token?: vscode.CancellationToken
 ): Promise<vscode.DocumentSymbol[]> {
 	const decls = await runGoOutline(options, token);
-	return convertToCodeSymbols(
-		options.document,
-		decls,
-		options.importsOption !== GoOutlineImportsOptions.Exclude,
-		makeMemoizedByteOffsetConverter(Buffer.from(options.document.getText()))
-	);
+	return options.document
+		? convertToCodeSymbols(
+				options.document,
+				decls,
+				options.importsOption !== GoOutlineImportsOptions.Exclude,
+				makeMemoizedByteOffsetConverter(Buffer.from(options.document.getText()))
+		  )
+		: [];
 }
 
 export function runGoOutline(
 	options: GoOutlineOptions,
-	token: vscode.CancellationToken
+	token?: vscode.CancellationToken
 ): Promise<GoOutlineDeclaration[]> {
-	return new Promise<GoOutlineDeclaration[]>((resolve, reject) => {
+	return new Promise((resolve, reject) => {
 		const gooutline = getBinPath('go-outline');
 		const gooutlineFlags = ['-f', options.fileName];
 		if (options.importsOption === GoOutlineImportsOptions.Only) {
@@ -85,9 +87,9 @@
 			gooutlineFlags.push('-modified');
 		}
 
-		let p: cp.ChildProcess;
+		let p: cp.ChildProcess | null | undefined;
 		if (token) {
-			token.onCancellationRequested(() => killProcess(p));
+			token.onCancellationRequested(() => p && killProcess(p));
 		}
 
 		// Spawn `go-outline` process
@@ -102,7 +104,7 @@
 						options.importsOption = GoOutlineImportsOptions.Include;
 					}
 					if (stderr.startsWith('flag provided but not defined: -modified')) {
-						options.document = null;
+						options.document = undefined;
 					}
 					p = null;
 					return runGoOutline(options, token).then((results) => {
@@ -110,7 +112,7 @@
 					});
 				}
 				if (err) {
-					return resolve(null);
+					return resolve([]);
 				}
 				const result = stdout.toString();
 				const decls = <GoOutlineDeclaration[]>JSON.parse(result);
@@ -120,7 +122,7 @@
 			}
 		});
 		if (options.document && p.pid) {
-			p.stdin.end(getFileArchive(options.document));
+			p.stdin?.end(getFileArchive(options.document));
 		}
 	});
 }
@@ -198,7 +200,7 @@
 
 	public async provideDocumentSymbols(
 		document: vscode.TextDocument,
-		token: vscode.CancellationToken
+		token?: vscode.CancellationToken
 	): Promise<vscode.DocumentSymbol[]> {
 		if (typeof this.includeImports !== 'boolean') {
 			const gotoSymbolConfig = getGoConfig(document.uri)['gotoSymbol'];
@@ -207,7 +209,7 @@
 
 		// TODO(suzmue): Check the commands available instead of the version.
 		if (languageClient && serverInfo?.Commands?.includes(GOPLS_LIST_IMPORTS)) {
-			const symbols: vscode.DocumentSymbol[] = await vscode.commands.executeCommand(
+			const symbols: vscode.DocumentSymbol[] | undefined = await vscode.commands.executeCommand(
 				'vscode.executeDocumentSymbolProvider',
 				document.uri
 			);
@@ -265,7 +267,7 @@
 		return this.runGoOutline(document, token);
 	}
 
-	private runGoOutline(document: vscode.TextDocument, token: vscode.CancellationToken) {
+	private runGoOutline(document: vscode.TextDocument, token?: vscode.CancellationToken) {
 		const options: GoOutlineOptions = {
 			fileName: document.fileName,
 			document,
@@ -276,7 +278,7 @@
 }
 
 async function listImports(document: vscode.TextDocument): Promise<{ Path: string; Name: string }[]> {
-	const uri = languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document).uri;
+	const uri = languageClient?.code2ProtocolConverter.asTextDocumentIdentifier(document).uri;
 	const params: ExecuteCommandParams = {
 		command: GOPLS_LIST_IMPORTS,
 		arguments: [
@@ -285,6 +287,6 @@
 			}
 		]
 	};
-	const resp = await languageClient.sendRequest(ExecuteCommandRequest.type, params);
+	const resp = await languageClient?.sendRequest(ExecuteCommandRequest.type, params);
 	return resp.Imports;
 }
diff --git a/src/language/legacy/goReferences.ts b/src/language/legacy/goReferences.ts
index 674a1b7..2239f25 100644
--- a/src/language/legacy/goReferences.ts
+++ b/src/language/legacy/goReferences.ts
@@ -96,7 +96,7 @@
 				}
 			});
 			if (process.pid) {
-				process.stdin.end(getFileArchive(document));
+				process.stdin?.end(getFileArchive(document));
 			}
 			token.onCancellationRequested(() => killProcessTree(process));
 		});
diff --git a/src/language/legacy/goSignature.ts b/src/language/legacy/goSignature.ts
index e4f84a6..7163ef8 100755
--- a/src/language/legacy/goSignature.ts
+++ b/src/language/legacy/goSignature.ts
@@ -27,7 +27,7 @@
 		document: TextDocument,
 		position: Position,
 		token: CancellationToken
-	): Promise<SignatureHelp> {
+	): Promise<SignatureHelp | null> {
 		let goConfig = this.goConfig || getGoConfig(document.uri);
 
 		const theCall = this.walkBackwardsToBeginningOfCall(document, position);
@@ -45,7 +45,7 @@
 				// The definition was not found
 				return null;
 			}
-			if (res.line === callerPos.line) {
+			if (res.line === callerPos?.line) {
 				// This must be a function definition
 				return null;
 			}
@@ -54,8 +54,8 @@
 				return null;
 			}
 			const result = new SignatureHelp();
-			let sig: string;
-			let si: SignatureInformation;
+			let sig: string | undefined;
+			let si: SignatureInformation | undefined;
 			if (res.toolUsed === 'godef') {
 				// declaration is of the form "Add func(a int, b int) int"
 				const nameEnd = declarationText.indexOf(' ');
@@ -71,8 +71,9 @@
 					declarationText = declarationText.substring(funcNameStart);
 				}
 				si = new SignatureInformation(declarationText, res.doc);
-				sig = declarationText.substring(res.name.length);
+				sig = declarationText.substring(res.name?.length ?? 0);
 			}
+			if (!si || !sig) return result;
 			si.parameters = getParametersAndReturnType(sig).params.map(
 				(paramText) => new ParameterInformation(paramText)
 			);
@@ -93,7 +94,7 @@
 			}
 			position = position.translate(0, -1);
 		}
-		return null;
+		return position;
 	}
 
 	/**
diff --git a/src/language/legacy/goSuggest.ts b/src/language/legacy/goSuggest.ts
index 95afca8..ca39c89 100644
--- a/src/language/legacy/goSuggest.ts
+++ b/src/language/legacy/goSuggest.ts
@@ -65,7 +65,7 @@
 class ExtendedCompletionItem extends vscode.CompletionItem {
 	public package?: string;
 	public receiver?: string;
-	public fileName: string;
+	public fileName!: string;
 }
 
 const lineCommentFirstWordRegex = /^\s*\/\/\s+[\S]*$/;
@@ -77,10 +77,10 @@
 	private killMsgShown = false;
 	private setGocodeOptions = true;
 	private isGoMod = false;
-	private globalState: vscode.Memento;
-	private previousFile: string;
-	private previousFileDir: string;
-	private gocodeFlags: string[];
+	private globalState: vscode.Memento | undefined;
+	private previousFile?: string;
+	private previousFileDir?: string;
+	private gocodeFlags?: string[];
 	private excludeDocs = false;
 
 	constructor(globalState?: vscode.Memento) {
@@ -213,8 +213,9 @@
 						const pkgPath = this.getPackagePathFromLine(lineTillCurrentPosition);
 						if (pkgPath.length === 1) {
 							// Now that we have the package path, import it right after the "package" statement
-							const v = parseFilePrelude(vscode.window.activeTextEditor.document.getText());
+							const v = parseFilePrelude(vscode.window.activeTextEditor?.document.getText() ?? '');
 							const pkg = v.pkg;
+							if (!pkg) return;
 							const posToAddImport = document.offsetAt(new vscode.Position(pkg.start + 1, 0));
 							const textToAdd = `import "${pkgPath[0]}"\n`;
 							inputText =
@@ -301,7 +302,7 @@
 			let stderr = '';
 
 			// stamblerre/gocode does not support -unimported-packages flags.
-			if (this.isGoMod) {
+			if (this.isGoMod && this.gocodeFlags) {
 				const unimportedPkgIndex = this.gocodeFlags.indexOf('-unimported-packages');
 				if (unimportedPkgIndex >= 0) {
 					this.gocodeFlags.splice(unimportedPkgIndex, 1);
@@ -309,14 +310,14 @@
 			}
 
 			// -exclude-docs is something we use internally and is not related to gocode
-			const excludeDocsIndex = this.gocodeFlags.indexOf('-exclude-docs');
-			if (excludeDocsIndex >= 0) {
+			const excludeDocsIndex = this.gocodeFlags?.indexOf('-exclude-docs') ?? -1;
+			if (excludeDocsIndex >= 0 && this.gocodeFlags) {
 				this.gocodeFlags.splice(excludeDocsIndex, 1);
 				this.excludeDocs = true;
 			}
 
 			// Spawn `gocode` process
-			const p = cp.spawn(gocode, [...this.gocodeFlags, 'autocomplete', filename, '' + offset], { env });
+			const p = cp.spawn(gocode, [...(this.gocodeFlags || []), 'autocomplete', filename, '' + offset], { env });
 			p.stdout.on('data', (data) => (stdout += data));
 			p.stderr.on('data', (data) => (stderr += data));
 			p.on('error', (err) => {
@@ -526,7 +527,7 @@
 							)
 							.then((selected) => {
 								if (selected === "Don't show again") {
-									this.globalState.update(gocodeNoSupportForgbMsgKey, true);
+									this.globalState?.update(gocodeNoSupportForgbMsgKey, true);
 								}
 							});
 					}
@@ -537,7 +538,7 @@
 				const existingOptions = stdout.split(/\r\n|\n/);
 				const optionsToSet: string[][] = [];
 				const setOption = () => {
-					const [name, value] = optionsToSet.pop();
+					const [name, value] = optionsToSet.pop() ?? [];
 					cp.execFile(gocode, ['set', name, value], { env }, () => {
 						if (optionsToSet.length) {
 							setOption();
@@ -603,14 +604,17 @@
  * @param document The current document
  * @param position The cursor position
  */
-function getCommentCompletion(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem {
+function getCommentCompletion(
+	document: vscode.TextDocument,
+	position: vscode.Position
+): vscode.CompletionItem | undefined {
 	const lineText = document.lineAt(position.line).text;
 	const lineTillCurrentPosition = lineText.substr(0, position.character);
 	// triggering completions in comments on exported members
 	if (lineCommentFirstWordRegex.test(lineTillCurrentPosition) && position.line + 1 < document.lineCount) {
 		const nextLine = document.lineAt(position.line + 1).text.trim();
 		const memberType = nextLine.match(exportedMemberRegex);
-		let suggestionItem: vscode.CompletionItem;
+		let suggestionItem: vscode.CompletionItem | undefined;
 		if (memberType && memberType.length === 4) {
 			suggestionItem = new vscode.CompletionItem(memberType[3], vscodeKindFromGoCodeClass(memberType[1], ''));
 		}
diff --git a/src/language/legacy/goSymbol.ts b/src/language/legacy/goSymbol.ts
index 75588b6..0aab278 100644
--- a/src/language/legacy/goSymbol.ts
+++ b/src/language/legacy/goSymbol.ts
@@ -51,7 +51,7 @@
 				const pos = new vscode.Position(decl.line, decl.character);
 				const symbolInfo = new vscode.SymbolInformation(
 					decl.name,
-					kind,
+					kind!,
 					new vscode.Range(pos, pos),
 					vscode.Uri.file(decl.path),
 					''
@@ -59,14 +59,13 @@
 				symbols.push(symbolInfo);
 			}
 		};
-		const root = getWorkspaceFolderPath(
-			vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.uri
-		);
+		const root =
+			getWorkspaceFolderPath(vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.uri) ?? '';
 		const goConfig = getGoConfig();
 
 		if (!root && !goConfig.gotoSymbol.includeGoroot) {
 			vscode.window.showInformationMessage('No workspace is open to find symbols.');
-			return;
+			return Promise.resolve([]);
 		}
 
 		return getWorkspaceSymbols(root, query, token, goConfig).then((results) => {
@@ -103,7 +102,7 @@
 	}
 
 	return Promise.all(calls)
-		.then(([...results]) => <GoSymbolDeclaration[]>[].concat(...results))
+		.then(([...results]) => <GoSymbolDeclaration[]>[].concat(...(results as any)))
 		.catch((err: Error) => {
 			if (err && (<any>err).code === 'ENOENT') {
 				promptForMissingTool('go-symbols');
@@ -112,6 +111,7 @@
 				promptForUpdatingTool('go-symbols');
 				return getWorkspaceSymbols(workspacePath, query, token, goConfig, false);
 			}
+			return [];
 		});
 }
 
diff --git a/src/language/legacy/goTypeDefinition.ts b/src/language/legacy/goTypeDefinition.ts
index c9c6e40..b4249b7 100644
--- a/src/language/legacy/goTypeDefinition.ts
+++ b/src/language/legacy/goTypeDefinition.ts
@@ -47,7 +47,7 @@
 		}
 		position = adjustedPos[2];
 
-		return new Promise<vscode.Definition>((resolve, reject) => {
+		return new Promise<vscode.Definition | null>((resolve, reject) => {
 			const goGuru = getBinPath('guru');
 			if (!path.isAbsolute(goGuru)) {
 				promptForMissingTool('guru');
@@ -84,9 +84,9 @@
 						}
 
 						// Fall back to position of declaration
-						return definitionLocation(document, position, null, false, token).then(
+						return definitionLocation(document, position, undefined, false, token).then(
 							(definitionInfo) => {
-								if (definitionInfo === null || definitionInfo.file === null) {
+								if (!definitionInfo || !definitionInfo.file) {
 									return null;
 								}
 								const definitionResource = vscode.Uri.file(definitionInfo.file);
@@ -95,7 +95,7 @@
 							},
 							(err) => {
 								const miss = parseMissingError(err);
-								if (miss[0]) {
+								if (miss[0] && miss[1]) {
 									promptForMissingTool(miss[1]);
 								} else if (err) {
 									return Promise.reject(err);
@@ -123,7 +123,7 @@
 				}
 			});
 			if (process.pid) {
-				process.stdin.end(getFileArchive(document));
+				process.stdin?.end(getFileArchive(document));
 			}
 			token.onCancellationRequested(() => killProcessTree(process));
 		});