Repository: OmniSharp/omnisharp-vscode
Pull request #5400 (2022-10-09 06:16:49)

Feature nullability
Note: I'm aware of #5276 and that the feature/ folder may well become obsolete in the near future.
Nevertheless, given there's no set timeline yet, I figure the sooner strict mode can be turned on globally, the better, and I also got to learn some new TypeScript tricks along the way (like type guards).

**Note that none of these changes have been validated on an actual codebase.**

A good chunk of the fixes fall into one of two buckets: a) simply explicitly returning undefined (or an empty array) when we have nothing to return to VS Code or b) reducing a `has` check on a Map followed by a `get` to just `get` and then a check against undefined.

Otherwise:
* Significant refactoring around creating codelens for tests. Mostly removing helper methods and checking for things inline, where we have more context on whether something can be nullable or not.
* For commands that need to act on active text editors, added various guards to make sure there _is_ an active editor in the first place.
* For autocomplete, pass the document that the initial autocomplete acted upon to afterInsert, to avoid race conditions where a) the active text editor changed, or b) the active text editor was closed between the time it took to insert the completion and call afterInsert.
* Changed Process in processPicker from a class to an interface - there is still some manual casting needed when we add a finalized process to the array, but at least we're no longer creating invalid Process classes anymore.

Impact:
Brings strict errors down to 234 from 377 when compared to master @ 9336244b129c7ca3a451e634514632fc2e764379.
(If we ignore tests, the number of violations goes down further to just 40, 27 of which are in folders I haven't looked at yet.)


Patch:
diff --git a/package.json b/package.json
index 188ec254..f86e50c8 100644
--- a/package.json
+++ b/package.json
@@ -1189,17 +1189,20 @@
       {
         "command": "o.fixAll.solution",
         "title": "Fix all occurrences of a code issue within solution",
-        "category": "OmniSharp"
+        "category": "OmniSharp",
+        "enablement": "editorFocus"
       },
       {
         "command": "o.fixAll.project",
         "title": "Fix all occurrences of a code issue within project",
-        "category": "OmniSharp"
+        "category": "OmniSharp",
+        "enablement": "editorFocus"
       },
       {
         "command": "o.fixAll.document",
         "title": "Fix all occurrences of a code issue within document",
-        "category": "OmniSharp"
+        "category": "OmniSharp",
+        "enablement": "editorFocus"
       },
       {
         "command": "o.reanalyze.allProjects",
@@ -1209,7 +1212,8 @@
       {
         "command": "o.reanalyze.currentProject",
         "title": "Analyze current project",
-        "category": "OmniSharp"
+        "category": "OmniSharp",
+        "enablement": "editorFocus"
       },
       {
         "command": "dotnet.generateAssets",
diff --git a/src/features/codeActionProvider.ts b/src/features/codeActionProvider.ts
index fffd7836..e02b2a9c 100644
--- a/src/features/codeActionProvider.ts
+++ b/src/features/codeActionProvider.ts
@@ -23,7 +23,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
         this.addDisposables(new CompositeDisposable(registerCommandDisposable));
     }
 
-    public async provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): Promise<vscode.CodeAction[]> {
+    public async provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): Promise<vscode.CodeAction[] | undefined> {
         const options = this.optionProvider.GetLatestOptions();
         if (options.disableCodeActions) {
             return;
@@ -75,7 +75,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
         }
     }
 
-    private mapOmniSharpCodeActionKindToVSCodeCodeActionKind(kind: string): vscode.CodeActionKind {
+    private mapOmniSharpCodeActionKindToVSCodeCodeActionKind(kind: string | undefined): vscode.CodeActionKind {
         switch (kind) {
             case 'QuickFix':
                 return vscode.CodeActionKind.QuickFix;
@@ -90,7 +90,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
         }
     }
 
-    private async _runCodeAction(req: protocol.V2.RunCodeActionRequest, token: vscode.CancellationToken): Promise<boolean | string | {}> {
+    private async _runCodeAction(req: protocol.V2.RunCodeActionRequest, token: vscode.CancellationToken): Promise<boolean | string | undefined> {
         try {
             const response = await serverUtils.runCodeAction(this._server, req);
             if (response) {
@@ -99,5 +99,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
         } catch (error) {
             return Promise.reject(`Problem invoking 'RunCodeAction' on OmniSharp server: ${error}`);
         }
+
+        return undefined;
     }
 }
diff --git a/src/features/codeLensProvider.ts b/src/features/codeLensProvider.ts
index 703c15fe..0d57d6ea 100644
--- a/src/features/codeLensProvider.ts
+++ b/src/features/codeLensProvider.ts
@@ -77,6 +77,11 @@ class DebugTestsCodeLens extends TestCodeLens {
     }
 }
 
+interface Test {
+    framework: string;
+    methodName: string;
+}
+
 export default class OmniSharpCodeLensProvider extends AbstractProvider implements vscode.CodeLensProvider {
 
     constructor(server: OmniSharpServer, testManager: TestManager, private optionProvider: OptionProvider, languageMiddlewareFeature: LanguageMiddlewareFeature) {
@@ -100,7 +105,7 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
         return [];
     }
 
-    async resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
+    async resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens | undefined> {
         if (codeLens instanceof ReferencesCodeLens) {
             return this.resolveReferencesCodeLens(codeLens, token);
         }
@@ -110,9 +115,11 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
         else if (codeLens instanceof DebugTestsCodeLens) {
             return this.resolveTestCodeLens(codeLens, 'Debug Test', 'dotnet.test.debug', 'Debug All Tests', 'dotnet.classTests.debug');
         }
+
+        return undefined;
     }
 
-    private async resolveReferencesCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
+    private async resolveReferencesCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens | undefined> {
         const request: protocol.FindUsagesRequest = {
             FileName: codeLens.fileName,
             Line: codeLens.range.start.line,
@@ -137,7 +144,7 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
             codeLens.command = {
                 title: count === 1 ? '1 reference' : `${count} references`,
                 command: 'editor.action.showReferences',
-                arguments: [vscode.Uri.file(request.FileName), codeLens.range.start, remappedLocations]
+                arguments: [vscode.Uri.file(codeLens.fileName), codeLens.range.start, remappedLocations]
             };
 
             return codeLens;
@@ -147,7 +154,7 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
         }
     }
 
-    private async resolveTestCodeLens(codeLens: TestCodeLens, singularTitle: string, singularCommandName: string, pluralTitle: string, pluralCommandName: string): Promise<vscode.CodeLens> {
+    private async resolveTestCodeLens(codeLens: TestCodeLens, singularTitle: string, singularCommandName: string, pluralTitle: string, pluralCommandName: string): Promise<vscode.CodeLens | undefined> {
         if (!codeLens.isTestContainer) {
             // This is just a single test method, not a container.
             codeLens.command = {
@@ -202,35 +209,33 @@ function createCodeLensesForElement(element: Structure.CodeElement, fileName: st
     }
 
     if (options.showTestsCodeLens) {
-        if (isValidMethodForTestCodeLens(element)) {
-            let [testFramework, testMethodName] = getTestFrameworkAndMethodName(element);
-            let range = element.Ranges[SymbolRangeNames.Name];
-
-            if (range && testFramework && testMethodName) {
-                results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, testFramework, [testMethodName]));
-                results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, testFramework, [testMethodName]));
+        if (element.Kind === SymbolKinds.Method) {
+            const test = getTest(element);
+            if (test !== undefined) {
+                let range = element.Ranges[SymbolRangeNames.Name];
+                if (range !== undefined) {
+                    results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, test.framework, [test.methodName]));
+                    results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, test.framework, [test.methodName]));
+                }
             }
-        }
-        else if (isValidClassForTestCodeLens(element)) {
-            // Note: We don't handle multiple test frameworks in the same class. The first test framework wins.
-            let testFramework: string = null;
-            let testMethodNames: string[] = [];
-            let range = element.Ranges[SymbolRangeNames.Name];
+        } else if (element.Kind === SymbolKinds.Class && element.Children !== undefined) {
+            const methods = element.Children.filter(child => child.Kind === SymbolKinds.Method);
 
-            for (let childElement of element.Children) {
-                let [childTestFramework, childTestMethodName] = getTestFrameworkAndMethodName(childElement);
+            const tests = methods
+                .map(method => getTest(method))
+                .filter((test): test is NonNullable<typeof test> => test !== undefined);
 
-                if (!testFramework && childTestFramework) {
-                    testFramework = childTestFramework;
-                    testMethodNames.push(childTestMethodName);
-                }
-                else if (testFramework && childTestFramework === testFramework) {
-                    testMethodNames.push(childTestMethodName);
-                }
+            // Note: We don't handle multiple test frameworks in the same class. The first test framework wins.
+            const testFramework = tests.length > 0 ? tests[0].framework : undefined;
+            if (testFramework !== undefined) {
+                const testMethodNames = tests
+                    .filter(test => test.framework === testFramework)
+                    .map(test => test.methodName);
+
+                let range = element.Ranges[SymbolRangeNames.Name];
+                results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
+                results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
             }
-
-            results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
-            results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
         }
     }
 
@@ -262,40 +267,19 @@ function isValidElementForReferencesCodeLens(element: Structure.CodeElement, opt
     return true;
 }
 
-
-function isValidClassForTestCodeLens(element: Structure.CodeElement): boolean {
-    if (element.Kind != SymbolKinds.Class) {
-        return false;
+function getTest(element: Structure.CodeElement): Test | undefined {
+    if (element.Properties === undefined) {
+        return undefined;
     }
 
-    if (!element.Children) {
-        return false;
+    const framework = element.Properties[SymbolPropertyNames.TestFramework];
+    const methodName = element.Properties[SymbolPropertyNames.TestMethodName];
+    if (framework === undefined || methodName === undefined) {
+        return undefined;
     }
 
-    return element.Children.find(isValidMethodForTestCodeLens) !== undefined;
-}
-
-function isValidMethodForTestCodeLens(element: Structure.CodeElement): boolean {
-    if (element.Kind != SymbolKinds.Method) {
-        return false;
+    return {
+        framework,
+        methodName,
     }
-
-    if (!element.Properties ||
-        !element.Properties[SymbolPropertyNames.TestFramework] ||
-        !element.Properties[SymbolPropertyNames.TestMethodName]) {
-        return false;
-    }
-
-    return true;
-}
-
-function getTestFrameworkAndMethodName(element: Structure.CodeElement): [string, string] {
-    if (!element.Properties) {
-        return [null, null];
-    }
-
-    const testFramework = element.Properties[SymbolPropertyNames.TestFramework];
-    const testMethodName = element.Properties[SymbolPropertyNames.TestMethodName];
-
-    return [testFramework, testMethodName];
 }
diff --git a/src/features/commands.ts b/src/features/commands.ts
index 70c2c0fc..45d99593 100644
--- a/src/features/commands.ts
+++ b/src/features/commands.ts
@@ -53,7 +53,7 @@ export default function registerCommands(context: vscode.ExtensionContext, serve
     // Register command for generating tasks.json and launch.json assets.
     disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex)));
 
-    disposable.add(vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue(vscode, eventStream, getDotnetInfo, platformInfo.isValidPlatformForMono(), optionProvider.GetLatestOptions(), monoResolver, dotnetResolver)));
+    disposable.add(vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue(vscode, context.extension.packageJSON.version, eventStream, getDotnetInfo, platformInfo.isValidPlatformForMono(), optionProvider.GetLatestOptions(), dotnetResolver, monoResolver)));
 
     disposable.add(vscode.commands.registerCommand('csharp.showDecompilationTerms', async () => showDecompilationTerms(context, server, optionProvider)));
 
@@ -154,8 +154,13 @@ async function reAnalyzeAllProjects(server: OmniSharpServer, eventStream: EventS
 }
 
 async function reAnalyzeCurrentProject(server: OmniSharpServer, eventStream: EventStream): Promise<void> {
+    const activeTextEditor = vscode.window.activeTextEditor;
+    if (activeTextEditor === undefined) {
+        return;
+    }
+
     await serverUtils.reAnalyze(server, {
-        fileName: vscode.window.activeTextEditor.document.uri.fsPath
+        FileName: activeTextEditor.document.uri.fsPath,
     });
 }
 
diff --git a/src/features/completionProvider.ts b/src/features/completionProvider.ts
index 1e2cdef3..db3f05b5 100644
--- a/src/features/completionProvider.ts
+++ b/src/features/completionProvider.ts
@@ -23,14 +23,14 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
         super(server, languageMiddlewareFeature);
     }
 
-    public async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionList> {
+    public async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionList | undefined> {
         let request = createRequest<protocol.CompletionRequest>(document, position);
         request.CompletionTrigger = (context.triggerKind + 1) as LspCompletionTriggerKind;
         request.TriggerCharacter = context.triggerCharacter;
 
         try {
             const response = await serverUtils.getCompletion(this._server, request, token);
-            let mappedItems = response.Items.map(arg => this._convertToVscodeCompletionItem(arg));
+            let mappedItems = response.Items.map(arg => this._convertToVscodeCompletionItem(arg, document));
 
             if (isVirtualCSharpDocument(document)) {
                 // The `await` completion item is not compatible with all Razor scenarios.
@@ -89,16 +89,16 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
         const request: protocol.CompletionResolveRequest = { Item: lspItem };
         try {
             const response = await serverUtils.getCompletionResolve(this._server, request, token);
-            return this._convertToVscodeCompletionItem(response.Item);
+            return this._convertToVscodeCompletionItem(response.Item, item.command?.arguments?.[1] as TextDocument);
         }
         catch (error) {
-            return;
+            return item;
         }
     }
 
-    public async afterInsert(item: protocol.OmnisharpCompletionItem) {
+    public async afterInsert(item: protocol.OmnisharpCompletionItem, document: TextDocument) {
         try {
-            const uri = window.activeTextEditor.document.uri;
+            const uri = document.uri;
             const response = await serverUtils.getCompletionAfterInsert(this._server, { Item: item });
 
             if (!response.Changes || !response.Column || !response.Line) {
@@ -119,18 +119,20 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
                 return;
             }
 
-            const responseLine = response.Line;
-            const responseColumn = response.Column;
+            const editor = window.visibleTextEditors.find(editor => editor.document === document);
+            if (editor === undefined) {
+                return;
+            }
 
-            const finalPosition = new Position(responseLine, responseColumn);
-            window.activeTextEditor.selections = [new Selection(finalPosition, finalPosition)];
+            const finalPosition = new Position(response.Line, response.Column);
+            editor.selections = [new Selection(finalPosition, finalPosition)];
         }
         catch (error) {
             return;
         }
     }
 
-    private _convertToVscodeCompletionItem(omnisharpCompletion: protocol.OmnisharpCompletionItem): CompletionItem {
+    private _convertToVscodeCompletionItem(omnisharpCompletion: protocol.OmnisharpCompletionItem, document: TextDocument): CompletionItem {
         const docs: MarkdownString | undefined = omnisharpCompletion.Documentation ? new MarkdownString(omnisharpCompletion.Documentation, false) : undefined;
 
         const mapRange = function (edit: protocol.LinePositionSpanTextChange): Range {
@@ -165,7 +167,7 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
             sortText: omnisharpCompletion.SortText,
             additionalTextEdits: additionalTextEdits,
             keepWhitespace: true,
-            command: omnisharpCompletion.HasAfterInsertStep ? { command: CompletionAfterInsertCommand, title: "", arguments: [omnisharpCompletion] } : undefined
+            command: omnisharpCompletion.HasAfterInsertStep ? { command: CompletionAfterInsertCommand, title: "", arguments: [omnisharpCompletion, document] } : undefined
         };
     }
 }
diff --git a/src/features/definitionMetadataDocumentProvider.ts b/src/features/definitionMetadataDocumentProvider.ts
index 29f7f67f..32ba6505 100644
--- a/src/features/definitionMetadataDocumentProvider.ts
+++ b/src/features/definitionMetadataDocumentProvider.ts
@@ -9,7 +9,7 @@ import { IDisposable } from '../Disposable';
 
 export default class DefinitionMetadataDocumentProvider implements TextDocumentContentProvider, IDisposable {
     readonly scheme = "omnisharp-metadata";
-    private _registration: IDisposable;
+    private _registration?: IDisposable;
     private _documents: Map<string, MetadataResponse>;
     private _documentClosedSubscription: IDisposable;
 
@@ -23,7 +23,7 @@ export default class DefinitionMetadataDocumentProvider implements TextDocumentC
     }
 
     public dispose(): void {
-        this._registration.dispose();
+        this._registration?.dispose();
         this._documentClosedSubscription.dispose();
         this._documents.clear();
     }
@@ -48,7 +48,7 @@ export default class DefinitionMetadataDocumentProvider implements TextDocumentC
         this._registration = workspace.registerTextDocumentContentProvider(this.scheme, this);
     }
 
-    public provideTextDocumentContent(uri: Uri): string {
+    public provideTextDocumentContent(uri: Uri): string | undefined {
         return this._documents.get(uri.toString())?.Source;
     }
 
@@ -56,4 +56,4 @@ export default class DefinitionMetadataDocumentProvider implements TextDocumentC
         return Uri.parse(this.scheme + "://" +
             sourceName.replace(/\\/g, "/").replace(/(.*)\/(.*)/g, "$1/[metadata] $2"));
     }
-}
\ No newline at end of file
+}
diff --git a/src/features/diagnosticsProvider.ts b/src/features/diagnosticsProvider.ts
index 3dc0a503..2d00e3ae 100644
--- a/src/features/diagnosticsProvider.ts
+++ b/src/features/diagnosticsProvider.ts
@@ -189,9 +189,9 @@ class DiagnosticsProvider extends AbstractSupport {
         }
     }
 
-    private _onDidChangeActiveTextEditor(textEditor: vscode.TextEditor): void {
+    private _onDidChangeActiveTextEditor(textEditor: vscode.TextEditor | undefined): void {
         // active text editor can be undefined.
-        if (textEditor != undefined && textEditor.document != null) {
+        if (textEditor !== undefined) {
             this._onDocumentOpenOrChange(textEditor.document);
         }
     }
@@ -272,22 +272,22 @@ class DiagnosticsProvider extends AbstractSupport {
     private _mapQuickFixesAsDiagnosticsInFile(quickFixes: protocol.QuickFix[]): { diagnostic: vscode.Diagnostic, fileName: string }[] {
         return quickFixes
             .map(quickFix => this._asDiagnosticInFileIfAny(quickFix))
-            .filter(diagnosticInFile => diagnosticInFile !== undefined);
+            .filter((diagnosticInFile): diagnosticInFile is NonNullable<typeof diagnosticInFile> => diagnosticInFile !== undefined);
     }
 
     private async _validateEntireWorkspace() {
-        let value = await serverUtils.codeCheck(this._server, { FileName: null }, new vscode.CancellationTokenSource().token);
+        let value = await serverUtils.codeCheck(this._server, {}, new vscode.CancellationTokenSource().token);
 
         let quickFixes = value.QuickFixes
             .sort((a, b) => a.FileName.localeCompare(b.FileName));
 
-        let entries: [vscode.Uri, vscode.Diagnostic[]][] = [];
-        let lastEntry: [vscode.Uri, vscode.Diagnostic[]];
+        let entries: [vscode.Uri, vscode.Diagnostic[] | undefined][] = [];
+        let lastEntry: [vscode.Uri, vscode.Diagnostic[]] | undefined;
 
         for (let diagnosticInFile of this._mapQuickFixesAsDiagnosticsInFile(quickFixes)) {
             let uri = vscode.Uri.file(diagnosticInFile.fileName);
 
-            if (lastEntry && lastEntry[0].toString() === uri.toString()) {
+            if (lastEntry !== undefined && lastEntry[0].toString() === uri.toString()) {
                 lastEntry[1].push(diagnosticInFile.diagnostic);
             } else {
                 // We're replacing all diagnostics in this file. Pushing an entry with undefined for
@@ -301,7 +301,7 @@ class DiagnosticsProvider extends AbstractSupport {
 
         // Clear diagnostics for files that no longer have any diagnostics.
         this._diagnostics.forEach((uri) => {
-            if (!entries.find(tuple => tuple[0].toString() === uri.toString())) {
+            if (entries.find(tuple => tuple[0].toString() === uri.toString()) === undefined) {
                 this._diagnostics.delete(uri);
             }
         });
@@ -310,7 +310,7 @@ class DiagnosticsProvider extends AbstractSupport {
         this._diagnostics.set(entries);
     }
 
-    private _asDiagnosticInFileIfAny(quickFix: protocol.QuickFix): { diagnostic: vscode.Diagnostic, fileName: string } {
+    private _asDiagnosticInFileIfAny(quickFix: protocol.QuickFix): { diagnostic: vscode.Diagnostic, fileName: string } | undefined {
         let display = this._getDiagnosticDisplay(quickFix, this._asDiagnosticSeverity(quickFix));
 
         if (display.severity === "hidden") {
@@ -332,12 +332,12 @@ class DiagnosticsProvider extends AbstractSupport {
 
     private _getDiagnosticDisplay(quickFix: protocol.QuickFix, severity: vscode.DiagnosticSeverity | "hidden"): { severity: vscode.DiagnosticSeverity | "hidden", isFadeout: boolean } {
         // These hard coded values bring the goodness of fading even when analyzers are disabled.
-        let isFadeout = (quickFix.Tags && !!quickFix.Tags.find(x => x.toLowerCase() == 'unnecessary'))
+        let isFadeout = (quickFix.Tags?.find(x => x.toLowerCase() === 'unnecessary') !== undefined)
             || quickFix.Id == "CS0162"  // CS0162: Unreachable code
             || quickFix.Id == "CS0219"  // CS0219: Unused variable
             || quickFix.Id == "CS8019"; // CS8019: Unnecessary using
 
-        if (isFadeout && quickFix.LogLevel.toLowerCase() === 'hidden' || quickFix.LogLevel.toLowerCase() === 'none') {
+        if (isFadeout && ['hidden', 'none'].includes(quickFix.LogLevel.toLowerCase())) {
             // Theres no such thing as hidden severity in VSCode,
             // however roslyn uses commonly analyzer with hidden to fade out things.
             // Without this any of those doesn't fade anything in vscode.
diff --git a/src/features/documentHighlightProvider.ts b/src/features/documentHighlightProvider.ts
index 996e904c..172d4abc 100644
--- a/src/features/documentHighlightProvider.ts
+++ b/src/features/documentHighlightProvider.ts
@@ -24,9 +24,9 @@ export default class OmnisharpDocumentHighlightProvider extends AbstractSupport
                 return res.QuickFixes.map(OmnisharpDocumentHighlightProvider._asDocumentHighlight);
             }
         }
-        catch (error) {
-            return [];
-        }
+        catch {}
+
+        return [];
     }
 
     private static _asDocumentHighlight(quickFix: protocol.QuickFix): DocumentHighlight {
diff --git a/src/features/fixAllProvider.ts b/src/features/fixAllProvider.ts
index aa010c02..2a15da9b 100644
--- a/src/features/fixAllProvider.ts
+++ b/src/features/fixAllProvider.ts
@@ -17,7 +17,7 @@ import { CancellationToken } from 'vscode-languageserver-protocol';
 export class FixAllProvider extends AbstractProvider implements vscode.CodeActionProvider {
     public static fixAllCodeActionKind =
       vscode.CodeActionKind.SourceFixAll.append('csharp');
-  
+
     public static metadata: vscode.CodeActionProviderMetadata = {
       providedCodeActionKinds: [FixAllProvider.fixAllCodeActionKind]
     };
@@ -50,7 +50,13 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio
     }
 
     private async fixAllMenu(server: OmniSharpServer, scope: protocol.FixAllScope): Promise<void> {
-        let availableFixes = await serverUtils.getFixAll(server, { FileName: vscode.window.activeTextEditor.document.fileName, Scope: scope });
+        const fileName = vscode.window.activeTextEditor?.document.fileName;
+        if (fileName === undefined) {
+            vscode.window.showWarningMessage('Text editor must be focused to fix all issues');
+            return;
+        }
+
+        let availableFixes = await serverUtils.getFixAll(server, { FileName: fileName, Scope: scope });
 
         let targets = availableFixes.Items.map(x => `${x.Id}: ${x.Message}`);
 
@@ -58,26 +64,25 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio
             targets = ["Fix all issues", ...targets];
         }
 
-        return vscode.window.showQuickPick(targets, {
+        const action = await vscode.window.showQuickPick(targets, {
             matchOnDescription: true,
             placeHolder: `Select fix all action`
-        }).then(async selectedAction => {
-            let filter: FixAllItem[] = undefined;
+        });
 
-            if (selectedAction === undefined) {
-                return;
-            }
+        if (action === undefined) {
+            return;
+        }
 
-            if (selectedAction !== "Fix all issues") {
-                let actionTokens = selectedAction.split(":");
-                filter = [{ Id: actionTokens[0], Message: actionTokens[1] }];
-            }
+        let filter: FixAllItem[] | undefined;
+        if (action !== "Fix all issues") {
+            let actionTokens = action.split(":");
+            filter = [{ Id: actionTokens[0], Message: actionTokens[1] }];
+        }
 
-            await this.applyFixes(vscode.window.activeTextEditor.document.fileName, scope, filter);
-        });
+        await this.applyFixes(fileName, scope, filter);
     }
 
-    private async applyFixes(fileName: string, scope: FixAllScope, fixAllFilter: FixAllItem[]): Promise<boolean | string | {}> {
+    private async applyFixes(fileName: string, scope: FixAllScope, fixAllFilter: FixAllItem[] | undefined): Promise<void> {
         let response = await serverUtils.runFixAll(this.server, {
             FileName: fileName,
             Scope: scope,
@@ -88,7 +93,7 @@ export class FixAllProvider extends AbstractProvider implements vscode.CodeActio
         });
 
         if (response) {
-            return buildEditForResponse(response.Changes, this._languageMiddlewareFeature, CancellationToken.None);
+            await buildEditForResponse(response.Changes, this._languageMiddlewareFeature, CancellationToken.None);
         }
     }
 }
diff --git a/src/features/formattingEditProvider.ts b/src/features/formattingEditProvider.ts
index d31a60b9..5a143e94 100644
--- a/src/features/formattingEditProvider.ts
+++ b/src/features/formattingEditProvider.ts
@@ -26,9 +26,9 @@ export default class FormattingSupport extends AbstractSupport implements Docume
                 return res.Changes.map(FormattingSupport._asEditOptionation);
             }
         }
-        catch (error) {
-            return [];
-        }
+        catch {}
+
+        return [];
     }
 
     public async provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[]> {
@@ -46,9 +46,9 @@ export default class FormattingSupport extends AbstractSupport implements Docume
                 return res.Changes.map(FormattingSupport._asEditOptionation);
             }
         }
-        catch (error) {
-            return [];
-        }
+        catch {}
+
+        return [];
     }
 
     private static _asEditOptionation(change: protocol.TextChange): TextEdit {
diff --git a/src/features/hoverProvider.ts b/src/features/hoverProvider.ts
index 2940f63c..07ca3eb9 100644
--- a/src/features/hoverProvider.ts
+++ b/src/features/hoverProvider.ts
@@ -11,7 +11,7 @@ import { HoverProvider, Hover, TextDocument, CancellationToken, Position, Markdo
 
 export default class OmniSharpHoverProvider extends AbstractSupport implements HoverProvider {
 
-    public async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover> {
+    public async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover | undefined> {
         let request = createRequest<protocol.QuickInfoRequest>(document, position);
         try {
             const response = await serverUtils.getQuickInfo(this._server, request, token);
diff --git a/src/features/implementationProvider.ts b/src/features/implementationProvider.ts
index 4b1b1584..29b347cf 100644
--- a/src/features/implementationProvider.ts
+++ b/src/features/implementationProvider.ts
@@ -10,19 +10,18 @@ import { createRequest, toLocation } from '../omnisharp/typeConversion';
 import { TextDocument, Position, CancellationToken, ImplementationProvider, Definition } from 'vscode';
 
 export default class CSharpImplementationProvider extends AbstractSupport implements ImplementationProvider {
-    public async provideImplementation(document: TextDocument, position: Position, token: CancellationToken): Promise<Definition> {
-        const request = <FindImplementationsRequest>createRequest(document, position);
+    public async provideImplementation(document: TextDocument, position: Position, token: CancellationToken): Promise<Definition | undefined> {
+        const request = createRequest<FindImplementationsRequest>(document, position);
 
-        const implementations = await serverUtils.findImplementations(this._server, request, token).then(response => {
-            if (!response || !response.QuickFixes) {
-                return;
-            }
+        try {
+            const response = await serverUtils.findImplementations(this._server, request, token);
+            const implementations = response?.QuickFixes?.map(fix => toLocation(fix)) ?? [];
 
-            return response.QuickFixes.map(fix => toLocation(fix));
-        }).catch();
+            // Allow language middlewares to re-map its edits if necessary.
+            const result = await this._languageMiddlewareFeature.remap("remapLocations", implementations, token);
+            return result;
+        } catch {}
 
-        // Allow language middlewares to re-map its edits if necessary.
-        const result = await this._languageMiddlewareFeature.remap("remapLocations", implementations, token);
-        return result;
+        return undefined;
     }
 }
diff --git a/src/features/inlayHintProvider.ts b/src/features/inlayHintProvider.ts
index fd0a17c0..349e9e8d 100644
--- a/src/features/inlayHintProvider.ts
+++ b/src/features/inlayHintProvider.ts
@@ -60,11 +60,12 @@ export default class CSharpInlayHintProvider extends AbstractProvider implements
     }
 
     async resolveInlayHint?(hint: vscode.InlayHint, token: vscode.CancellationToken): Promise<vscode.InlayHint> {
-        if (!this._hintsMap.has(hint)) {
+        const inlayHint = this._hintsMap.get(hint);
+        if (inlayHint === undefined) {
             return Promise.reject(`Outdated inlay hint was requested to be resolved, aborting.`);
         }
 
-        const request: InlayHintResolveRequest = { Hint: this._hintsMap.get(hint) };
+        const request: InlayHintResolveRequest = { Hint: inlayHint };
 
         try {
             const result = await serverUtils.resolveInlayHints(this._server, request, token);
@@ -82,8 +83,8 @@ export default class CSharpInlayHintProvider extends AbstractProvider implements
             textEdits: toVSCodeTextEdits(inlayHint.TextEdits),
         };
 
-        function toVSCodeTextEdits(textEdits: LinePositionSpanTextChange[]): vscode.TextEdit[] {
-            return textEdits ? textEdits.map(toVSCodeTextEdit) : undefined;
+        function toVSCodeTextEdits(textEdits: LinePositionSpanTextChange[] | undefined): vscode.TextEdit[] | undefined {
+            return textEdits?.map(toVSCodeTextEdit) ?? undefined;
         }
     }
 }
diff --git a/src/features/json/jsonContributions.ts b/src/features/json/jsonContributions.ts
index 4e25d0f2..3001d6af 100644
--- a/src/features/json/jsonContributions.ts
+++ b/src/features/json/jsonContributions.ts
@@ -22,11 +22,11 @@ export interface ISuggestionsCollector {
 
 export interface IJSONContribution {
     getDocumentSelector(): DocumentSelector;
-    getInfoContribution(fileName: string, location: Location): Thenable<MarkedString[]>;
-    collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable<void>;
-    collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<void>;
-    collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable<void>;
-    resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem>;
+    getInfoContribution(fileName: string, location: Location): Promise<MarkedString[] | undefined>;
+    collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Promise<void>;
+    collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Promise<void>;
+    collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Promise<void>;
+    resolveSuggestion?(item: CompletionItem): Promise<CompletionItem | undefined>;
 }
 
 export function addJSONProviders(): CompositeDisposable {
@@ -35,7 +35,7 @@ export function addJSONProviders(): CompositeDisposable {
     // configure the XHR library with the latest proxy settings
     function configureHttpRequest() {
         let httpSettings = workspace.getConfiguration('http');
-        configureXHR(httpSettings.get<string>('proxy'), httpSettings.get<boolean>('proxyStrictSSL'));
+        configureXHR(httpSettings.get<string>('proxy', ''), httpSettings.get<boolean>('proxyStrictSSL', false));
     }
 
     configureHttpRequest();
@@ -58,24 +58,22 @@ export class JSONHoverProvider implements HoverProvider {
     constructor(private jsonContribution: IJSONContribution) {
     }
 
-    public provideHover(document: TextDocument, position: Position, token: CancellationToken): Thenable<Hover> {
+    public async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover | undefined> {
         let offset = document.offsetAt(position);
         let location = getLocation(document.getText(), offset);
         let node = location.previousNode;
-        if (node && node.offset <= offset && offset <= node.offset + node.length) {
-            let promise = this.jsonContribution.getInfoContribution(document.fileName, location);
-            if (promise) {
-                return promise.then(htmlContent => {
-                    let range = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
-                    let result: Hover = {
-                        contents: htmlContent,
-                        range: range
-                    };
-                    return result;
-                });
+        if (node !== undefined && node.offset <= offset && offset <= node.offset + node.length) {
+            let htmlContent = await this.jsonContribution.getInfoContribution(document.fileName, location);
+            if (htmlContent !== undefined) {
+                let range = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
+                let result: Hover = {
+                    contents: htmlContent,
+                    range: range
+                };
+                return result;
             }
         }
-        return null;
+        return undefined;
     }
 }
 
@@ -84,19 +82,19 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
     constructor(private jsonContribution: IJSONContribution) {
     }
 
-    public resolveCompletionItem(item: CompletionItem, token: CancellationToken): Thenable<CompletionItem> {
+    public async resolveCompletionItem(item: CompletionItem, token: CancellationToken): Promise<CompletionItem> {
         if (this.jsonContribution.resolveSuggestion) {
-            let resolver = this.jsonContribution.resolveSuggestion(item);
-            if (resolver) {
+            let resolver = await this.jsonContribution.resolveSuggestion(item);
+            if (resolver !== undefined) {
                 return resolver;
             }
         }
-        return Promise.resolve(item);
+        return item;
     }
 
-    public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Thenable<CompletionList> {
+    public async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise<CompletionList | undefined> {
         let currentWord = this.getCurrentWord(document, position);
-        let overwriteRange: Range = null;
+        let overwriteRange: Range | undefined;
         let items: CompletionItem[] = [];
         let isIncomplete = false;
 
@@ -115,7 +113,7 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
             add: (suggestion: CompletionItem) => {
                 if (!proposed[<string>suggestion.label]) {
                     proposed[<string>suggestion.label] = true;
-                    if (overwriteRange) {
+                    if (overwriteRange !== undefined) {
                         suggestion.insertText = suggestion.insertText;
                         suggestion.range = overwriteRange;
                     }
@@ -128,7 +126,7 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
             log: (message: string) => console.log(message)
         };
 
-        let collectPromise: Thenable<void> = null;
+        let collectPromise: Promise<void> | undefined;
 
         if (location.isAtPropertyKey) {
             let addValue = !location.previousNode || !location.previousNode.colonOffset && (offset == (location.previousNode.offset + location.previousNode.length));
@@ -144,15 +142,12 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
                 collectPromise = this.jsonContribution.collectValueSuggestions(document.fileName, location, collector);
             }
         }
-        if (collectPromise) {
-            return collectPromise.then(() => {
-                if (items.length > 0) {
-                    return new CompletionList(items, isIncomplete);
-                }
-                return null;
-            });
+
+        await collectPromise;
+        if (items.length > 0) {
+            return new CompletionList(items, isIncomplete);
         }
-        return null;
+        return undefined;
     }
 
     private getCurrentWord(document: TextDocument, position: Position) {
diff --git a/src/features/json/projectJSONContribution.ts b/src/features/json/projectJSONContribution.ts
index 92e00b6d..432796e0 100644
--- a/src/features/json/projectJSONContribution.ts
+++ b/src/features/json/projectJSONContribution.ts
@@ -21,15 +21,12 @@ const FEED_INDEX_URL = 'https://api.nuget.org/v3/index.json';
 const LIMIT = 30;
 
 interface NugetServices {
-    'SearchQueryService'?: string;
-    'SearchAutocompleteService'?: string;
-    'PackageBaseAddress/3.0.0'?: string;
     [key: string]: string;
 }
 
 export class ProjectJSONContribution implements IJSONContribution {
 
-    private nugetIndexPromise: Thenable<NugetServices>;
+    private nugetIndexPromise?: Promise<NugetServices>;
 
     public constructor(private requestService: XHRRequest) {
     }
@@ -38,135 +35,131 @@ export class ProjectJSONContribution implements IJSONContribution {
         return [{ language: 'json', pattern: '**/project.json' }];
     }
 
-    private getNugetIndex(): Thenable<NugetServices> {
-        if (!this.nugetIndexPromise) {
-            this.nugetIndexPromise = this.makeJSONRequest<NuGetIndexResponse>(FEED_INDEX_URL).then(indexContent => {
-                let services: NugetServices = {};
-                if (indexContent && Array.isArray(indexContent.resources)) {
-                    let resources = indexContent.resources;
-                    for (let i = resources.length - 1; i >= 0; i--) {
-                        let type = resources[i]['@type'];
-                        let id = resources[i]['@id'];
-                        if (type && id) {
-                            services[type] = id;
-                        }
+    private async getNugetIndex(): Promise<NugetServices> {
+        if (this.nugetIndexPromise === undefined) {
+            const indexContent = await this.makeJSONRequest<NuGetIndexResponse>(FEED_INDEX_URL);
+            let services: NugetServices = {};
+            if (indexContent && Array.isArray(indexContent.resources)) {
+                let resources = indexContent.resources;
+                for (let i = resources.length - 1; i >= 0; i--) {
+                    let type = resources[i]['@type'];
+                    let id = resources[i]['@id'];
+                    if (type && id) {
+                        services[type] = id;
                     }
                 }
-                return services;
-            });
+            }
+            return services;
         }
         return this.nugetIndexPromise;
     }
 
-    private getNugetService(serviceType: string): Thenable<string> {
-        return this.getNugetIndex().then(services => {
-            let serviceURL = services[serviceType];
-            if (!serviceURL) {
-                return Promise.reject<string>(localize('json.nugget.error.missingservice', 'NuGet index document is missing service {0}', serviceType));
-            }
-            return serviceURL;
-        });
+    private async getNugetService(serviceType: string): Promise<string> {
+        const services = await this.getNugetIndex();
+        let serviceURL = services[serviceType];
+        if (!serviceURL) {
+            return Promise.reject(localize('json.nugget.error.missingservice', 'NuGet index document is missing service {0}', serviceType));
+        }
+        return serviceURL;
     }
 
-    private makeJSONRequest<T>(url: string): Thenable<T> {
-        return this.requestService({
+    private async makeJSONRequest<T>(url: string): Promise<T> {
+        const response = await this.requestService({
             url: url
-        }).then(success => {
-            if (success.status === 200) {
+        });
+
+        try {
+            if (response.status === 200) {
                 try {
-                    return <T>JSON.parse(success.responseText);
+                    return JSON.parse(response.responseText) as T;
                 } catch (e) {
-                    return Promise.reject<T>(localize('json.nugget.error.invalidformat', '{0} is not a valid JSON document', url));
+                    return Promise.reject(localize('json.nugget.error.invalidformat', '{0} is not a valid JSON document', url));
                 }
             }
-            return Promise.reject<T>(localize('json.nugget.error.indexaccess', 'Request to {0} failed: {1}', url, success.responseText));
-        }, async (error: XHRResponse) => {
-            return Promise.reject<T>(localize('json.nugget.error.access', 'Request to {0} failed: {1}', url, getErrorStatusDescription(error.status)));
-        });
+            return Promise.reject(localize('json.nugget.error.indexaccess', 'Request to {0} failed: {1}', url, response.responseText));
+        } catch (error) {
+            return Promise.reject(localize('json.nugget.error.access', 'Request to {0} failed: {1}', url, getErrorStatusDescription((error as XHRResponse).status)));
+        }
     }
 
-    public collectPropertySuggestions(
+    public async collectPropertySuggestions(
         resource: string,
         location: Location,
         currentWord: string,
         addValue: boolean,
         isLast: boolean,
-        result: ISuggestionsCollector): Thenable<void> {
+        result: ISuggestionsCollector): Promise<void> {
         if ((location.matches(['dependencies']) || location.matches(['frameworks', '*', 'dependencies']) || location.matches(['frameworks', '*', 'frameworkAssemblies']))) {
-
-            return this.getNugetService('SearchAutocompleteService').then(service => {
+            try {
+                const service = await this.getNugetService('SearchAutocompleteService');
                 let queryUrl: string;
                 if (currentWord.length > 0) {
                     queryUrl = service + '?q=' + encodeURIComponent(currentWord) + '&take=' + LIMIT;
                 } else {
                     queryUrl = service + '?take=' + LIMIT;
                 }
-                return this.makeJSONRequest<NuGetSearchAutocompleteServiceResponse>(queryUrl).then(resultObj => {
-                    if (Array.isArray(resultObj.data)) {
-                        let results = resultObj.data;
-                        for (let i = 0; i < results.length; i++) {
-                            let name = results[i];
-                            let insertText = JSON.stringify(name);
-                            if (addValue) {
-                                insertText += ': "{{}}"';
-                                if (!isLast) {
-                                    insertText += ',';
-                                }
+
+                const response = await this.makeJSONRequest<NuGetSearchAutocompleteServiceResponse>(queryUrl);
+                if (Array.isArray(response.data)) {
+                    let results = response.data;
+                    for (let i = 0; i < results.length; i++) {
+                        let name = results[i];
+                        let insertText = JSON.stringify(name);
+                        if (addValue) {
+                            insertText += ': "{{}}"';
+                            if (!isLast) {
+                                insertText += ',';
                             }
-                            let proposal = new CompletionItem(name);
-                            proposal.kind = CompletionItemKind.Property;
-                            proposal.insertText = insertText;
-                            proposal.filterText = JSON.stringify(name);
-                            result.add(proposal);
-                        }
-                        if (results.length === LIMIT) {
-                            result.setAsIncomplete();
                         }
+                        let proposal = new CompletionItem(name);
+                        proposal.kind = CompletionItemKind.Property;
+                        proposal.insertText = insertText;
+                        proposal.filterText = JSON.stringify(name);
+                        result.add(proposal);
+                    }
+                    if (results.length === LIMIT) {
+                        result.setAsIncomplete();
                     }
-                }, error => {
-                    result.error(error);
-                });
-            }, error => {
-                result.error(error);
-            });
+                }
+            } catch (error) {
+                const message = (error as Error).message;
+                result.error(message);
+            }
         }
-        return null;
     }
 
-    public collectValueSuggestions(resource: string, location: Location, result: ISuggestionsCollector): Thenable<void> {
+    public async collectValueSuggestions(resource: string, location: Location, result: ISuggestionsCollector): Promise<void> {
         if ((location.matches(['dependencies', '*']) || location.matches(['frameworks', '*', 'dependencies', '*']) || location.matches(['frameworks', '*', 'frameworkAssemblies', '*']))) {
-            return this.getNugetService('PackageBaseAddress/3.0.0').then(service => {
+            try {
+                const service = await this.getNugetService('PackageBaseAddress/3.0.0');
                 let currentKey = location.path[location.path.length - 1];
                 if (typeof currentKey === 'string') {
                     let queryUrl = service + currentKey + '/index.json';
-                    return this.makeJSONRequest<NuGetFlatContainerPackageResponse>(queryUrl).then(obj => {
-                        if (Array.isArray(obj.versions)) {
-                            let results = obj.versions;
-                            for (let i = 0; i < results.length; i++) {
-                                let curr = results[i];
-                                let name = JSON.stringify(curr);
-                                let proposal = new CompletionItem(name);
-                                proposal.kind = CompletionItemKind.Class;
-                                proposal.insertText = name;
-                                proposal.documentation = '';
-                                result.add(proposal);
-                            }
-                            if (results.length === LIMIT) {
-                                result.setAsIncomplete();
-                            }
+                    const response = await this.makeJSONRequest<NuGetFlatContainerPackageResponse>(queryUrl);
+                    if (Array.isArray(response.versions)) {
+                        let results = response.versions;
+                        for (let i = 0; i < results.length; i++) {
+                            let curr = results[i];
+                            let name = JSON.stringify(curr);
+                            let proposal = new CompletionItem(name);
+                            proposal.kind = CompletionItemKind.Class;
+                            proposal.insertText = name;
+                            proposal.documentation = '';
+                            result.add(proposal);
                         }
-                    }, error => {
-                        result.error(error);
-                    });
+                        if (results.length === LIMIT) {
+                            result.setAsIncomplete();
+                        }
+                    }
                 }
-            }, error => {
-                result.error(error);
-            });
+            } catch (error) {
+                const message = (error as Error).message;
+                result.error(message);
+            }
         }
-        return null;
     }
 
-    public collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Thenable<null> {
+    public async collectDefaultSuggestions(resource: string, result: ISuggestionsCollector): Promise<void> {
         let defaultValue = {
             'version': '{{1.0.0-*}}',
             'dependencies': {},
@@ -179,69 +172,62 @@ export class ProjectJSONContribution implements IJSONContribution {
         proposal.kind = CompletionItemKind.Module;
         proposal.insertText = JSON.stringify(defaultValue, null, '\t');
         result.add(proposal);
-        return null;
     }
 
-    public resolveSuggestion(item: CompletionItem): Thenable<CompletionItem> {
+    public async resolveSuggestion(item: CompletionItem): Promise<CompletionItem | undefined> {
         if (item.kind === CompletionItemKind.Property) {
             let pack = item.label;
-            return this.getInfo(<string>pack).then(info => {
-                if (info.description) {
-                    item.documentation = info.description;
-                }
-                if (info.version) {
-                    item.detail = info.version;
-                    item.insertText = (<string>item.insertText).replace(/\{\{\}\}/, '{{' + info.version + '}}');
-                }
-                return item;
-            });
+            const info = await this.getInfo(<string>pack);
+            if (info !== undefined) {
+                item.documentation = info.description;
+                item.detail = info.version;
+                item.insertText = (<string>item.insertText).replace(/\{\{\}\}/, '{{' + info.version + '}}');
+            }
+            return item;
         }
-        return null;
+        return undefined;
     }
 
-    private getInfo(pack: string): Thenable<{ description?: string; version?: string }> {
-        return this.getNugetService('SearchQueryService').then(service => {
+    private async getInfo(pack: string): Promise<{ description: string; version: string } | undefined> {
+        try {
+            const service = await this.getNugetService('SearchQueryService');
             let queryUrl = service + '?q=' + encodeURIComponent(pack) + '&take=' + 5;
-            return this.makeJSONRequest<NuGetSearchQueryServiceResponse>(queryUrl).then(resultObj => {
-                if (Array.isArray(resultObj.data)) {
-                    let results = resultObj.data;
-                    let info: { description?: string; version?: string } = {};
-                    for (let i = 0; i < results.length; i++) {
-                        let res = results[i];
-                        if (res.id === pack) {
-                            info.description = res.description;
-                            info.version = localize('json.nugget.version.hover', 'Latest version: {0}', res.version);
-                        }
+            const response = await this.makeJSONRequest<NuGetSearchQueryServiceResponse>(queryUrl);
+            if (Array.isArray(response.data)) {
+                let results = response.data;
+                let info: { description: string; version: string } | undefined;
+                for (let i = 0; i < results.length; i++) {
+                    let res = results[i];
+                    if (res.id === pack) {
+                        info = {
+                            description: res.description,
+                            version: localize('json.nugget.version.hover', 'Latest version: {0}', res.version),
+                        };
                     }
-                    return info;
                 }
-                return null;
-            }, (error) => {
-                return null;
-            });
-        }, (error) => {
-            return null;
-        });
+                return info;
+            }
+            return undefined;
+        } catch (error) {
+            return undefined;
+        };
     }
 
 
-    public getInfoContribution(resource: string, location: Location): Thenable<MarkedString[]> {
+    public async getInfoContribution(resource: string, location: Location): Promise<MarkedString[] | undefined> {
         if ((location.matches(['dependencies', '*']) || location.matches(['frameworks', '*', 'dependencies', '*']) || location.matches(['frameworks', '*', 'frameworkAssemblies', '*']))) {
             let pack = location.path[location.path.length - 1];
             if (typeof pack === 'string') {
-                return this.getInfo(pack).then(info => {
+                const info = await this.getInfo(pack);
+                if (info !== undefined) {
                     let htmlContent: MarkedString[] = [];
                     htmlContent.push(localize('json.nugget.package.hover', '{0}', pack));
-                    if (info.description) {
-                        htmlContent.push(info.description);
-                    }
-                    if (info.version) {
-                        htmlContent.push(info.version);
-                    }
+                    htmlContent.push(info.description);
+                    htmlContent.push(info.version);
                     return htmlContent;
-                });
+                }
             }
         }
-        return null;
+        return undefined;
     }
 }
diff --git a/src/features/processPicker.ts b/src/features/processPicker.ts
index e7e77b73..8e0a9f32 100644
--- a/src/features/processPicker.ts
+++ b/src/features/processPicker.ts
@@ -24,21 +24,14 @@ export interface AttachItemsProvider {
 export class AttachPicker {
     constructor(private attachItemsProvider: AttachItemsProvider) { }
 
-    public async ShowAttachEntries(): Promise<AttachItem> {
-        return this.attachItemsProvider.getAttachItems()
-            .then(processEntries => {
-                let attachPickOptions: vscode.QuickPickOptions = {
-                    ignoreFocusOut: true,
-                    matchOnDescription: true,
-                    matchOnDetail: true,
-                    placeHolder: "Select the process to attach to"
-                };
-
-                return vscode.window.showQuickPick(processEntries, attachPickOptions)
-                    .then(chosenProcess => {
-                        return chosenProcess;
-                    });
-            });
+    public async ShowAttachEntries(): Promise<AttachItem | undefined> {
+        const processEntries = await this.attachItemsProvider.getAttachItems();
+        return vscode.window.showQuickPick(processEntries, {
+            ignoreFocusOut: true,
+            matchOnDescription: true,
+            matchOnDetail: true,
+            placeHolder: "Select the process to attach to",
+        });
     }
 }
 
@@ -57,13 +50,14 @@ export class RemoteAttachPicker {
     public static get scriptShellCmd() { return "sh -s"; }
 
 
-    private static _channel: vscode.OutputChannel = null;
+    private static _channel: vscode.OutputChannel | undefined;
 
     public static async ValidateAndFixPipeProgram(program: string): Promise<string> {
         return PlatformInformation.GetCurrent().then(platformInfo => {
             // Check if we are on a 64 bit Windows
             if (platformInfo.isWindows() && platformInfo.architecture === "x86_64") {
-                let sysRoot: string = process.env.SystemRoot;
+                // If this doesn't exist, a lot more than OmniSharp is going to break.
+                let sysRoot = process.env.SystemRoot!;
                 let oldPath = path.join(sysRoot, 'System32');
                 let newPath = path.join(sysRoot, 'sysnative');
 
@@ -94,9 +88,9 @@ export class RemoteAttachPicker {
         let pipeArgs: string[] | string = pipeTransport.pipeArgs;
         let pipeCwd: string = pipeTransport.pipeCwd;
         let quoteArgs: boolean = pipeTransport.quoteArgs != null ? pipeTransport.quoteArgs : true; // default value is true
-        let platformSpecificPipeTransportOptions: IPipeTransportOptions = this.getPlatformSpecificPipeTransportOptions(pipeTransport, osPlatform);
+        let platformSpecificPipeTransportOptions = this.getPlatformSpecificPipeTransportOptions(pipeTransport, osPlatform);
 
-        if (platformSpecificPipeTransportOptions) {
+        if (platformSpecificPipeTransportOptions !== undefined) {
             pipeProgram = platformSpecificPipeTransportOptions.pipeProgram || pipeProgram;
             pipeArgs = platformSpecificPipeTransportOptions.pipeArgs || pipeArgs;
             pipeCwd = platformSpecificPipeTransportOptions.pipeCwd || pipeCwd;
@@ -115,7 +109,7 @@ export class RemoteAttachPicker {
     // is included, then use that specific pipe transport configuration.
     //
     // Note: osPlatform is passed as an argument for testing.
-    private static getPlatformSpecificPipeTransportOptions(config: any, osPlatform: string): IPipeTransportOptions {
+    private static getPlatformSpecificPipeTransportOptions(config: any, osPlatform: string): IPipeTransportOptions | undefined {
         if (osPlatform === "darwin" && config.osx) {
             return config.osx;
         } else if (osPlatform === "linux" && config.linux) {
@@ -124,7 +118,7 @@ export class RemoteAttachPicker {
             return config.windows;
         }
 
-        return null;
+        return undefined;
     }
 
     // Creates a pipe command string based on the type of pipe args.
@@ -197,9 +191,9 @@ export class RemoteAttachPicker {
         return args.map(arg => this.quoteArg(arg)).join(" ");
     }
 
-    public static async ShowAttachEntries(args: any, platformInfo: PlatformInformation): Promise<AttachItem> {
+    public static async ShowAttachEntries(args: any, platformInfo: PlatformInformation): Promise<AttachItem | undefined> {
         // Create remote attach output channel for errors.
-        if (!RemoteAttachPicker._channel) {
+        if (RemoteAttachPicker._channel === undefined) {
             RemoteAttachPicker._channel = vscode.window.createOutputChannel('remote-attach');
         } else {
             RemoteAttachPicker._channel.clear();
@@ -219,26 +213,22 @@ export class RemoteAttachPicker {
             return Promise.reject<AttachItem>(new Error("Configuration \"" + name + "\" in launch.json does not have a " +
                 "pipeTransport argument with debuggerPath for remote process listing."));
         } else {
-            let pipeTransport = this.getPipeTransportOptions(args.pipeTransport, os.platform());
-
-            return RemoteAttachPicker.createPipeCmd(pipeTransport.pipeProgram, pipeTransport.pipeArgs, pipeTransport.quoteArgs)
-                .then(async pipeCmd => RemoteAttachPicker.getRemoteOSAndProcesses(pipeCmd, pipeTransport.pipeCwd, platformInfo))
-                .then(processes => {
-                    let attachPickOptions: vscode.QuickPickOptions = {
-                        ignoreFocusOut: true,
-                        matchOnDescription: true,
-                        matchOnDetail: true,
-                        placeHolder: "Select the process to attach to"
-                    };
-                    return vscode.window.showQuickPick(processes, attachPickOptions);
-                });
+            const pipeTransport = this.getPipeTransportOptions(args.pipeTransport, os.platform());
+            const pipeCmd = await RemoteAttachPicker.createPipeCmd(pipeTransport.pipeProgram, pipeTransport.pipeArgs, pipeTransport.quoteArgs);
+            const processes = await RemoteAttachPicker.getRemoteOSAndProcesses(pipeCmd, pipeTransport.pipeCwd, RemoteAttachPicker._channel, platformInfo);
+            return vscode.window.showQuickPick(processes, {
+                ignoreFocusOut: true,
+                matchOnDescription: true,
+                matchOnDetail: true,
+                placeHolder: "Select the process to attach to",
+            });
         }
     }
 
-    public static async getRemoteOSAndProcesses(pipeCmd: string, pipeCwd: string, platformInfo: PlatformInformation): Promise<AttachItem[]> {
+    public static async getRemoteOSAndProcesses(pipeCmd: string, pipeCwd: string, channel: vscode.OutputChannel, platformInfo: PlatformInformation): Promise<AttachItem[]> {
         const scriptPath = path.join(getExtensionPath(), 'scripts', 'remoteProcessPickerScript');
 
-        return execChildProcessAndOutputErrorToChannel(`${pipeCmd} < "${scriptPath}"`, pipeCwd, RemoteAttachPicker._channel, platformInfo).then(output => {
+        return execChildProcessAndOutputErrorToChannel(`${pipeCmd} < "${scriptPath}"`, pipeCwd, channel, platformInfo).then(output => {
             // OS will be on first line
             // Processess will follow if listed
             let lines = output.split(/\r?\n/);
@@ -265,24 +255,17 @@ export class RemoteAttachPicker {
     }
 }
 
-export class Process {
-    constructor(public name: string, public pid: string, public commandLine: string, public flags: number) { }
-
-    public toAttachItem(): AttachItem {
-        return {
-            label: this.name,
-            description: this.pid,
-            detail: this.commandLine,
-            id: this.pid,
-            flags: this.flags
-        };
-    }
+export interface Process {
+    name: string;
+    pid: string;
+    commandLine?: string;
+    flags?: number;
 }
 
 export class DotNetAttachItemsProviderFactory {
     static Get(): AttachItemsProvider {
         if (os.platform() === 'win32') {
-            const pwsh: string | undefined = findPowerShell();
+            const pwsh = findPowerShell();
             return pwsh ? new CimAttachItemsProvider(pwsh) : new WmicAttachItemsProvider();
         }
         else {
@@ -307,7 +290,13 @@ function sortProcessEntries(processEntries: Process[], osPlatform: string): Atta
     let dotnetProcessName = (osPlatform === 'win32') ? 'dotnet.exe' : 'dotnet';
     processEntries = processEntries.sort((a, b) => {
         if (a.name.toLowerCase() === dotnetProcessName && b.name.toLowerCase() === dotnetProcessName) {
-            return a.commandLine.toLowerCase() < b.commandLine.toLowerCase() ? -1 : 1;
+            if (a.commandLine !== undefined && b.commandLine !== undefined) {
+                return a.commandLine.toLowerCase() < b.commandLine.toLowerCase() ? -1 : 1;
+            } else if (a.commandLine !== undefined) {
+                return -1;
+            } else {
+                return 1;
+            }
         } else if (a.name.toLowerCase() === dotnetProcessName) {
             return -1;
         } else if (b.name.toLowerCase() === dotnetProcessName) {
@@ -317,7 +306,13 @@ function sortProcessEntries(processEntries: Process[], osPlatform: string): Atta
         }
     });
 
-    let attachItems = processEntries.map(p => p.toAttachItem());
+    let attachItems = processEntries.map(process => ({
+        label: process.name,
+        description: process.pid,
+        detail: process.commandLine,
+        id: process.pid,
+        flags: process.flags ?? 0,
+    }));
     return attachItems;
 }
 
@@ -329,7 +324,7 @@ export class PsAttachItemsProvider extends DotNetAttachItemsProvider {
         // https://bugzilla.redhat.com/show_bug.cgi?id=429565
         // Since 'args' contains the full path to the executable, even if truncated, searching will work as desired.
         const psCommand = os.platform() === 'darwin' ? RemoteAttachPicker.osxPsCommand : RemoteAttachPicker.linuxPsCommand;
-        return execChildProcess(psCommand, null).then(processes => {
+        return execChildProcess(psCommand).then(processes => {
             return PsOutputParser.parseProcessFromPs(processes);
         });
     }
@@ -401,7 +396,7 @@ export class PsOutputParser {
         return processEntries;
     }
 
-    private static parseLineFromPs(line: string): Process {
+    private static parseLineFromPs(line: string): Process | undefined {
         // Explanation of the regex:
         //   - any leading whitespace
         //   - PID
@@ -414,13 +409,20 @@ export class PsOutputParser {
         //   - args (might be empty)
         const psEntry = new RegExp(`^\\s*([0-9]+)\\s+([0-9a-fA-F]+)\\s+(.{${PsOutputParser.secondColumnCharacters - 1}})\\s+(.*)$`);
         const matches = psEntry.exec(line);
-        if (matches && matches.length === 5) {
+        if (matches?.length === 5) {
             const pid = matches[1].trim();
             const flags = parseInt(matches[2].trim(), 16); // flags comes in as hex
-            const executable = matches[3].trim();
+            const name = matches[3].trim();
             const cmdline = matches[4].trim();
-            return new Process(executable, pid, cmdline, flags);
+            return {
+                name,
+                pid,
+                commandLine: cmdline,
+                flags,
+            };
         }
+
+        return undefined;
     }
 }
 
@@ -430,7 +432,7 @@ export class CimAttachItemsProvider extends DotNetAttachItemsProvider {
     protected async getInternalProcessEntries(): Promise<Process[]> {
         const pwshCommand: string = `${this.pwsh} -NoProfile -Command`;
         const cimCommand: string = 'Get-CimInstance Win32_Process | Select-Object Name,ProcessId,CommandLine | ConvertTo-JSON';
-        const processes: string = await execChildProcess(`${pwshCommand} "${cimCommand}"`, undefined);
+        const processes: string = await execChildProcess(`${pwshCommand} "${cimCommand}"`);
         return CimProcessParser.ParseProcessFromCim(processes);
     }
 }
@@ -449,14 +451,18 @@ export class CimProcessParser {
     public static ParseProcessFromCim(processes: string): Process[] {
         const processInfos: CimProcessInfo[] = JSON.parse(processes);
         return processInfos.map(info => {
-            let cmdline: string | undefined = info.CommandLine || undefined;
-            if (cmdline?.startsWith(this.extendedLengthPathPrefix)) {
-                cmdline = cmdline.slice(this.extendedLengthPathPrefix.length);
+            let commandLine = info.CommandLine ?? undefined;
+            if (commandLine?.startsWith(this.extendedLengthPathPrefix)) {
+                commandLine = commandLine.slice(this.extendedLengthPathPrefix.length);
             }
-            if (cmdline?.startsWith(this.ntObjectManagerPathPrefix)) {
-                cmdline = cmdline.slice(this.ntObjectManagerPathPrefix.length);
+            if (commandLine?.startsWith(this.ntObjectManagerPathPrefix)) {
+                commandLine = commandLine.slice(this.ntObjectManagerPathPrefix.length);
             }
-            return new Process(info.Name, `${info.ProcessId}`, cmdline, null);
+            return {
+                name: info.Name,
+                pid: `${info.ProcessId}`,
+                commandLine,
+            };
         });
     }
 }
@@ -464,7 +470,7 @@ export class CimProcessParser {
 export class WmicAttachItemsProvider extends DotNetAttachItemsProvider {
     protected async getInternalProcessEntries(): Promise<Process[]> {
         const wmicCommand = 'wmic process get Name,ProcessId,CommandLine /FORMAT:list';
-        return execChildProcess(wmicCommand, null).then(processes => {
+        return execChildProcess(wmicCommand).then(processes => {
             return WmicOutputParser.parseProcessFromWmic(processes);
         });
     }
@@ -486,7 +492,7 @@ export class WmicOutputParser {
     // Only public for tests.
     public static parseProcessFromWmic(processes: string): Process[] {
         let lines = processes.split(os.EOL);
-        let currentProcess: Process = new Process(null, null, null, null);
+        let currentProcess: Partial<Process> = {};
         let processEntries: Process[] = [];
 
         for (let i = 0; i < lines.length; i++) {
@@ -499,15 +505,15 @@ export class WmicOutputParser {
 
             // Each entry of processes has ProcessId as the last line
             if (line.startsWith(WmicOutputParser.wmicPidTitle)) {
-                processEntries.push(currentProcess);
-                currentProcess = new Process(null, null, null, null);
+                processEntries.push(currentProcess as Process);
+                currentProcess = {};
             }
         }
 
         return processEntries;
     }
 
-    private static parseLineFromWmic(line: string, process: Process) {
+    private static parseLineFromWmic(line: string, process: Partial<Process>) {
         let splitter = line.indexOf('=');
         if (splitter >= 0) {
             let key = line.slice(0, line.indexOf('='));
@@ -531,9 +537,9 @@ export class WmicOutputParser {
 
 }
 
-async function execChildProcess(process: string, workingDirectory: string): Promise<string> {
+async function execChildProcess(process: string): Promise<string> {
     return new Promise<string>((resolve, reject) => {
-        child_process.exec(process, { cwd: workingDirectory, maxBuffer: 500 * 1024 }, (error: Error, stdout: string, stderr: string) => {
+        child_process.exec(process, { maxBuffer: 500 * 1024 }, (error, stdout, stderr) => {
             if (error) {
                 reject(error);
                 return;
@@ -567,7 +573,7 @@ async function execChildProcessAndOutputErrorToChannel(process: string, workingD
 
     return new Promise<string>(async (resolve, reject) => {
         return GetSysNativePathIfNeeded(platformInfo).then(newEnv => {
-            child_process.exec(process, { cwd: workingDirectory, env: newEnv, maxBuffer: 500 * 1024 }, (error: Error, stdout: string, stderr: string) => {
+            child_process.exec(process, { cwd: workingDirectory, env: newEnv, maxBuffer: 500 * 1024 }, (error, stdout, stderr) => {
                 let channelOutput = "";
 
                 if (stdout && stdout.length > 0) {
diff --git a/src/features/referenceProvider.ts b/src/features/referenceProvider.ts
index f599a207..8721ac06 100644
--- a/src/features/referenceProvider.ts
+++ b/src/features/referenceProvider.ts
@@ -34,9 +34,9 @@ export default class OmnisharpReferenceProvider extends AbstractSupport implemen
                 return result;
             }
         }
-        catch (error) {
-            return [];
-        }
+        catch {}
+
+        return [];
     }
 
     private mapToLocationWithGeneratedInfoPopulation(symbolLocation: protocol.SymbolLocation): Location {
diff --git a/src/features/renameProvider.ts b/src/features/renameProvider.ts
index 586cb8c9..985817f9 100644
--- a/src/features/renameProvider.ts
+++ b/src/features/renameProvider.ts
@@ -11,7 +11,7 @@ import { RenameProvider, WorkspaceEdit, TextDocument, Uri, CancellationToken, Po
 
 export default class OmnisharpRenameProvider extends AbstractSupport implements RenameProvider {
 
-    public async provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): Promise<WorkspaceEdit> {
+    public async provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): Promise<WorkspaceEdit | undefined> {
 
         let req = createRequest<protocol.RenameRequest>(document, position);
         req.WantsTextChanges = true;
diff --git a/src/features/reportIssue.ts b/src/features/reportIssue.ts
index 75bd97c7..3657c340 100644
--- a/src/features/reportIssue.ts
+++ b/src/features/reportIssue.ts
@@ -3,9 +3,7 @@
  *  Licensed under the MIT License. See License.txt in the project root for license information.
  *--------------------------------------------------------------------------------------------*/
 
-import { vscode } from "../vscodeAdapter";
-import { Extension } from "../vscodeAdapter";
-import { CSharpExtensionId } from "../constants/CSharpExtensionId";
+import { vscode, Extension } from "../vscodeAdapter";
 import { EventStream } from "../EventStream";
 import { OpenURL } from "../omnisharp/loggingEvents";
 import { Options } from "../omnisharp/options";
@@ -15,12 +13,11 @@ import { dirname } from "path";
 
 const issuesUrl = "https://github.com/OmniSharp/omnisharp-vscode/issues/new";
 
-export default async function reportIssue(vscode: vscode, eventStream: EventStream, getDotnetInfo: IGetDotnetInfo, isValidPlatformForMono: boolean, options: Options, monoResolver: IHostExecutableResolver, dotnetResolver: IHostExecutableResolver) {
+export default async function reportIssue(vscode: vscode, csharpExtVersion: string, eventStream: EventStream, getDotnetInfo: IGetDotnetInfo, isValidPlatformForMono: boolean, options: Options, dotnetResolver: IHostExecutableResolver, monoResolver: IHostExecutableResolver) {
     // Get info for the dotnet that the Omnisharp executable is run on, not the dotnet Omnisharp will execute user code on.
     const dotnetInfo = await getDotnetInfo([ dirname((await dotnetResolver.getHostExecutableInfo(options)).path) ]);
     const monoInfo = await getMonoIfPlatformValid(isValidPlatformForMono, options, monoResolver);
     let extensions = getInstalledExtensions(vscode);
-    let csharpExtVersion = getCsharpExtensionVersion(vscode);
 
     const body = `## Issue Description ##
 ## Steps to Reproduce ##
@@ -85,12 +82,10 @@ ${tableHeader}\n${table};
 
 async function getMonoIfPlatformValid(isValidPlatformForMono: boolean, options: Options, monoResolver: IHostExecutableResolver): Promise<string> {
     if (isValidPlatformForMono) {
-        let monoVersion: string;
+        let monoVersion = "Unknown Mono version";
         try {
-            let monoInfo = await monoResolver.getHostExecutableInfo(options);
-            if (monoInfo) {
-                monoVersion = `OmniSharp using mono :${monoInfo.version}`;
-            }
+            const monoInfo = await monoResolver.getHostExecutableInfo(options);
+            monoVersion = `OmniSharp using mono: ${monoInfo.version}`;
         }
         catch (error) {
             monoVersion = `There is a problem with running OmniSharp on mono: ${error}`;
@@ -111,8 +106,3 @@ function getInstalledExtensions(vscode: vscode) {
 
     return extensions.sort(sortExtensions);
 }
-
-function getCsharpExtensionVersion(vscode: vscode): string {
-    const extension = vscode.extensions.getExtension(CSharpExtensionId);
-    return extension.packageJSON.version;
-}
diff --git a/src/features/semanticTokensProvider.ts b/src/features/semanticTokensProvider.ts
index 4bfaddf3..e71bcb22 100644
--- a/src/features/semanticTokensProvider.ts
+++ b/src/features/semanticTokensProvider.ts
@@ -171,7 +171,7 @@ export default class SemanticTokensProvider extends AbstractProvider implements
 
     async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null> {
 
-        return this._provideSemanticTokens(document, null, token);
+        return this._provideSemanticTokens(document, undefined, token);
     }
 
     async provideDocumentRangeSemanticTokens(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null> {
@@ -189,7 +189,7 @@ export default class SemanticTokensProvider extends AbstractProvider implements
         return this._provideSemanticTokens(document, v2Range, token);
     }
 
-    async _provideSemanticTokens(document: vscode.TextDocument, range: protocol.V2.Range, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null> {
+    async _provideSemanticTokens(document: vscode.TextDocument, range: protocol.V2.Range | undefined, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null> {
         // We can only semantically highlight file from disk.
         if (document.uri.scheme !== "file") {
             return null;
@@ -312,7 +312,7 @@ tokenModifiers[DefaultTokenModifier.modification] = 'modification';
 tokenModifiers[DefaultTokenModifier.async] = 'async';
 tokenModifiers[DefaultTokenModifier.readonly] = 'readonly';
 
-const tokenTypeMap: number[] = [];
+const tokenTypeMap: (number | undefined)[] = [];
 tokenTypeMap[SemanticHighlightClassification.Comment] = DefaultTokenType.comment;
 tokenTypeMap[SemanticHighlightClassification.ExcludedCode] = CustomTokenType.excludedCode;
 tokenTypeMap[SemanticHighlightClassification.Identifier] = DefaultTokenType.variable;
diff --git a/src/features/signatureHelpProvider.ts b/src/features/signatureHelpProvider.ts
index 565a2d88..23d1a25f 100644
--- a/src/features/signatureHelpProvider.ts
+++ b/src/features/signatureHelpProvider.ts
@@ -12,7 +12,7 @@ import { SignatureHelpParameter } from '../omnisharp/protocol';
 
 export default class OmniSharpSignatureHelpProvider extends AbstractSupport implements SignatureHelpProvider {
 
-    public async provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken): Promise<SignatureHelp> {
+    public async provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken): Promise<SignatureHelp | undefined> {
 
         let req = createRequest(document, position);
 
diff --git a/src/features/sourceGeneratedDocumentProvider.ts b/src/features/sourceGeneratedDocumentProvider.ts
index 52d77f1d..5bef7f76 100644
--- a/src/features/sourceGeneratedDocumentProvider.ts
+++ b/src/features/sourceGeneratedDocumentProvider.ts
@@ -11,7 +11,7 @@ import { OmniSharpServer } from '../omnisharp/server';
 
 export default class SourceGeneratedDocumentProvider implements TextDocumentContentProvider, IDisposable {
     readonly scheme = "omnisharp-source-generated";
-    private _registration: IDisposable;
+    private _registration?: IDisposable;
     private _documents: Map<SourceGeneratedFileInfo, SourceGeneratedFileResponse>;
     private _uriToDocumentInfo: Map<string, SourceGeneratedFileInfo>;
     private _documentClosedSubscription: IDisposable;
@@ -30,33 +30,33 @@ export default class SourceGeneratedDocumentProvider implements TextDocumentCont
 
     private async onTextDocumentClosed(document: TextDocument) {
         const uriString = document.uri.toString();
-        if (this._uriToDocumentInfo.has(uriString)) {
-            const info = this._uriToDocumentInfo.get(uriString);
+        const info = this._uriToDocumentInfo.get(uriString);
+        if (info !== undefined) {
             this._documents.delete(info);
             this._uriToDocumentInfo.delete(uriString);
             await serverUtils.sourceGeneratedFileClosed(this.server, info);
         }
     }
 
-    private async onVisibleTextEditorsChanged(editors?: TextEditor[]) {
+    private async onVisibleTextEditorsChanged(editors: readonly TextEditor[]) {
         for (const editor of editors) {
             const documentUri = editor.document.uri;
             const uriString = documentUri.toString();
-            if (this._uriToDocumentInfo.has(uriString)) {
+            const existingInfo = this._uriToDocumentInfo.get(uriString);
+            if (existingInfo !== undefined) {
                 try {
-                    const existingInfo = this._uriToDocumentInfo.get(uriString);
                     const existingResponse = this._documents.get(existingInfo);
-                    const update = await serverUtils.getUpdatedSourceGeneratedFile(this.server, existingInfo);
-                    if (!update) {
+                    if (existingResponse === undefined) {
                         continue;
                     }
 
+                    const update = await serverUtils.getUpdatedSourceGeneratedFile(this.server, existingInfo);
                     switch (update.UpdateType) {
                         case UpdateType.Deleted:
-                            this._documents.set(existingInfo, { Source: "Document is no longer being generated.", SourceName: existingResponse.SourceName });
+                            existingResponse.Source = "Document is no longer being generated.";
                             break;
                         case UpdateType.Modified:
-                            this._documents.set(existingInfo, { Source: update.Source, SourceName: existingResponse.SourceName });
+                            existingResponse.Source = update.Source;
                             break;
                         case UpdateType.Unchanged:
                             continue;
@@ -75,46 +75,37 @@ export default class SourceGeneratedDocumentProvider implements TextDocumentCont
     }
 
     public dispose() {
-        this._registration.dispose();
+        this._registration?.dispose();
         this._documentClosedSubscription.dispose();
         this._visibleTextEditorsChangedSubscription.dispose();
         this._documents.clear();
     }
 
-    public tryGetExistingSourceGeneratedFile(fileInfo: SourceGeneratedFileInfo): Uri | undefined {
-        if (this._documents.has(fileInfo)) {
-            return this.getUriForName(this._documents.get(fileInfo).SourceName);
-        }
-
-        return undefined;
-    }
-
     public addSourceGeneratedFileWithoutInitialContent(fileInfo: SourceGeneratedFileInfo, fileName: string): Uri {
-        if (this._documents.has(fileInfo)) {
-            // Raced with something, return the existing one
-            return this.tryGetExistingSourceGeneratedFile(fileInfo);
+        const response = this._documents.get(fileInfo);
+        if (response !== undefined) {
+            // Raced with something, return the existing one.
+            return this.getUriForName(response.SourceName);
         }
 
         const uri = this.getUriForName(fileName);
         const uriString = uri.toString();
 
-        if (this._uriToDocumentInfo.has(uriString)) {
-            this._documents.delete(fileInfo);
-            this._uriToDocumentInfo.delete(uriString);
-        }
-
-        // Provide will see the null and retrieve the file when asked.
-        this._documents.set(fileInfo, null);
+        // Provide will see that the document doesn't exist and retrieve the file when asked.
+        this._documents.delete(fileInfo);
         this._uriToDocumentInfo.set(uriString, fileInfo);
 
         return uri;
     }
 
-    public async provideTextDocumentContent(uri: Uri, token: CancellationToken): Promise<string> {
+    public async provideTextDocumentContent(uri: Uri, token: CancellationToken): Promise<string | undefined> {
         const fileInfo = this._uriToDocumentInfo.get(uri.toString());
-        let response = this._documents.get(fileInfo);
+        if (fileInfo === undefined) {
+            return undefined;
+        }
 
-        if (response === null) {
+        let response = this._documents.get(fileInfo);
+        if (response === undefined) {
             // No content yet, get it
             response = await serverUtils.getSourceGeneratedFile(this.server, fileInfo, token);
             this._documents.set(fileInfo, response);
diff --git a/src/features/structureProvider.ts b/src/features/structureProvider.ts
index 5ac7eb7c..50d9f8ec 100644
--- a/src/features/structureProvider.ts
+++ b/src/features/structureProvider.ts
@@ -6,11 +6,11 @@
 import { FoldingRangeProvider, TextDocument, FoldingContext, CancellationToken, FoldingRange, FoldingRangeKind } from "vscode";
 import AbstractSupport from './abstractProvider';
 import { blockStructure } from "../omnisharp/utils";
-import { Request } from "../omnisharp/protocol";
+import { V2 } from "../omnisharp/protocol";
 
 export class StructureProvider extends AbstractSupport implements FoldingRangeProvider {
     async provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken): Promise<FoldingRange[]> {
-        let request: Request = {
+        let request: V2.BlockStructureRequest = {
             FileName: document.fileName,
         };
 
@@ -28,7 +28,7 @@ export class StructureProvider extends AbstractSupport implements FoldingRangePr
         }
     }
 
-    GetType(type: string): FoldingRangeKind {
+    GetType(type: string): FoldingRangeKind | undefined {
         switch (type) {
             case "Comment":
                 return FoldingRangeKind.Comment;
@@ -37,8 +37,8 @@ export class StructureProvider extends AbstractSupport implements FoldingRangePr
             case "Region":
                 return FoldingRangeKind.Region;
             default:
-                return null;
+                return undefined;
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/src/features/workspaceSymbolProvider.ts b/src/features/workspaceSymbolProvider.ts
index 806b83e6..5e299276 100644
--- a/src/features/workspaceSymbolProvider.ts
+++ b/src/features/workspaceSymbolProvider.ts
@@ -26,23 +26,23 @@ export default class OmnisharpWorkspaceSymbolProvider extends AbstractSupport im
 
     public async provideWorkspaceSymbols(search: string, token: CancellationToken): Promise<SymbolInformation[]> {
 
-        let options = this.optionProvider.GetLatestOptions();
-        let minFilterLength = options.minFindSymbolsFilterLength > 0 ? options.minFindSymbolsFilterLength : undefined;
-        let maxItemsToReturn = options.maxFindSymbolsItems > 0 ? options.maxFindSymbolsItems : undefined;
+        const options = this.optionProvider.GetLatestOptions();
+        const minFilterLength = options.minFindSymbolsFilterLength > 0 ? options.minFindSymbolsFilterLength : undefined;
+        const maxItemsToReturn = options.maxFindSymbolsItems > 0 ? options.maxFindSymbolsItems : undefined;
 
-        if (minFilterLength != undefined && search.length < minFilterLength) {
+        if (minFilterLength !== undefined && search.length < minFilterLength) {
             return [];
         }
 
         try {
-            let res = await serverUtils.findSymbols(this._server, { Filter: search, MaxItemsToReturn: maxItemsToReturn, FileName: '' }, token);
-            if (res && Array.isArray(res.QuickFixes)) {
+            const res = await serverUtils.findSymbols(this._server, { Filter: search, MaxItemsToReturn: maxItemsToReturn }, token);
+            if (Array.isArray(res?.QuickFixes)) {
                 return res.QuickFixes.map(symbol => this._asSymbolInformation(symbol));
             }
         }
-        catch (error) {
-            return [];
-        }
+        catch {}
+
+        return [];
     }
 
     private _asSymbolInformation(symbolInfo: protocol.SymbolLocation): SymbolInformation {
diff --git a/src/omnisharp/extension.ts b/src/omnisharp/extension.ts
index 7e064985..40132801 100644
--- a/src/omnisharp/extension.ts
+++ b/src/omnisharp/extension.ts
@@ -98,7 +98,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
             localDisposables.add(vscode.languages.registerOnTypeFormattingEditProvider(documentSelector, new FormatProvider(server, languageMiddlewareFeature), '}', '/', '\n', ';'));
         }
         localDisposables.add(vscode.languages.registerCompletionItemProvider(documentSelector, completionProvider, '.', ' '));
-        localDisposables.add(vscode.commands.registerCommand(CompletionAfterInsertCommand, async (item) => completionProvider.afterInsert(item)));
+        localDisposables.add(vscode.commands.registerCommand(CompletionAfterInsertCommand, async (item, document) => completionProvider.afterInsert(item, document)));
         localDisposables.add(vscode.languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(server, optionProvider, languageMiddlewareFeature, sourceGeneratedDocumentProvider)));
         localDisposables.add(vscode.languages.registerSignatureHelpProvider(documentSelector, new SignatureHelpProvider(server, languageMiddlewareFeature), '(', ','));
         // Since the CodeActionProvider registers its own commands, we must instantiate it and add it to the localDisposables
diff --git a/src/omnisharp/loggingEvents.ts b/src/omnisharp/loggingEvents.ts
index f469d476..f75cf80a 100644
--- a/src/omnisharp/loggingEvents.ts
+++ b/src/omnisharp/loggingEvents.ts
@@ -90,7 +90,7 @@ export class OmnisharpRequestMessage implements BaseEvent {
 
 export class TestExecutionCountReport implements BaseEvent {
     type = EventType.TestExecutionCountReport;
-    constructor(public debugCounts: { [testFrameworkName: string]: number }, public runCounts: { [testFrameworkName: string]: number }) { }
+    constructor(public debugCounts: { [testFrameworkName: string]: number } | undefined, public runCounts: { [testFrameworkName: string]: number } | undefined) { }
 }
 
 export class OmnisharpServerOnError implements BaseEvent {
@@ -185,7 +185,7 @@ export class ZipError implements BaseEvent {
 
 export class ReportDotNetTestResults implements BaseEvent {
     type = EventType.ReportDotNetTestResults;
-    constructor(public results: protocol.V2.DotNetTestResult[]) { }
+    constructor(public results: protocol.V2.DotNetTestResult[] | undefined) { }
 }
 
 export class DotNetTestRunStart implements BaseEvent {
diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts
index 87ba5092..c52cf38a 100644
--- a/src/omnisharp/protocol.ts
+++ b/src/omnisharp/protocol.ts
@@ -71,7 +71,7 @@ export namespace WireProtocol {
 }
 
 export interface FileBasedRequest {
-    FileName: string;
+    FileName?: string;
 }
 
 export interface Request extends FileBasedRequest {
@@ -507,6 +507,12 @@ export interface RunFixAllRequest extends FileBasedRequest {
     ApplyChanges: boolean;
 }
 
+export interface ReAnalyzeRequest extends FileBasedRequest {
+}
+
+export interface ReAnalyzeReponse {
+}
+
 export interface QuickInfoRequest extends Request {
 }
 
@@ -576,11 +582,17 @@ export interface SourceGeneratedFileResponse {
 export interface UpdateSourceGeneratedFileRequest extends SourceGeneratedFileInfo {
 }
 
-export interface UpdateSourceGeneratedFileResponse {
-    UpdateType: UpdateType;
-    Source?: string;
+interface UpdateSourceGeneratedFileNotModifiedResponse {
+    UpdateType: Exclude<UpdateType, UpdateType.Modified>;
 }
 
+interface UpdateSourceGeneratedFileModifiedResponse {
+    UpdateType: UpdateType.Modified;
+    Source: string;
+}
+
+export type UpdateSourceGeneratedFileResponse = UpdateSourceGeneratedFileNotModifiedResponse | UpdateSourceGeneratedFileModifiedResponse;
+
 export enum UpdateType {
     Unchanged,
     Deleted,
@@ -745,9 +757,9 @@ export namespace V2 {
 
     // dotnet-test endpoints
     interface BaseTestRequest extends Request {
-        RunSettings: string;
+        RunSettings?: string;
         TestFrameworkName: string;
-        TargetFrameworkVersion: string;
+        TargetFrameworkVersion?: string;
         NoBuild?: boolean;
     }
 
diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts
index ab13d7fb..40136e60 100644
--- a/src/omnisharp/utils.ts
+++ b/src/omnisharp/utils.ts
@@ -137,8 +137,8 @@ export async function getMetadata(server: OmniSharpServer, request: protocol.Met
     return server.makeRequest<protocol.MetadataResponse>(protocol.Requests.Metadata, request);
 }
 
-export async function reAnalyze(server: OmniSharpServer, request: any) {
-    return server.makeRequest<any>(protocol.Requests.ReAnalyze, request);
+export async function reAnalyze(server: OmniSharpServer, request: protocol.ReAnalyzeRequest) {
+    return server.makeRequest<protocol.ReAnalyzeReponse>(protocol.Requests.ReAnalyze, request);
 }
 
 export async function getTestStartInfo(server: OmniSharpServer, request: protocol.V2.GetTestStartInfoRequest) {
diff --git a/src/statusBarItemAdapter.ts b/src/statusBarItemAdapter.ts
index e1b078ca..dac617e7 100644
--- a/src/statusBarItemAdapter.ts
+++ b/src/statusBarItemAdapter.ts
@@ -32,11 +32,11 @@ export class StatusBarItemAdapter implements vscodeAdapter.StatusBarItem {
         this.statusBarItem.tooltip = value;
     }
 
-    get color(): string {
-        return this.statusBarItem.color as string;
+    get color(): string | vscode.ThemeColor | undefined {
+        return this.statusBarItem.color;
     }
 
-    set color(value: string) {
+    set color(value: string | vscode.ThemeColor | undefined) {
         this.statusBarItem.color = value;
     }
 
diff --git a/src/vscodeAdapter.ts b/src/vscodeAdapter.ts
index 1cb0940e..3217a9cf 100644
--- a/src/vscodeAdapter.ts
+++ b/src/vscodeAdapter.ts
@@ -223,6 +223,18 @@ export interface Command {
     arguments?: any[];
 }
 
+/**
+ * A reference to one of the workbench colors as defined in https://code.visualstudio.com/docs/getstarted/theme-color-reference.
+ * Using a theme color is preferred over a custom color as it gives theme authors and users the possibility to change the color.
+ */
+declare class ThemeColor {
+    /**
+     * Creates a reference to a theme color.
+     * @param id of the color. The available colors are listed in https://code.visualstudio.com/docs/getstarted/theme-color-reference.
+     */
+    constructor(id: string);
+}
+
 export interface StatusBarItem {
 
     /**
@@ -254,7 +266,7 @@ export interface StatusBarItem {
     /**
      * The foreground color for this entry.
      */
-    color: string | undefined;
+    color: string | ThemeColor | undefined;
 
     /**
      * The identifier of a command to run on click. The command must be
@@ -970,7 +982,6 @@ export interface vscode {
         onDidChangeConfiguration: Event<ConfigurationChangeEvent>;
     };
     extensions: {
-        getExtension(extensionId: string): Extension<any> | undefined;
         all: ReadonlyArray<Extension<any>>;
     };
     Uri: {


Test patch:
diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts
index c7b8a828..cb8c35d7 100644
--- a/src/features/dotnetTest.ts
+++ b/src/features/dotnetTest.ts
@@ -25,9 +25,9 @@ const TelemetryReportingDelay = 2 * 60 * 1000; // two minutes
 
 export default class TestManager extends AbstractProvider {
 
-    private _runCounts: { [testFrameworkName: string]: number };
-    private _debugCounts: { [testFrameworkName: string]: number };
-    private _telemetryIntervalId: NodeJS.Timer = undefined;
+    private _runCounts?: { [testFrameworkName: string]: number };
+    private _debugCounts?: { [testFrameworkName: string]: number };
+    private _telemetryIntervalId?: NodeJS.Timer = undefined;
     private _eventStream: EventStream;
 
     constructor(private optionProvider: OptionProvider, server: OmniSharpServer, eventStream: EventStream, languageMiddlewareFeature: LanguageMiddlewareFeature) {
@@ -127,7 +127,7 @@ export default class TestManager extends AbstractProvider {
             vscode.workspace.saveAll(/*includeUntitled*/ false));
     }
 
-    private async _runTest(fileName: string, testMethod: string, runSettings: string, testFrameworkName: string, targetFrameworkVersion: string, noBuild: boolean): Promise<protocol.V2.DotNetTestResult[]> {
+    private async _runTest(fileName: string, testMethod: string, runSettings: string | undefined, testFrameworkName: string, targetFrameworkVersion: string | undefined, noBuild: boolean): Promise<protocol.V2.DotNetTestResult[]> {
         const request: protocol.V2.RunTestRequest = {
             FileName: fileName,
             MethodName: testMethod,
@@ -137,16 +137,11 @@ export default class TestManager extends AbstractProvider {
             NoBuild: noBuild
         };
 
-        try {
-            let response = await serverUtils.runTest(this._server, request);
-            return response.Results;
-        }
-        catch (error) {
-            return undefined;
-        }
+        let response = await serverUtils.runTest(this._server, request);
+        return response.Results;
     }
 
-    private async _recordRunAndGetFrameworkVersion(fileName: string, testFrameworkName?: string): Promise<string> {
+    private async _recordRunAndGetFrameworkVersion(fileName: string, testFrameworkName?: string): Promise<string | undefined> {
 
         await this._saveDirtyFiles();
         this._recordRunRequest(testFrameworkName);
@@ -170,7 +165,7 @@ export default class TestManager extends AbstractProvider {
         return targetFrameworkVersion;
     }
 
-    public async discoverTests(fileName: string, testFrameworkName: string, noBuild: boolean): Promise<protocol.V2.TestInfo[]> {
+    public async discoverTests(fileName: string, testFrameworkName: string, noBuild: boolean): Promise<protocol.V2.TestInfo[] | undefined> {
 
         let targetFrameworkVersion = await this._recordRunAndGetFrameworkVersion(fileName, testFrameworkName);
         let runSettings = this._getRunSettings(fileName);
@@ -187,9 +182,9 @@ export default class TestManager extends AbstractProvider {
             let response = await serverUtils.discoverTests(this._server, request);
             return response.Tests;
         }
-        catch (error) {
-            return undefined;
-        }
+        catch {}
+
+        return undefined;
     }
 
     private _getRunSettings(filename: string): string | undefined {
@@ -205,6 +200,9 @@ export default class TestManager extends AbstractProvider {
         // Path is relative to the workspace. Create absolute path.
         const fileUri = vscode.Uri.file(filename);
         const workspaceFolder = vscode.workspace.getWorkspaceFolder(fileUri);
+        if (workspaceFolder === undefined) {
+            return undefined;
+        }
 
         return path.join(workspaceFolder.uri.fsPath, testSettingsPath);
     }
@@ -224,8 +222,9 @@ export default class TestManager extends AbstractProvider {
             let results = await this._runTest(fileName, testMethod, runSettings, testFrameworkName, targetFrameworkVersion, noBuild);
             this._eventStream.post(new ReportDotNetTestResults(results));
         }
-        catch (reason) {
-            this._eventStream.post(new DotNetTestRunFailure(reason));
+        catch (error) {
+            const message = (error as Error).message;
+            this._eventStream.post(new DotNetTestRunFailure(message));
         }
         finally {
             listener.dispose();
@@ -248,15 +247,16 @@ export default class TestManager extends AbstractProvider {
             let results = await this._runTestsInClass(fileName, runSettings, testFrameworkName, targetFrameworkVersion, methodsInClass, noBuild);
             this._eventStream.post(new ReportDotNetTestResults(results));
         }
-        catch (reason) {
-            this._eventStream.post(new DotNetTestRunFailure(reason));
+        catch (error) {
+            const message = (error as Error).message;
+            this._eventStream.post(new DotNetTestRunFailure(message));
         }
         finally {
             listener.dispose();
         }
     }
 
-    private async _runTestsInClass(fileName: string, runSettings: string, testFrameworkName: string, targetFrameworkVersion: string, methodsToRun: string[], noBuild: boolean): Promise<protocol.V2.DotNetTestResult[]> {
+    private async _runTestsInClass(fileName: string, runSettings: string | undefined, testFrameworkName: string, targetFrameworkVersion: string | undefined, methodsToRun: string[], noBuild: boolean): Promise<protocol.V2.DotNetTestResult[]> {
         const request: protocol.V2.RunTestsInClassRequest = {
             FileName: fileName,
             RunSettings: runSettings,
@@ -305,8 +305,9 @@ export default class TestManager extends AbstractProvider {
                 this._eventStream.post(new ReportDotNetTestResults(response.Results));
             }
         }
-        catch (reason) {
-            this._eventStream.post(new DotNetTestRunFailure(reason));
+        catch (error) {
+            const message = (error as Error).message;
+            this._eventStream.post(new DotNetTestRunFailure(message));
         }
         finally {
             listener.dispose();
@@ -344,11 +345,11 @@ export default class TestManager extends AbstractProvider {
     private async _getLaunchConfigurationForVSTest(
         fileName: string,
         testMethod: string,
-        runSettings: string,
+        runSettings: string | undefined,
         testFrameworkName: string,
-        targetFrameworkVersion: string,
+        targetFrameworkVersion: string | undefined,
         debugEventListener: DebugEventListener,
-        noBuild: boolean): Promise<LaunchConfiguration> {
+        noBuild: boolean | undefined): Promise<LaunchConfiguration> {
 
         // Listen for test messages while getting start info.
         const listener = this._server.onTestMessage(e => {
@@ -378,7 +379,7 @@ export default class TestManager extends AbstractProvider {
         }
     }
 
-    private async _recordDebugAndGetDebugValues(fileName: string, testFrameworkName?: string) {
+    private async _recordDebugAndGetDebugValues(fileName: string, testFrameworkName?: string): Promise<{ debugEventListener: DebugEventListener, targetFrameworkVersion: string }> {
         await this._saveDirtyFiles();
         this._recordDebugRequest(testFrameworkName);
         let projectInfo: protocol.ProjectInformationResponse;
@@ -386,21 +387,16 @@ export default class TestManager extends AbstractProvider {
             projectInfo = await serverUtils.requestProjectInformation(this._server, { FileName: fileName });
         }
         catch (error) {
-            return undefined;
+            throw new Error('Could not determine project type.');
         }
 
-        let debugEventListener: DebugEventListener = null;
-        let targetFrameworkVersion: string;
-
-        if (projectInfo.MsBuildProject) {
-            targetFrameworkVersion = projectInfo.MsBuildProject.TargetFramework;
-            debugEventListener = new DebugEventListener(fileName, this._server, this._eventStream);
-            debugEventListener.start();
-        }
-        else {
+        if (!projectInfo.MsBuildProject) {
             throw new Error('Expected .csproj project.');
         }
 
+        const targetFrameworkVersion = projectInfo.MsBuildProject.TargetFramework;
+        const debugEventListener = new DebugEventListener(fileName, this._server, this._eventStream);
+        await debugEventListener.start();
         return { debugEventListener, targetFrameworkVersion };
     }
 
@@ -418,11 +414,10 @@ export default class TestManager extends AbstractProvider {
             const workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(fileName));
             return vscode.debug.startDebugging(workspaceFolder, config);
         }
-        catch (reason) {
-            this._eventStream.post(new DotNetTestDebugStartFailure(reason));
-            if (debugEventListener !== null) {
-                debugEventListener.close();
-            }
+        catch (error) {
+            const message = (error as Error).message;
+            this._eventStream.post(new DotNetTestDebugStartFailure(message));
+            debugEventListener.close();
         }
     }
 
@@ -438,11 +433,10 @@ export default class TestManager extends AbstractProvider {
             const workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(fileName));
             return vscode.debug.startDebugging(workspaceFolder, config);
         }
-        catch (reason) {
-            this._eventStream.post(new DotNetTestDebugStartFailure(reason));
-            if (debugEventListener != null) {
-                debugEventListener.close();
-            }
+        catch (error) {
+            const message = (error as Error).message;
+            this._eventStream.post(new DotNetTestDebugStartFailure(message));
+            debugEventListener.close();
         }
     }
 
@@ -467,22 +461,21 @@ export default class TestManager extends AbstractProvider {
             const workspaceFolder = vscode.workspace.getWorkspaceFolder(fileUri);
             return vscode.debug.startDebugging(workspaceFolder, config);
         }
-        catch (reason) {
-            this._eventStream.post(new DotNetTestRunFailure(reason));
-            if (debugEventListener !== null) {
-                debugEventListener.close();
-            }
+        catch (error) {
+            const message = (error as Error).message;
+            this._eventStream.post(new DotNetTestRunFailure(message));
+            debugEventListener.close();
         }
     }
 
     private async _getLaunchConfigurationForVSTestClass(
         fileName: string,
         methodsToRun: string[],
-        runSettings: string,
+        runSettings: string | undefined,
         testFrameworkName: string,
-        targetFrameworkVersion: string,
+        targetFrameworkVersion: string | undefined,
         debugEventListener: DebugEventListener,
-        noBuild: boolean): Promise<LaunchConfiguration> {
+        noBuild: boolean | undefined): Promise<LaunchConfiguration> {
 
         const listener = this._server.onTestMessage(e => {
             this._eventStream.post(new DotNetTestMessage(e.Message));
@@ -510,8 +503,8 @@ export default class TestManager extends AbstractProvider {
         fileName: string,
         line: number,
         column: number,
-        runSettings: string,
-        targetFrameworkVersion: string,
+        runSettings: string | undefined,
+        targetFrameworkVersion: string | undefined,
         debugEventListener: DebugEventListener): Promise<LaunchConfiguration | null> {
 
         const listener = this._server.onTestMessage(e => {
@@ -529,11 +522,12 @@ export default class TestManager extends AbstractProvider {
         try {
             let response = await serverUtils.debugTestsInContextGetStartInfo(this._server, request);
             if (!response.Succeeded) {
+                // FailureReason is populated if Succeeded is false
                 if (response.ContextHadNoTests) {
-                    this._eventStream.post(new DotNetTestMessage(response.FailureReason));
+                    this._eventStream.post(new DotNetTestMessage(response.FailureReason!));
                 }
                 else {
-                    this._eventStream.post(new DotNetTestRunFailure(response.FailureReason));
+                    this._eventStream.post(new DotNetTestRunFailure(response.FailureReason!));
                 }
 
                 return null;
@@ -548,13 +542,13 @@ export default class TestManager extends AbstractProvider {
 }
 
 class DebugEventListener {
-    static s_activeInstance: DebugEventListener = null;
+    static s_activeInstance: DebugEventListener | undefined;
     _fileName: string;
     _server: OmniSharpServer;
     _pipePath: string;
     _eventStream: EventStream;
 
-    _serverSocket: net.Server;
+    _serverSocket: net.Server | undefined;
     _isClosed: boolean = false;
 
     constructor(fileName: string, server: OmniSharpServer, eventStream: EventStream) {
@@ -574,14 +568,14 @@ class DebugEventListener {
     public async start(): Promise<void> {
 
         // We use our process id as part of the pipe name, so if we still somehow have an old instance running, close it.
-        if (DebugEventListener.s_activeInstance !== null) {
+        if (DebugEventListener.s_activeInstance !== undefined) {
             DebugEventListener.s_activeInstance.close();
         }
 
         DebugEventListener.s_activeInstance = this;
 
-        this._serverSocket = net.createServer((socket: net.Socket) => {
-            socket.on('data', (buffer: Buffer) => {
+        const serverSocket = net.createServer(socket => {
+            socket.on('data', buffer => {
                 let event: DebuggerEventsProtocol.DebuggerEvent;
                 try {
                     event = DebuggerEventsProtocol.decodePacket(buffer);
@@ -610,19 +604,21 @@ class DebugEventListener {
             });
         });
 
+        // We need the local serverSocket variable for the promise to capture below,
+        // or else TypeScript will warn that the instance variable could be undefined.
+        this._serverSocket = serverSocket;
+
         await this.removeSocketFileIfExists();
-        return new Promise<void>((resolve, reject) => {
-            let isStarted: boolean = false;
-            this._serverSocket.on('error', (err: Error) => {
-                if (!isStarted) {
+        return new Promise((resolve, reject) => {
+            serverSocket.on('error', err => {
+                if (!serverSocket.listening) {
                     reject(err.message);
                 } else {
                     this._eventStream.post(new DotNetTestDebugWarning(`Communications error on debugger event channel. ${err.message}`));
                 }
             });
 
-            this._serverSocket.listen(this._pipePath, () => {
-                isStarted = true;
+            serverSocket.listen(this._pipePath, () => {
                 resolve();
             });
         });
@@ -634,7 +630,7 @@ class DebugEventListener {
 
     public close() {
         if (this === DebugEventListener.s_activeInstance) {
-            DebugEventListener.s_activeInstance = null;
+            DebugEventListener.s_activeInstance = undefined;
         }
 
         if (this._isClosed) {
@@ -643,7 +639,7 @@ class DebugEventListener {
 
         this._isClosed = true;
 
-        if (this._serverSocket !== null) {
+        if (this._serverSocket !== undefined) {
             this._serverSocket.close();
         }
     }
diff --git a/test/unitTests/features/reportIssue.test.ts b/test/unitTests/features/reportIssue.test.ts
index 9d4dcb4b..ae4494e3 100644
--- a/test/unitTests/features/reportIssue.test.ts
+++ b/test/unitTests/features/reportIssue.test.ts
@@ -50,18 +50,10 @@ suite(`${reportIssue.name}`, () => {
 
     setup(() => {
         vscode = getFakeVsCode();
-        vscode.extensions.getExtension = () => {
-            return {
-                packageJSON: {
-                    version: csharpExtVersion
-                },
-                id: ""
-            };
-        };
 
         vscode.env.clipboard.writeText = (body: string) => {
             issueBody = body;
-            return undefined;
+            return Promise.resolve();
         };
 
         vscode.version = vscodeVersion;
@@ -73,52 +65,52 @@ suite(`${reportIssue.name}`, () => {
     });
 
     test(`${OpenURL.name} event is created`, async () => {
-        await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
+        await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, isValidForMono, options, fakeDotnetResolver, fakeMonoResolver);
         let events = eventBus.getEvents();
         expect(events).to.have.length(1);
         expect(events[0].constructor.name).to.be.equal(`${OpenURL.name}`);
     });
 
     test(`${OpenURL.name} event is created with the omnisharp-vscode github repo issues url`, async () => {
-        await reportIssue(vscode, eventStream, getDotnetInfo, false, options, fakeMonoResolver, fakeDotnetResolver);
+        await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, false, options, fakeDotnetResolver, fakeMonoResolver);
         let url = (<OpenURL>eventBus.getEvents()[0]).url;
         expect(url).to.include("https://github.com/OmniSharp/omnisharp-vscode/issues/new?body=Please paste the output from your clipboard");
     });
 
     suite("The body is passed to the vscode clipboard and", () => {
         test("it contains the vscode version", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
+            await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, isValidForMono, options, fakeDotnetResolver, fakeMonoResolver);
             expect(issueBody).to.include(`**VSCode version**: ${vscodeVersion}`);
         });
 
         test("it contains the csharp extension version", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
+            await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, isValidForMono, options, fakeDotnetResolver, fakeMonoResolver);
             expect(issueBody).to.include(`**C# Extension**: ${csharpExtVersion}`);
         });
 
         test("it contains dotnet info", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
+            await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, isValidForMono, options, fakeDotnetResolver, fakeMonoResolver);
             expect(issueBody).to.contain(fakeDotnetInfo.FullInfo);
         });
 
         test("mono information is obtained when it is a valid mono platform", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
+            await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, isValidForMono, options, fakeDotnetResolver, fakeMonoResolver);
             expect(fakeMonoResolver.getMonoCalled).to.be.equal(true);
         });
 
         test("mono version is put in the body when it is a valid mono platform", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
+            await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, isValidForMono, options, fakeDotnetResolver, fakeMonoResolver);
             expect(fakeMonoResolver.getMonoCalled).to.be.equal(true);
             expect(issueBody).to.contain(fakeMonoInfo.version);
         });
 
         test("mono information is not obtained when it is not a valid mono platform", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, false, options, fakeMonoResolver, fakeDotnetResolver);
+            await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, false, options, fakeDotnetResolver, fakeMonoResolver);
             expect(fakeMonoResolver.getMonoCalled).to.be.equal(false);
         });
 
         test("The url contains the name, publisher and version for all the extensions that are not builtin", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
+            await reportIssue(vscode, csharpExtVersion, eventStream, getDotnetInfo, isValidForMono, options, fakeDotnetResolver, fakeMonoResolver);
             expect(issueBody).to.contain(extension2.packageJSON.name);
             expect(issueBody).to.contain(extension2.packageJSON.publisher);
             expect(issueBody).to.contain(extension2.packageJSON.version);
@@ -127,4 +119,4 @@ suite(`${reportIssue.name}`, () => {
             expect(issueBody).to.not.contain(extension1.packageJSON.version);
         });
     });
-});
\ No newline at end of file
+});
diff --git a/test/unitTests/testAssets/Fakes.ts b/test/unitTests/testAssets/Fakes.ts
index 3a8973c2..2ead30aa 100644
--- a/test/unitTests/testAssets/Fakes.ts
+++ b/test/unitTests/testAssets/Fakes.ts
@@ -134,9 +134,6 @@ export function getFakeVsCode(): vscode.vscode {
             }
         },
         extensions: {
-            getExtension: () => {
-                throw new Error("Not Implemented");
-            },
             all: []
         },
         Uri: {