Repository: rokucommunity/brighterscript
Pull request #1248 (2024-07-12 14:57:17)

Remove `file` prop from `BsDiagnostic`
All diagnostics internal to Brighterscript now use a `location: Location` prop instead of `file: BscFile`

This has a few good points:
 - Diagnostics no longer have a reference to a file, so no circular references to remove
 - Aligns better with the change to use `Location` for Tokens and AstNodes

One thing to note is that sometimes we need to back-convert a URI to a path for finding the files that a diagnostic applies to. 
There might be some inconsistencies between Operating Systems (Mac vs. Windows, etc) that *should* be dealt with the `URI` namespace from `vscode-uri`, but I'm not entirely sure.


Addresses: #1155


markwpearce: @TwitchBronBron This fails in windows (I *think* due to somewhere the URI or `srcPath` has an uppercase drive letter?) ..
when you get a chance, can you look into that?

Patch:
diff --git a/src/CodeActionUtil.ts b/src/CodeActionUtil.ts
index cb8257af..72903fd3 100644
--- a/src/CodeActionUtil.ts
+++ b/src/CodeActionUtil.ts
@@ -1,6 +1,7 @@
 import type { Diagnostic, Position, Range, WorkspaceEdit } from 'vscode-languageserver';
 import { CodeActionKind, CodeAction, TextEdit } from 'vscode-languageserver';
-import { URI } from 'vscode-uri';
+import type { BsDiagnostic } from './interfaces';
+import util from './util';
 
 export class CodeActionUtil {
 
@@ -9,7 +10,7 @@ export class CodeActionUtil {
             changes: {}
         } as WorkspaceEdit;
         for (const change of obj.changes) {
-            const uri = URI.file(change.filePath).toString();
+            const uri = util.pathToUri(change.filePath);
 
             //create the edit changes array for this uri
             if (!edit.changes![uri]) {
@@ -31,9 +32,9 @@ export class CodeActionUtil {
         return action;
     }
 
-    public serializableDiagnostics(diagnostics: Diagnostic[] | undefined) {
-        return diagnostics?.map(({ range, severity, code, source, message, relatedInformation }) => ({
-            range: range,
+    public serializableDiagnostics(diagnostics: BsDiagnostic[] | undefined): Diagnostic[] {
+        return diagnostics?.map(({ location, severity, code, source, message, relatedInformation }) => ({
+            range: location?.range,
             severity: severity,
             source: source,
             code: code,
@@ -47,7 +48,7 @@ export { CodeActionKind };
 
 export interface CodeActionShorthand {
     title: string;
-    diagnostics?: Diagnostic[];
+    diagnostics?: BsDiagnostic[];
     kind?: CodeActionKind;
     isPreferred?: boolean;
     changes: Array<InsertChange | ReplaceChange>;
diff --git a/src/CommentFlagProcessor.ts b/src/CommentFlagProcessor.ts
index bef1dfa7..1344a83e 100644
--- a/src/CommentFlagProcessor.ts
+++ b/src/CommentFlagProcessor.ts
@@ -86,8 +86,7 @@ export class CommentFlagProcessor {
                 } else {
                     this.diagnostics.push({
                         ...DiagnosticMessages.unknownDiagnosticCode(codeInt),
-                        file: this.file,
-                        range: codeToken.range
+                        location: util.createLocationFromFileRange(this.file, codeToken.range)
                     });
                 }
             }
diff --git a/src/CrossScopeValidator.spec.ts b/src/CrossScopeValidator.spec.ts
index fa9b90f8..d9d548fc 100644
--- a/src/CrossScopeValidator.spec.ts
+++ b/src/CrossScopeValidator.spec.ts
@@ -6,6 +6,7 @@ import type { BrsFile } from './files/BrsFile';
 import { trim, expectZeroDiagnostics, expectDiagnostics, expectDiagnosticsIncludes } from './testHelpers.spec';
 import { expect } from 'chai';
 import { SymbolTypeFlag } from './SymbolTypeFlag';
+import util from './util';
 
 describe('CrossScopeValidator', () => {
     let sinon = sinonImport.createSandbox();
@@ -1153,15 +1154,11 @@ describe('CrossScopeValidator', () => {
             expectDiagnostics(program, [
                 {
                     message: DiagnosticMessages.cannotFindName('fontName', 'TextOptions.fontName', 'TextOptions').message,
-                    file: {
-                        srcPath: button1.srcPath
-                    }
+                    location: { uri: util.pathToUri(button1.srcPath) }
                 },
                 {
                     message: DiagnosticMessages.cannotFindName('fontName', 'TextOptions.fontName', 'TextOptions').message,
-                    file: {
-                        srcPath: button2.srcPath
-                    }
+                    location: { uri: util.pathToUri(button2.srcPath) }
                 }
             ]);
         });
@@ -1221,15 +1218,11 @@ describe('CrossScopeValidator', () => {
             expectDiagnostics(program, [
                 {
                     message: DiagnosticMessages.mismatchArgumentCount(1, 0).message,
-                    file: {
-                        srcPath: button1.srcPath
-                    }
+                    location: { uri: util.pathToUri(button1.srcPath) }
                 },
                 {
                     message: DiagnosticMessages.mismatchArgumentCount(1, 0).message,
-                    file: {
-                        srcPath: button2.srcPath
-                    }
+                    location: { uri: util.pathToUri(button2.srcPath) }
                 }
             ]);
         });
@@ -1397,15 +1390,11 @@ describe('CrossScopeValidator', () => {
             expectDiagnostics(program, [
                 {
                     message: DiagnosticMessages.cannotFindName('getValue', 'alpha.beta.getValue', 'alpha.beta', 'namespace').message,
-                    file: {
-                        srcPath: button1.srcPath
-                    }
+                    location: { uri: util.pathToUri(button1.srcPath) }
                 },
                 {
                     message: DiagnosticMessages.cannotFindName('getValue').message,
-                    file: {
-                        srcPath: button2.srcPath
-                    }
+                    location: { uri: util.pathToUri(button2.srcPath) }
                 }
             ]);
         });
@@ -1451,9 +1440,7 @@ describe('CrossScopeValidator', () => {
                 expectDiagnostics(program, [
                     {
                         message: DiagnosticMessages.cannotFindFunction('someFunc', 'alpha.beta.someFunc', 'alpha.beta', 'namespace').message,
-                        file: {
-                            srcPath: widgetFile.srcPath
-                        }
+                        location: { uri: util.pathToUri(widgetFile.srcPath) }
                     }
                 ]);
 
diff --git a/src/CrossScopeValidator.ts b/src/CrossScopeValidator.ts
index 0d5314a0..89c3fa23 100644
--- a/src/CrossScopeValidator.ts
+++ b/src/CrossScopeValidator.ts
@@ -16,7 +16,6 @@ import type { BscType } from './types/BscType';
 import type { BscFile } from './files/BscFile';
 import type { ClassStatement, ConstStatement, EnumMemberStatement, EnumStatement, InterfaceStatement, NamespaceStatement } from './parser/Statement';
 import { ParseMode } from './parser/Parser';
-import { URI } from 'vscode-uri';
 import { globalFile } from './globalCallables';
 import type { DottedGetExpression, VariableExpression } from './parser/Expression';
 import type { InheritableType } from './types';
@@ -508,7 +507,7 @@ export class CrossScopeValidator {
 
     addDiagnosticsForScopes(scopes: Scope[]) { //, changedFiles: BrsFile[]) {
         const addDuplicateSymbolDiagnostics = true;
-        const missingSymbolInScope = new Map<BrsFile, Map<UnresolvedSymbol, Set<Scope>>>();
+        const missingSymbolInScope = new Map<UnresolvedSymbol, Set<Scope>>();
         this.providedTreeMap.clear();
         this.clearResolutionsForScopes(scopes);
 
@@ -582,15 +581,11 @@ export class CrossScopeValidator {
 
                                 const relatedInformation = thatNameRange ? [{
                                     message: `${thatNodeKindName} declared here`,
-                                    location: util.createLocationFromRange(
-                                        URI.file(otherDupe.file?.srcPath).toString(),
-                                        thatNameRange
-                                    )
+                                    location: util.createLocationFromFileRange(otherDupe.file, thatNameRange)
                                 }] : undefined;
                                 this.program.diagnostics.register({
                                     ...DiagnosticMessages.nameCollision(thisNodeKindName, thatNodeKindName, thatName),
-                                    file: dupe.file,
-                                    range: thisNameRange,
+                                    location: util.createLocationFromFileRange(dupe.file, thisNameRange),
                                     relatedInformation: relatedInformation
                                 }, {
                                     scope: scope,
@@ -603,36 +598,28 @@ export class CrossScopeValidator {
             }
             // build map of the symbols and scopes where the symbols are missing per file
             for (const missingSymbol of missingSymbols) {
-                let missingSymbolPerFile = missingSymbolInScope.get(missingSymbol.file);
-                if (!missingSymbolPerFile) {
-                    missingSymbolPerFile = new Map<UnresolvedSymbol, Set<Scope>>();
-                    missingSymbolInScope.set(missingSymbol.file, missingSymbolPerFile);
-                }
-                let scopesWithMissingSymbol = missingSymbolPerFile.get(missingSymbol);
+
+                let scopesWithMissingSymbol = missingSymbolInScope.get(missingSymbol);
                 if (!scopesWithMissingSymbol) {
                     scopesWithMissingSymbol = new Set<Scope>();
-                    missingSymbolPerFile.set(missingSymbol, scopesWithMissingSymbol);
+                    missingSymbolInScope.set(missingSymbol, scopesWithMissingSymbol);
                 }
                 scopesWithMissingSymbol.add(scope);
             }
         }
 
-        // Check each file for missing symbols - if they are missing in SOME scopes, add diagnostic
-        for (let [file, missingSymbolPerFile] of missingSymbolInScope.entries()) {
-            for (const [symbol, scopeList] of missingSymbolPerFile) {
-                const typeChainResult = util.processTypeChain(symbol.typeChain);
-
-                for (const scope of scopeList) {
-                    this.program.diagnostics.register({
+        // If symbols are missing in SOME scopes, add diagnostic
+        for (const [symbol, scopeList] of missingSymbolInScope.entries()) {
+            const typeChainResult = util.processTypeChain(symbol.typeChain);
 
-                        ...this.getCannotFindDiagnostic(scope, typeChainResult),
-                        file: file,
-                        range: typeChainResult.location?.range
-                    }, {
-                        scope: scope,
-                        tags: [CrossScopeValidatorDiagnosticTag]
-                    });
-                }
+            for (const scope of scopeList) {
+                this.program.diagnostics.register({
+                    ...this.getCannotFindDiagnostic(scope, typeChainResult),
+                    location: typeChainResult.location
+                }, {
+                    scope: scope,
+                    tags: [CrossScopeValidatorDiagnosticTag]
+                });
             }
         }
 
@@ -644,8 +631,7 @@ export class CrossScopeValidator {
                 const scopeListName = [...incompatibleScopes.values()].map(s => s.name).join(', ');
                 this.program.diagnostics.register({
                     ...DiagnosticMessages.incompatibleSymbolDefinition(typeChainResult.fullChainName, scopeListName),
-                    file: symbol.file,
-                    range: typeChainResult.location?.range
+                    location: typeChainResult.location
                 }, {
                     tags: [CrossScopeValidatorDiagnosticTag]
                 });
diff --git a/src/DiagnosticCollection.spec.ts b/src/DiagnosticCollection.spec.ts
index 9047c763..046571c6 100644
--- a/src/DiagnosticCollection.spec.ts
+++ b/src/DiagnosticCollection.spec.ts
@@ -2,7 +2,6 @@ import type { BsDiagnostic } from './interfaces';
 import { DiagnosticCollection } from './DiagnosticCollection';
 import type { Project } from './LanguageServer';
 import type { ProgramBuilder } from './ProgramBuilder';
-import type { BscFile } from './files/BscFile';
 import util from './util';
 import { expect } from './chai-config.spec';
 
@@ -26,11 +25,16 @@ describe('DiagnosticCollection', () => {
         const patch = collection.getPatch(projects);
         //convert the patch into our test structure
         const actual = {};
-        for (const filePath in patch) {
-            actual[filePath] = patch[filePath].map(x => x.message);
+        for (const fileUri in patch) {
+            actual[fileUri] = patch[fileUri].map(x => x.message);
+        }
+        const expectedWithUris = {};
+        for (const fileSrc in expected) {
+            const fileUri = util.pathToUri(fileSrc);
+            expectedWithUris[fileUri] = expected[fileSrc];
         }
 
-        expect(actual).to.eql(expected);
+        expect(actual).to.eql(expectedWithUris);
     }
 
     it('does not crash for diagnostics with missing locations', () => {
@@ -93,9 +97,10 @@ describe('DiagnosticCollection', () => {
     });
 
     function removeDiagnostic(srcPath: string, message: string) {
+        const fileUri = util.pathToUri(srcPath);
         for (let i = 0; i < diagnostics.length; i++) {
             const diagnostic = diagnostics[i];
-            if (diagnostic.file.srcPath === srcPath && diagnostic.message === message) {
+            if (diagnostic.location.uri === fileUri && diagnostic.message === message) {
                 diagnostics.splice(i, 1);
                 return;
             }
@@ -105,12 +110,10 @@ describe('DiagnosticCollection', () => {
 
     function addDiagnostics(srcPath: string, messages: string[]) {
         const newDiagnostics: BsDiagnostic[] = [];
+        const fileUri = util.pathToUri(srcPath);
         for (const message of messages) {
             newDiagnostics.push({
-                file: {
-                    srcPath: srcPath
-                } as BscFile,
-                range: util.createRange(0, 0, 0, 0),
+                location: { uri: fileUri, range: util.createRange(0, 0, 0, 0) },
                 //the code doesn't matter as long as the messages are different, so just enforce unique messages for this test files
                 code: 123,
                 message: message
diff --git a/src/DiagnosticCollection.ts b/src/DiagnosticCollection.ts
index 98d06702..e354554a 100644
--- a/src/DiagnosticCollection.ts
+++ b/src/DiagnosticCollection.ts
@@ -30,18 +30,18 @@ export class DiagnosticCollection {
         const keys = {};
         //build the full current set of diagnostics by file
         for (let diagnostic of diagnostics) {
-            const srcPath = diagnostic.file.srcPath ?? diagnostic.file.srcPath;
+            const fileUri = diagnostic.location.uri;
             //ensure the file entry exists
-            if (!result[srcPath]) {
-                result[srcPath] = [];
+            if (!result[fileUri]) {
+                result[fileUri] = [];
             }
-            const diagnosticMap = result[srcPath];
+            const diagnosticMap = result[fileUri];
 
             //fall back to a default range if missing
-            const range = diagnostic?.range ?? util.createRange(0, 0, 0, 0);
+            const range = diagnostic?.location?.range ?? util.createRange(0, 0, 0, 0);
 
             diagnostic.key =
-                srcPath.toLowerCase() + '-' +
+                fileUri.toLowerCase() + '-' +
                 diagnostic.code + '-' +
                 range.start.line + '-' +
                 range.start.character + '-' +
diff --git a/src/DiagnosticFilterer.spec.ts b/src/DiagnosticFilterer.spec.ts
index 7b50657a..f76bd1a4 100644
--- a/src/DiagnosticFilterer.spec.ts
+++ b/src/DiagnosticFilterer.spec.ts
@@ -1,7 +1,7 @@
 import { expect } from './chai-config.spec';
 import { DiagnosticFilterer } from './DiagnosticFilterer';
 import type { BsDiagnostic } from './interfaces';
-import { standardizePath as s } from './util';
+import util, { standardizePath as s } from './util';
 import { createSandbox } from 'sinon';
 const sinon = createSandbox();
 let rootDir = s`${process.cwd()}/rootDir`;
@@ -260,9 +260,9 @@ describe('DiagnosticFilterer', () => {
             getDiagnostic(4, s`${rootDir}/source/Common2.brs`)
         ]);
         expect(stub.callCount).to.eql(2);
-        expect(stub.getCalls().map(x => x.args[1])).to.eql([
-            s`${rootDir.toLowerCase()}/source/common1.brs`,
-            s`${rootDir.toLowerCase()}/source/common2.brs`
+        expect(stub.getCalls().map(x => x.args[1].toLowerCase())).to.eql([
+            util.pathToUri(s`${rootDir.toLowerCase()}/source/common1.brs`).toLowerCase(),
+            util.pathToUri(s`${rootDir.toLowerCase()}/source/common2.brs`).toLowerCase()
         ]);
     });
 
@@ -270,8 +270,8 @@ describe('DiagnosticFilterer', () => {
 
 function getDiagnostic(code: number | string, srcPath: string) {
     return {
-        file: {
-            srcPath: s`${srcPath}`
+        location: {
+            uri: util.pathToUri(s`${srcPath}`)
         },
         code: code
     } as BsDiagnostic;
diff --git a/src/DiagnosticFilterer.ts b/src/DiagnosticFilterer.ts
index a2f72638..db84fce5 100644
--- a/src/DiagnosticFilterer.ts
+++ b/src/DiagnosticFilterer.ts
@@ -2,7 +2,8 @@ import type { BsDiagnostic } from './interfaces';
 import * as path from 'path';
 import * as minimatch from 'minimatch';
 import type { BsConfig } from './BsConfig';
-import { standardizePath as s } from './util';
+import util, { standardizePath as s } from './util';
+import { URI } from 'vscode-uri';
 
 interface DiagnosticWithSuppression {
     diagnostic: BsDiagnostic;
@@ -73,17 +74,17 @@ export class DiagnosticFilterer {
         this.byFile = {};
 
         for (let diagnostic of diagnostics) {
-            const srcPath = diagnostic?.file?.srcPath ?? diagnostic?.file?.srcPath;
+            const fileUri = diagnostic?.location?.uri ?? 'invalid-uri';
             //skip diagnostics that have issues
-            if (!srcPath) {
+            if (!fileUri) {
                 continue;
             }
-            const lowerSrcPath = srcPath.toLowerCase();
+            const lowerFileUri = fileUri.toLowerCase();
             //make a new array for this file if one does not yet exist
-            if (!this.byFile[lowerSrcPath]) {
-                this.byFile[lowerSrcPath] = [];
+            if (!this.byFile[lowerFileUri]) {
+                this.byFile[lowerFileUri] = [];
             }
-            this.byFile[lowerSrcPath].push({
+            this.byFile[lowerFileUri].push({
                 diagnostic: diagnostic,
                 isSuppressed: false
             });
@@ -91,7 +92,7 @@ export class DiagnosticFilterer {
     }
 
     private filterAllFiles(filter: NormalizedFilter) {
-        let matchedFilePaths: string[];
+        let matchedFileUris: string[];
 
         //if there's a src, match against all files
         if (filter.src) {
@@ -100,35 +101,36 @@ export class DiagnosticFilterer {
                 path.isAbsolute(filter.src) ? filter.src : `${this.rootDir}/${filter.src}`
             );
 
-            matchedFilePaths = minimatch.match(Object.keys(this.byFile), src, {
+            const byFileSrcs = Object.keys(this.byFile).map(uri => URI.parse(uri).fsPath);
+            matchedFileUris = minimatch.match(byFileSrcs, src, {
                 nocase: true
-            });
+            }).map(src => util.pathToUri(src).toLowerCase());
 
             //there is no src; this applies to all files
         } else {
-            matchedFilePaths = Object.keys(this.byFile);
+            matchedFileUris = Object.keys(this.byFile);
         }
 
         //filter each matched file
-        for (let filePath of matchedFilePaths) {
-            this.filterFile(filter, filePath);
+        for (let fileUri of matchedFileUris) {
+            this.filterFile(filter, fileUri);
         }
     }
 
-    private filterFile(filter: NormalizedFilter, filePath: string) {
+    private filterFile(filter: NormalizedFilter, fileUri: string) {
         //if the filter is negative, we're turning diagnostics on
         //if the filter is not negative we're turning diagnostics off
         const isSuppressing = !filter.isNegative;
-
+        const lowerFileUri = fileUri.toLowerCase();
         //if there is no code, set isSuppressed on every diagnostic in this file
         if (!filter.codes) {
-            this.byFile[filePath].forEach(diagnostic => {
+            this.byFile[lowerFileUri].forEach(diagnostic => {
                 diagnostic.isSuppressed = isSuppressing;
             });
 
             //set isSuppressed for any diagnostics with matching codes
         } else {
-            let fileDiagnostics = this.byFile[filePath];
+            let fileDiagnostics = this.byFile[lowerFileUri];
             for (const diagnostic of fileDiagnostics) {
                 if (filter.codes.includes(diagnostic.diagnostic.code!)) {
                     diagnostic.isSuppressed = isSuppressing;
diff --git a/src/DiagnosticManager.spec.ts b/src/DiagnosticManager.spec.ts
new file mode 100644
index 00000000..f06ff170
--- /dev/null
+++ b/src/DiagnosticManager.spec.ts
@@ -0,0 +1,31 @@
+import type { BrsFile } from './files/BrsFile';
+import type { BsDiagnostic } from './interfaces';
+import { Program } from './Program';
+import util from './util';
+
+
+describe('DiagnosticManager', () => {
+    describe('diagnosticIsSuppressed', () => {
+        it('does not crash when diagnostic is missing location information', () => {
+            const program = new Program({});
+            const file = program.setFile('source/main.brs', '') as BrsFile;
+            const diagnostic: BsDiagnostic = {
+                message: 'crash',
+                //important part of the test. range must be missing
+                location: { uri: util.pathToUri(file?.srcPath), range: undefined }
+            };
+
+            file.commentFlags.push({
+                affectedRange: util.createRange(1, 2, 3, 4),
+                codes: [1, 2, 3],
+                file: file,
+                range: util.createRange(1, 2, 3, 4)
+            });
+            program.diagnostics.register(diagnostic);
+
+            program.diagnostics.isDiagnosticSuppressed(diagnostic);
+
+            //test passes if there's no crash
+        });
+    });
+});
diff --git a/src/DiagnosticManager.ts b/src/DiagnosticManager.ts
index 891d5128..4808d945 100644
--- a/src/DiagnosticManager.ts
+++ b/src/DiagnosticManager.ts
@@ -1,11 +1,9 @@
 import type { DiagnosticContext, BsDiagnostic, DiagnosticContextPair } from './interfaces';
-import { URI } from 'vscode-uri';
 import type { AstNode } from './parser/AstNode';
 import type { Scope } from './Scope';
 import { util } from './util';
 import { Cache } from './Cache';
 import { isBsDiagnostic, isXmlScope } from './astUtils/reflection';
-import type { BscFile } from './files/BscFile';
 import type { DiagnosticRelatedInformation } from 'vscode-languageserver-protocol';
 import { DiagnosticFilterer } from './DiagnosticFilterer';
 import { DiagnosticSeverityAdjuster } from './DiagnosticSeverityAdjuster';
@@ -13,6 +11,7 @@ import type { FinalizedBsConfig } from './BsConfig';
 import chalk from 'chalk';
 import type { Logger } from './logging';
 import { LogLevel, createLogger } from './logging';
+import type { Program } from './Program';
 
 /**
  * Manages all diagnostics for a program.
@@ -37,6 +36,8 @@ export class DiagnosticManager {
 
     public options: FinalizedBsConfig;
 
+    public program: Program;
+
     /**
      * Registers a diagnostic (or multiple diagnostics) for a program.
      * Diagnostics can optionally be associated with a context
@@ -109,17 +110,14 @@ export class DiagnosticManager {
                     relatedInformation.push({
                         message: `In component scope '${scope?.xmlFile?.componentName?.text}'`,
                         location: util.createLocationFromRange(
-                            URI.file(scope.xmlFile.srcPath).toString(),
+                            util.pathToUri(scope.xmlFile?.srcPath),
                             scope?.xmlFile?.ast?.componentElement?.getAttribute('name')?.tokens?.value?.location?.range ?? util.createRange(0, 0, 0, 10)
                         )
                     });
                 } else {
                     relatedInformation.push({
                         message: `In scope '${scope.name}'`,
-                        location: util.createLocationFromRange(
-                            URI.file(diagnostic.file.srcPath).toString(),
-                            diagnostic.range
-                        )
+                        location: diagnostic.location
                     });
                 }
 
@@ -127,9 +125,29 @@ export class DiagnosticManager {
             diagnostic.relatedInformation = relatedInformation;
             results.push(diagnostic);
         }
-        return results.filter((x) => {
-            return !util.diagnosticIsSuppressed(x);
+        const filteredResults = results.filter((x) => {
+            return !this.isDiagnosticSuppressed(x);
         });
+        return filteredResults;
+    }
+
+    /**
+     * Determine whether this diagnostic should be supressed or not, based on brs comment-flags
+     */
+    public isDiagnosticSuppressed(diagnostic: BsDiagnostic) {
+        const diagnosticCode = typeof diagnostic.code === 'string' ? diagnostic.code.toLowerCase() : diagnostic.code;
+        const file = this.program?.getFile(diagnostic.location?.uri);
+
+        for (let flag of file?.commentFlags ?? []) {
+            //this diagnostic is affected by this flag
+            if (diagnostic.location.range && util.rangeContains(flag.affectedRange, diagnostic.location.range.start)) {
+                //if the flag acts upon this diagnostic's code
+                if (flag.codes === null || (diagnosticCode !== undefined && flag.codes.includes(diagnosticCode))) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     private filterDiagnostics(diagnostics: BsDiagnostic[]) {
@@ -146,9 +164,9 @@ export class DiagnosticManager {
     }
 
     public clearForFile(fileSrcPath: string) {
-        const fileSrcPathLower = fileSrcPath.toLowerCase();
+        const fileSrcPathUri = util.pathToUri(fileSrcPath).toLowerCase();
         for (const [key, cachedData] of this.diagnosticsCache.entries()) {
-            if (cachedData.diagnostic.file.srcPath.toLowerCase() === fileSrcPathLower) {
+            if (cachedData.diagnostic.location.uri.toLowerCase() === fileSrcPathUri) {
                 this.diagnosticsCache.delete(key);
             }
         }
@@ -204,7 +222,7 @@ export class DiagnosticManager {
         const needToMatch = {
             tag: !!filter.tag,
             scope: !!filter.scope,
-            file: !!filter.file,
+            fileUri: !!filter.fileUri,
             segment: !!filter.segment
         };
 
@@ -218,8 +236,8 @@ export class DiagnosticManager {
                 if (isMatch && needToMatch.scope) {
                     isMatch = context.scope === filter.scope;
                 }
-                if (isMatch && needToMatch.file) {
-                    isMatch = cachedData.diagnostic.file === filter.file;
+                if (isMatch && needToMatch.fileUri) {
+                    isMatch = cachedData.diagnostic.location?.uri === filter.fileUri;
                 }
                 if (isMatch && needToMatch.segment) {
                     isMatch = context.segment === filter.segment;
@@ -239,12 +257,12 @@ export class DiagnosticManager {
 
 
     private getDiagnosticKey(diagnostic: BsDiagnostic) {
-        return `${diagnostic.file?.srcPath} - ${diagnostic.code} - ${diagnostic.message} - ${util.rangeToString(diagnostic.range)}`;
+        return `${diagnostic.location?.uri ?? 'No uri'} ${util.rangeToString(diagnostic.location?.range)} - ${diagnostic.code} - ${diagnostic.message}`;
     }
 
     private mergeRelatedInformation(target: DiagnosticRelatedInformation[], source: DiagnosticRelatedInformation[]) {
         function getRiKey(relatedInfo: DiagnosticRelatedInformation) {
-            return `${relatedInfo.message} - ${relatedInfo.location.uri} - ${util.rangeToString(relatedInfo.location.range)}`.toLowerCase();
+            return `${relatedInfo.message} - ${relatedInfo.location?.uri} - ${util.rangeToString(relatedInfo.location?.range)}`.toLowerCase();
         }
 
         const existingKeys = target.map(ri => getRiKey(ri));
@@ -262,6 +280,6 @@ export class DiagnosticManager {
 interface DiagnosticContextFilter {
     tag?: string;
     scope?: Scope;
-    file?: BscFile;
+    fileUri?: string;
     segment?: AstNode;
 }
diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts
index 1f7f2cd7..5d481f59 100644
--- a/src/DiagnosticMessages.ts
+++ b/src/DiagnosticMessages.ts
@@ -883,4 +883,4 @@ export interface DiagnosticInfo {
 export type DiagnosticMessageType<K extends keyof D, D extends Record<string, (...args: any) => any> = typeof DiagnosticMessages> =
     ReturnType<D[K]> &
     //include the missing properties from BsDiagnostic
-    Pick<BsDiagnostic, 'range' | 'file' | 'relatedInformation' | 'tags'>;
+    Pick<BsDiagnostic, 'code' | 'location' | 'relatedInformation' | 'tags'>;
diff --git a/src/LanguageServer.spec.ts b/src/LanguageServer.spec.ts
index 4f92e68b..5ed8896d 100644
--- a/src/LanguageServer.spec.ts
+++ b/src/LanguageServer.spec.ts
@@ -16,7 +16,6 @@ import { expectZeroDiagnostics, trim } from './testHelpers.spec';
 import { isBrsFile, isLiteralString } from './astUtils/reflection';
 import { createVisitor, WalkMode } from './astUtils/visitors';
 import { tempDir, rootDir } from './testHelpers.spec';
-import { URI } from 'vscode-uri';
 import { BusyStatusTracker } from './BusyStatusTracker';
 import type { BscFile } from './files/BscFile';
 
@@ -196,8 +195,8 @@ describe('LanguageServer', () => {
                 builder: {
                     getDiagnostics: () => {
                         return [{
-                            file: {
-                                srcPath: s`${rootDir}/source/main.brs`
+                            location: {
+                                uri: util.pathToUri(s`${rootDir}/source/main.brs`)
                             },
                             code: 1000,
                             range: Range.create(1, 2, 3, 4)
@@ -209,8 +208,8 @@ describe('LanguageServer', () => {
                 builder: {
                     getDiagnostics: () => {
                         return [{
-                            file: {
-                                srcPath: s`${rootDir}/source/main.brs`
+                            location: {
+                                uri: util.pathToUri(s`${rootDir}/source/main.brs`)
                             },
                             code: 1000,
                             range: Range.create(1, 2, 3, 4)
@@ -1146,7 +1145,7 @@ describe('LanguageServer', () => {
             `;
         };
 
-        const uri = URI.file(s`${rootDir}/source/sgnode.bs`).toString();
+        const uri = util.pathToUri(s`${rootDir}/source/sgnode.bs`);
 
         fsExtra.outputFileSync(s`${rootDir}/source/sgnode.bs`, getContents());
         server.run();
diff --git a/src/LanguageServer.ts b/src/LanguageServer.ts
index 2d2545a4..37157612 100644
--- a/src/LanguageServer.ts
+++ b/src/LanguageServer.ts
@@ -457,7 +457,7 @@ export class LanguageServer {
      * anytime the program wants to load a file, check with our in-memory document cache first
      */
     private documentFileResolver(srcPath: string) {
-        let pathUri = URI.file(srcPath).toString();
+        let pathUri = util.pathToUri(srcPath);
         let document = this.documents.get(pathUri);
         if (document) {
             return document.getText();
@@ -469,7 +469,7 @@ export class LanguageServer {
         if (workspacePath.startsWith('file:')) {
             scopeUri = URI.parse(workspacePath).toString();
         } else {
-            scopeUri = URI.file(workspacePath).toString();
+            scopeUri = util.pathToUri(workspacePath);
         }
         let config = {
             configFile: undefined
@@ -599,7 +599,7 @@ export class LanguageServer {
         if (configFilePath && path.basename(configFilePath) === 'brsconfig.json') {
             builder.addDiagnostic(configFilePath, {
                 ...DiagnosticMessages.brsConfigJsonIsDeprecated(),
-                range: util.createRange(0, 0, 0, 0)
+                location: util.createLocationFromRange(util.pathToUri(configFilePath), util.createRange(0, 0, 0, 0))
             });
             return this.sendDiagnostics();
         }
@@ -1305,12 +1305,11 @@ export class LanguageServer {
             //Get only the changes to diagnostics since the last time we sent them to the client
             const patch = this.diagnosticCollection.getPatch(this.projects);
 
-            for (let filePath in patch) {
-                const uri = URI.file(filePath).toString();
-                const diagnostics = patch[filePath].map(d => util.toDiagnostic(d, uri));
+            for (let fileUri in patch) {
+                const diagnostics = patch[fileUri].map(d => util.toDiagnostic(d, fileUri));
 
                 await this.connection.sendDiagnostics({
-                    uri: uri,
+                    uri: fileUri,
                     diagnostics: diagnostics
                 });
             }
diff --git a/src/Program.spec.ts b/src/Program.spec.ts
index 53f1904d..62204257 100644
--- a/src/Program.spec.ts
+++ b/src/Program.spec.ts
@@ -7,7 +7,6 @@ import type { BrsFile } from './files/BrsFile';
 import type { XmlFile } from './files/XmlFile';
 import { Program } from './Program';
 import { standardizePath as s, util } from './util';
-import { URI } from 'vscode-uri';
 import type { FunctionStatement, PrintStatement } from './parser/Statement';
 import { EmptyStatement } from './parser/Statement';
 import { expectDiagnostics, expectHasDiagnostics, expectTypeToBe, expectZeroDiagnostics, trim, trimMap } from './testHelpers.spec';
@@ -63,9 +62,8 @@ describe('Program', () => {
     it('allows diagnostics to be set on AssetFile', () => {
         const file = program.setFile<AssetFile>('manifest', ``);
         program.diagnostics.register({
-            file: file,
             message: 'Manifest is totally bogus',
-            range: util.createRange(0, 0, 0, 10),
+            location: util.createLocationFromFileRange(file, util.createRange(0, 0, 0, 10)),
             code: 10
         });
         program.validate();
@@ -235,6 +233,28 @@ describe('Program', () => {
         });
     });
 
+    describe('getFile', () => {
+        it('can get a file from the srcPath', () => {
+            const file = program.setFile({
+                src: s`${rootDir}/source/main.brs`,
+                dest: s`${rootDir}/source/maindest.brs`
+            }, '');
+            expect(program.getFile(s`${rootDir}/source/main.brs`)).to.exist;
+            expect(program.getFile(s`${rootDir}\\source\\main.brs`)).to.exist;
+            expect(program.getFile(s`${rootDir}/SOURCE/MAIN.brs`, true)).to.exist;
+
+            expect(program.getFile(file.srcPath)).to.exist;
+        });
+
+        it('can get a file from the uri', () => {
+            const file = program.setFile({
+                src: s`${rootDir}/source/main.brs`,
+                dest: s`${rootDir}/source/maindest.brs`
+            }, '');
+            expect(program.getFile(util.pathToUri(file?.srcPath))).to.exist;
+        });
+    });
+
     describe('validate', () => {
         it('retains expressions after validate', () => {
             const file = program.setFile<BrsFile>('source/main.bs', `
@@ -260,22 +280,24 @@ describe('Program', () => {
                 </component>
             `);
             program.validate();
+            // the duplicate of component1.xml, which is in component2.xml is first
+            // the duplicate of component2.xml, which is in component1.xml is second
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.duplicateComponentName('Component1'),
-                range: Range.create(1, 17, 1, 27),
+                location: { range: Range.create(1, 17, 1, 27) },
                 relatedInformation: [{
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/components/component1.xml`).toString(),
+                        util.pathToUri(s`${rootDir}/components/component2.xml`),
                         Range.create(1, 17, 1, 27)
                     ),
                     message: 'Also defined here'
                 }]
             }, {
                 ...DiagnosticMessages.duplicateComponentName('Component1'),
-                range: Range.create(1, 17, 1, 27),
+                location: { range: Range.create(1, 17, 1, 27) },
                 relatedInformation: [{
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/components/component2.xml`).toString(),
+                        util.pathToUri(s`${rootDir}/components/component1.xml`),
                         Range.create(1, 17, 1, 27)
                     ),
                     message: 'Also defined here'
@@ -690,7 +712,7 @@ describe('Program', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.referencedFileDoesNotExist(),
-                range: Range.create(2, 42, 2, 72)
+                location: { range: Range.create(2, 42, 2, 72) }
             }]);
         });
 
diff --git a/src/Program.ts b/src/Program.ts
index fa1bfb60..6669b79c 100644
--- a/src/Program.ts
+++ b/src/Program.ts
@@ -79,9 +79,10 @@ export class Program {
         this.plugins = plugins || new PluginInterface([], { logger: this.logger });
         this.diagnostics = diagnosticsManager || new DiagnosticManager();
 
-        // initialize teh diagnostics Manager
+        // initialize the diagnostics Manager
         this.diagnostics.logger = this.logger;
         this.diagnostics.options = this.options;
+        this.diagnostics.program = this;
 
         //inject the bsc plugin as the first plugin in the stack.
         this.plugins.addFirst(new BscPlugin());
@@ -997,14 +998,10 @@ export class Program {
                     const { componentName } = xmlFile;
                     this.diagnostics.register({
                         ...DiagnosticMessages.duplicateComponentName(componentName.text),
-                        range: xmlFile.componentName.location?.range,
-                        file: xmlFile,
+                        location: xmlFile.componentName.location,
                         relatedInformation: xmlFiles.filter(x => x !== xmlFile).map(x => {
                             return {
-                                location: util.createLocationFromRange(
-                                    URI.file(xmlFile.srcPath ?? xmlFile.srcPath).toString(),
-                                    x.componentName.location?.range
-                                ),
+                                location: x.componentName.location,
                                 message: 'Also defined here'
                             };
                         })
@@ -1038,6 +1035,11 @@ export class Program {
             return this.files[
                 (normalizePath ? util.standardizePath(filePath) : filePath).toLowerCase()
             ] as T;
+        } else if (util.isUriLike(filePath)) {
+            const path = URI.parse(filePath).fsPath;
+            return this.files[
+                (normalizePath ? util.standardizePath(path) : path).toLowerCase()
+            ] as T;
         } else {
             return this.destMap.get(
                 (normalizePath ? util.standardizePath(filePath) : filePath).toLowerCase()
@@ -1304,13 +1306,14 @@ export class Program {
         const codeActions = [] as CodeAction[];
         const file = this.getFile(srcPath);
         if (file) {
+            const fileUri = util.pathToUri(file?.srcPath);
             const diagnostics = this
                 //get all current diagnostics (filtered by diagnostic filters)
                 .getDiagnostics()
                 //only keep diagnostics related to this file
-                .filter(x => x.file === file)
+                .filter(x => x.location.uri === fileUri)
                 //only keep diagnostics that touch this range
-                .filter(x => util.rangesIntersectOrTouch(x.range, range));
+                .filter(x => util.rangesIntersectOrTouch(x.location.range, range));
 
             const scopes = this.getScopesForFile(file);
 
diff --git a/src/ProgramBuilder.spec.ts b/src/ProgramBuilder.spec.ts
index 6a9c8241..6de762e4 100644
--- a/src/ProgramBuilder.spec.ts
+++ b/src/ProgramBuilder.spec.ts
@@ -189,6 +189,7 @@ describe('ProgramBuilder', () => {
                 project: s`${rootDir}/bsconfig.json`,
                 username: 'john'
             });
+            // TODO: this is not run - builder.run throws a diagnostic
             expect(builder.program.options.username).to.equal('rokudev');
         });
 
@@ -289,17 +290,15 @@ describe('ProgramBuilder', () => {
     describe('printDiagnostics', () => {
 
         it('does not crash when a diagnostic is missing range informtaion', () => {
-            const file = builder.program.setFile('source/main.brs', ``);
+            builder.program.setFile('source/main.brs', ``);
             builder.diagnostics.register([{
                 message: 'message 1',
-                code: 'test1',
-                file: file
+                code: 'test1'
             }, {
                 message: 'message 2',
-                code: 'test1',
-                file: file
+                code: 'test1'
             }] as any);
-            const stub = sinon.stub(diagnosticUtils, 'printDiagnostic').callsFake(() => { });
+            const stub = sinon.stub(diagnosticUtils, 'printDiagnostic').callsFake((...args) => { });
             //if this doesn't crash, then the test passes
             builder['printDiagnostics']();
             expect(stub.getCalls().map(x => x.args[4].message)).to.eql([
@@ -330,12 +329,10 @@ describe('ProgramBuilder', () => {
         it('prints diagnostic, when file is present in project', () => {
             builder.options.showDiagnosticsInConsole = true;
 
-            let diagnostics = createBsDiagnostic('p1', ['m1']);
-            let f1 = diagnostics[0].file as BrsFile;
-            f1.fileContents = `l1\nl2\nl3`;
-            sinon.stub(builder, 'getDiagnostics').returns(diagnostics);
+            let diagnostics = createBsDiagnostics('p1', ['m1']);
+            builder.program.setFile('p1', `l1\nl2\nl3`);
 
-            sinon.stub(builder.program, 'getFile').returns(f1);
+            sinon.stub(builder, 'getDiagnostics').returns(diagnostics);
 
             let printStub = sinon.stub(diagnosticUtils, 'printDiagnostic');
 
@@ -348,13 +345,9 @@ describe('ProgramBuilder', () => {
     it('prints diagnostic, when file has no lines', () => {
         builder.options.showDiagnosticsInConsole = true;
 
-        let diagnostics = createBsDiagnostic('p1', ['m1']);
-        let f1 = diagnostics[0].file as BrsFile;
-        (f1.fileContents as any) = null;
+        let diagnostics = createBsDiagnostics('p1', ['m1']);
+        builder.program.setFile('p1', null);
         sinon.stub(builder, 'getDiagnostics').returns(diagnostics);
-
-        sinon.stub(builder.program, 'getFile').returns(f1);
-
         let printStub = sinon.stub(diagnosticUtils, 'printDiagnostic');
 
         builder['printDiagnostics']();
@@ -365,7 +358,7 @@ describe('ProgramBuilder', () => {
     it('prints diagnostic, when no file present', () => {
         builder.options.showDiagnosticsInConsole = true;
 
-        let diagnostics = createBsDiagnostic('p1', ['m1']);
+        let diagnostics = createBsDiagnostics('p1', ['m1']);
         sinon.stub(builder, 'getDiagnostics').returns(diagnostics);
 
         sinon.stub(builder.program, 'getFile').returns(null as any);
@@ -427,12 +420,11 @@ describe('ProgramBuilder', () => {
     });
 });
 
-function createBsDiagnostic(filePath: string, messages: string[]): BsDiagnostic[] {
+function createBsDiagnostics(filePath: string, messages: string[]): BsDiagnostic[] {
     let file = new BrsFile({ srcPath: filePath, destPath: filePath, program: null });
     let diagnostics = [];
     for (let message of messages) {
         let d = createDiagnostic(file, 1, message);
-        d.file = file;
         diagnostics.push(d);
     }
     return diagnostics;
@@ -447,11 +439,10 @@ function createDiagnostic(
     endCol = 99999,
     severity: DiagnosticSeverity = DiagnosticSeverity.Error
 ) {
-    const diagnostic = {
+    const diagnostic: BsDiagnostic = {
         code: code,
         message: message,
-        range: util.createRange(startLine, startCol, endLine, endCol),
-        file: file,
+        location: util.createLocationFromFileRange(file, util.createRange(startLine, startCol, endLine, endCol)),
         severity: severity
     };
     return diagnostic;
diff --git a/src/ProgramBuilder.ts b/src/ProgramBuilder.ts
index 3878d747..af266aa1 100644
--- a/src/ProgramBuilder.ts
+++ b/src/ProgramBuilder.ts
@@ -14,10 +14,7 @@ import * as diagnosticUtils from './diagnosticUtils';
 import * as fsExtra from 'fs-extra';
 import * as requireRelative from 'require-relative';
 import { Throttler } from './Throttler';
-import { AssetFile } from './files/AssetFile';
-import type { BscFile } from './files/BscFile';
 import type { BrsFile } from './files/BrsFile';
-import { URI } from 'vscode-uri';
 import { DiagnosticManager } from './DiagnosticManager';
 
 /**
@@ -71,18 +68,14 @@ export class ProgramBuilder {
         if (!this.program) {
             throw new Error('Cannot call `ProgramBuilder.addDiagnostic` before `ProgramBuilder.run()`');
         }
-        let file: BscFile | undefined = this.program.getFile(srcPath);
-        if (!file) {
-            // eslint-disable-next-line @typescript-eslint/dot-notation
-            const paths = this.program['getPaths'](srcPath, this.program.options.rootDir ?? this.options.rootDir);
-            file = new AssetFile(paths);
-            //keep this for backwards-compatibility. TODO remove in v1
-            // eslint-disable-next-line @typescript-eslint/dot-notation
-            file['pathAbsolute'] = file.srcPath;
-            diagnostic.file = file;
+        if (!diagnostic.location) {
+            diagnostic.location = {
+                uri: util.pathToUri(srcPath),
+                range: util.createRange(0, 0, 0, 0)
+            };
+        } else {
+            diagnostic.location.uri = util.pathToUri(srcPath);
         }
-
-        diagnostic.file = file;
         this.diagnostics.register(<any>diagnostic, { tags: ['ProgramBuilder'] });
     }
 
@@ -106,7 +99,7 @@ export class ProgramBuilder {
             this.loadRequires();
             this.loadPlugins();
         } catch (e: any) {
-            if (e?.file && e.message && e.code) {
+            if (e?.location && e.message && e.code) {
                 let err = e as BsDiagnostic;
                 this.diagnostics.register(err);
             } else {
@@ -300,33 +293,34 @@ export class ProgramBuilder {
         //group the diagnostics by file
         let diagnosticsByFile = {} as Record<string, BsDiagnostic[]>;
         for (let diagnostic of diagnostics) {
-            if (!diagnosticsByFile[diagnostic.file.srcPath]) {
-                diagnosticsByFile[diagnostic.file.srcPath] = [];
+            const diagnosticFileKey = diagnostic.location?.uri ?? 'no-uri';
+            if (!diagnosticsByFile[diagnosticFileKey]) {
+                diagnosticsByFile[diagnosticFileKey] = [];
             }
-            diagnosticsByFile[diagnostic.file.srcPath].push(diagnostic);
+            diagnosticsByFile[diagnosticFileKey].push(diagnostic);
         }
 
         //get printing options
         const options = diagnosticUtils.getPrintDiagnosticOptions(this.options);
         const { cwd, emitFullPaths } = options;
 
-        let srcPaths = Object.keys(diagnosticsByFile).sort();
-        for (let srcPath of srcPaths) {
-            let diagnosticsForFile = diagnosticsByFile[srcPath];
+        let fileUris = Object.keys(diagnosticsByFile).sort();
+        for (let fileUri of fileUris) {
+            let diagnosticsForFile = diagnosticsByFile[fileUri];
             //sort the diagnostics in line and column order
             let sortedDiagnostics = diagnosticsForFile.sort((a, b) => {
                 return (
-                    (a.range?.start.line ?? -1) - (b.range?.start.line ?? -1) ||
-                    (a.range?.start.character ?? -1) - (b.range?.start.character ?? -1)
+                    (a.location?.range?.start.line ?? -1) - (b.location?.range?.start.line ?? -1) ||
+                    (a.location?.range?.start.character ?? -1) - (b.location?.range?.start.character ?? -1)
                 );
             });
 
-            let filePath = srcPath;
+            let filePath = util.uriToPath(fileUri);
             if (!emitFullPaths) {
                 filePath = path.relative(cwd, filePath);
             }
             //load the file text
-            const file = this.program?.getFile(srcPath);
+            const file = this.program?.getFile(fileUri);
             //get the file's in-memory contents if available
             const lines = (file as BrsFile)?.fileContents?.split(/\r?\n/g) ?? [];
 
@@ -334,7 +328,7 @@ export class ProgramBuilder {
                 //default the severity to error if undefined
                 let severity = typeof diagnostic.severity === 'number' ? diagnostic.severity : DiagnosticSeverity.Error;
                 let relatedInformation = (util.toDiagnostic(diagnostic, diagnostic.source)?.relatedInformation ?? []).map(x => {
-                    let relatedInfoFilePath = URI.parse(x.location.uri).fsPath;
+                    let relatedInfoFilePath = util.uriToPath(x.location?.uri);
                     if (!emitFullPaths) {
                         relatedInfoFilePath = path.relative(cwd, relatedInfoFilePath);
                     }
diff --git a/src/Scope.spec.ts b/src/Scope.spec.ts
index 260348d0..d5228ce0 100644
--- a/src/Scope.spec.ts
+++ b/src/Scope.spec.ts
@@ -356,16 +356,16 @@ describe('Scope', () => {
             expectDiagnostics(program, [
                 {
                     ...DiagnosticMessages.cannotFindName('delta', 'constants.alpha.delta', 'constants.alpha', 'namespace'),
-                    file: {
-                        srcPath: buttonPrimary.srcPath
+                    location: {
+                        uri: util.pathToUri(buttonPrimary?.srcPath)
                     },
                     relatedInformation: [{
                         message: `In component scope 'ButtonPrimary'`
                     }]
                 }, {
                     ...DiagnosticMessages.cannotFindName('delta', 'constants.alpha.delta', 'constants.alpha', 'namespace'),
-                    file: {
-                        srcPath: buttonSecondary.srcPath
+                    location: {
+                        uri: util.pathToUri(buttonSecondary?.srcPath)
                     },
                     relatedInformation: [{
                         message: `In component scope 'ButtonSecondary'`
@@ -420,10 +420,10 @@ describe('Scope', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.cannotFindName('Charlie'),
-                range: util.createRange(6, 32, 6, 39)
+                location: { range: util.createRange(6, 32, 6, 39) }
             }, {
                 ...DiagnosticMessages.itemCannotBeUsedAsVariable('namespace'),
-                range: util.createRange(7, 32, 7, 50)
+                location: { range: util.createRange(7, 32, 7, 50) }
             }]);
         });
 
@@ -461,7 +461,7 @@ describe('Scope', () => {
             expectDiagnostics(program, [{
                 //print |lineHeight|
                 message: DiagnosticMessages.cannotFindName('lineHeight').message,
-                range: util.createRange(3, 30, 3, 40)
+                location: { range: util.createRange(3, 30, 3, 40) }
             }]);
         });
 
@@ -778,7 +778,7 @@ describe('Scope', () => {
                 // only care about code and `roFontMetrics` match
                 expectDiagnostics(program, [{
                     ...DiagnosticMessages.unknownBrightScriptComponent('roFontMetrics'),
-                    range: Range.create(2, 51, 2, 66)
+                    location: { range: Range.create(2, 51, 2, 66) }
                 }]);
             });
 
@@ -911,7 +911,7 @@ describe('Scope', () => {
                 program.validate();
                 expectDiagnostics(program, [{
                     ...DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib'),
-                    range: Range.create(2, 24, 2, 27)
+                    location: { range: Range.create(2, 24, 2, 27) }
                 }]);
             });
 
@@ -953,7 +953,7 @@ describe('Scope', () => {
                 program.validate();
                 expectDiagnostics(program, [{
                     message: DiagnosticMessages.localVarFunctionShadowsParentFunction('scope').message,
-                    range: Range.create(2, 24, 2, 32)
+                    location: { range: Range.create(2, 24, 2, 32) }
                 }]);
             });
 
@@ -970,7 +970,7 @@ describe('Scope', () => {
                 program.validate();
                 expectDiagnostics(program, [{
                     message: DiagnosticMessages.localVarShadowedByScopedFunction().message,
-                    range: Range.create(2, 24, 2, 32)
+                    location: { range: Range.create(2, 24, 2, 32) }
                 }]);
             });
 
@@ -986,7 +986,7 @@ describe('Scope', () => {
                 program.validate();
                 expectDiagnostics(program, [{
                     message: DiagnosticMessages.localVarShadowedByScopedFunction().message,
-                    range: Range.create(1, 29, 1, 37)
+                    location: { range: Range.create(1, 29, 1, 37) }
                 }]);
             });
 
@@ -1004,7 +1004,7 @@ describe('Scope', () => {
                     DiagnosticMessages.nameCollision('Function', 'Global Function', 'Str').message,
                     {
                         message: DiagnosticMessages.scopeFunctionShadowedByBuiltInFunction().message,
-                        range: Range.create(4, 29, 4, 32)
+                        location: { range: Range.create(4, 29, 4, 32) }
                     }]);
             });
         });
diff --git a/src/XmlScope.spec.ts b/src/XmlScope.spec.ts
index 6ec0d5bc..e26400de 100644
--- a/src/XmlScope.spec.ts
+++ b/src/XmlScope.spec.ts
@@ -118,21 +118,21 @@ describe('XmlScope', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.xmlFunctionNotFound('func2'),
-                range: Range.create(4, 24, 4, 29)
+                location: { range: Range.create(4, 24, 4, 29) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('function', 'name'),
-                range: Range.create(5, 9, 5, 17)
+                location: { range: Range.create(5, 9, 5, 17) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('function', 'name'),
-                range: Range.create(6, 9, 6, 17)
+                location: { range: Range.create(6, 9, 6, 17) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('function', 'name'),
-                range: Range.create(7, 9, 7, 17)
+                location: { range: Range.create(7, 9, 7, 17) }
             }, { // syntax error expecting '=' but found '/>'
                 code: DiagnosticMessages.xmlGenericParseError('').code
             }, { // onChange function
                 ...DiagnosticMessages.xmlFunctionNotFound('func4'),
-                range: Range.create(8, 51, 8, 56)
+                location: { range: Range.create(8, 51, 8, 56) }
             }]);
         });
 
@@ -161,22 +161,22 @@ describe('XmlScope', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.xmlInvalidFieldType('no'),
-                range: Range.create(4, 33, 4, 35)
+                location: { range: Range.create(4, 33, 4, 35) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('field', 'type'),
-                range: Range.create(5, 9, 5, 14)
+                location: { range: Range.create(5, 9, 5, 14) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('field', 'id'),
-                range: Range.create(6, 9, 6, 14)
+                location: { range: Range.create(6, 9, 6, 14) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('field', 'id'),
-                range: Range.create(8, 9, 8, 14)
+                location: { range: Range.create(8, 9, 8, 14) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('field', 'id'),
-                range: Range.create(9, 9, 9, 14)
+                location: { range: Range.create(9, 9, 9, 14) }
             }, {
                 ...DiagnosticMessages.xmlTagMissingAttribute('field', 'type'),
-                range: Range.create(9, 9, 9, 14)
+                location: { range: Range.create(9, 9, 9, 14) }
             }, { // syntax error expecting '=' but found '/>'
                 code: DiagnosticMessages.xmlGenericParseError('').code
             }]);
diff --git a/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts b/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts
index e74975f7..577ec705 100644
--- a/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts
+++ b/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts
@@ -1,5 +1,4 @@
 import { expect } from '../../chai-config.spec';
-import { URI } from 'vscode-uri';
 import type { Range } from 'vscode-languageserver';
 import { Program } from '../../Program';
 import { expectCodeActions, trim } from '../../testHelpers.spec';
@@ -93,7 +92,7 @@ describe('CodeActionsProcessor', () => {
                 util.createRange(1, 5, 1, 5)
             );
             expect(
-                codeActions[0].edit!.changes![URI.file(s`${rootDir}/components/comp1.xml`).toString()][0].range
+                codeActions[0].edit!.changes![util.pathToUri(s`${rootDir}/components/comp1.xml`)][0].range
             ).to.eql(
                 util.createRange(1, 51, 1, 51)
             );
diff --git a/src/bscPlugin/codeActions/CodeActionsProcessor.ts b/src/bscPlugin/codeActions/CodeActionsProcessor.ts
index 8058441b..ed330a6c 100644
--- a/src/bscPlugin/codeActions/CodeActionsProcessor.ts
+++ b/src/bscPlugin/codeActions/CodeActionsProcessor.ts
@@ -1,4 +1,3 @@
-import type { Diagnostic } from 'vscode-languageserver';
 import { CodeActionKind } from 'vscode-languageserver';
 import { codeActionUtil } from '../../CodeActionUtil';
 import type { DiagnosticMessageType } from '../../DiagnosticMessages';
@@ -6,7 +5,7 @@ import { DiagnosticCodeMap } from '../../DiagnosticMessages';
 import type { BrsFile } from '../../files/BrsFile';
 import type { BscFile } from '../../files/BscFile';
 import type { XmlFile } from '../../files/XmlFile';
-import type { OnGetCodeActionsEvent } from '../../interfaces';
+import type { BsDiagnostic, OnGetCodeActionsEvent } from '../../interfaces';
 import { ParseMode } from '../../parser/Parser';
 import { util } from '../../util';
 import { isBrsFile } from '../../astUtils/reflection';
@@ -35,7 +34,7 @@ export class CodeActionsProcessor {
     /**
      * Generic import suggestion function. Shouldn't be called directly from the main loop, but instead called by more specific diagnostic handlers
      */
-    private suggestImports(diagnostic: Diagnostic, key: string, files: BscFile[]) {
+    private suggestImports(diagnostic: BsDiagnostic, key: string, files: BscFile[]) {
         //skip if we already have this suggestion
         if (this.suggestedImports.has(key) || !isBrsFile(this.event.file)) {
             return;
@@ -69,7 +68,8 @@ export class CodeActionsProcessor {
 
     private suggestCannotFindName(diagnostic: DiagnosticMessageType<'cannotFindName'>) {
         //skip if not a BrighterScript file
-        if ((diagnostic.file as BrsFile).parseMode !== ParseMode.BrighterScript) {
+        const file = this.event.program.getFile(diagnostic.location?.uri);
+        if (!file || (file as BrsFile).parseMode !== ParseMode.BrighterScript) {
             return;
         }
         const lowerName = (diagnostic.data.fullName ?? diagnostic.data.name).toLowerCase();
@@ -88,7 +88,8 @@ export class CodeActionsProcessor {
 
     private suggestClassImports(diagnostic: DiagnosticMessageType<'classCouldNotBeFound'>) {
         //skip if not a BrighterScript file
-        if ((diagnostic.file as BrsFile).parseMode !== ParseMode.BrighterScript) {
+        const file = this.event.program.getFile(diagnostic.location?.uri);
+        if (!file || (file as BrsFile).parseMode !== ParseMode.BrighterScript) {
             return;
         }
         const lowerClassName = diagnostic.data.className.toLowerCase();
diff --git a/src/bscPlugin/definition/DefinitionProvider.spec.ts b/src/bscPlugin/definition/DefinitionProvider.spec.ts
index 44a8babb..d7587036 100644
--- a/src/bscPlugin/definition/DefinitionProvider.spec.ts
+++ b/src/bscPlugin/definition/DefinitionProvider.spec.ts
@@ -4,7 +4,6 @@ import { standardizePath as s, util } from '../../util';
 let rootDir = s`${process.cwd()}/rootDir`;
 import { createSandbox } from 'sinon';
 import { DefinitionProvider } from './DefinitionProvider';
-import { URI } from 'vscode-uri';
 const sinon = createSandbox();
 
 describe('DefinitionProvider', () => {
@@ -53,10 +52,10 @@ describe('DefinitionProvider', () => {
             //   m.customButton@.click|CustomButon()
             program.getDefinition(brsFile.srcPath, util.createPosition(2, 37))
         ).to.eql([{
-            uri: URI.file(customButtonXml.srcPath).toString(),
+            uri: util.pathToUri(customButtonXml.srcPath),
             range: util.createRange(4, 36, 4, 53)
         }, {
-            uri: URI.file(customButtonBrs.srcPath).toString(),
+            uri: util.pathToUri(customButtonBrs.srcPath),
             range: util.createRange(1, 21, 1, 38)
         }]);
     });
@@ -90,7 +89,7 @@ describe('DefinitionProvider', () => {
         expect(
             program.getDefinition(main.srcPath, util.createPosition(4, 24))
         ).to.eql([{
-            uri: URI.file(main.srcPath).toString(),
+            uri: util.pathToUri(main.srcPath),
             range: util.createRange(2, 16, 2, 22)
         }]);
     });
diff --git a/src/bscPlugin/definition/DefinitionProvider.ts b/src/bscPlugin/definition/DefinitionProvider.ts
index 41f7ffd7..189d72d8 100644
--- a/src/bscPlugin/definition/DefinitionProvider.ts
+++ b/src/bscPlugin/definition/DefinitionProvider.ts
@@ -6,7 +6,6 @@ import type { Location } from 'vscode-languageserver-protocol';
 import type { ClassStatement, FunctionStatement, NamespaceStatement } from '../../parser/Statement';
 import { ParseMode } from '../../parser/Parser';
 import util from '../../util';
-import { URI } from 'vscode-uri';
 import { WalkMode, createVisitor } from '../../astUtils/visitors';
 import type { Token } from '../../lexer/Token';
 import type { XmlFile } from '../../files/XmlFile';
@@ -56,10 +55,7 @@ export class DefinitionProvider {
             const constant = scope?.getConstFileLink(fullName, containingNamespace);
             if (constant) {
                 this.event.definitions.push(
-                    util.createLocationFromRange(
-                        URI.file(constant.file.srcPath).toString(),
-                        constant.item.tokens.name?.location?.range
-                    )
+                    constant.item.tokens.name?.location
                 );
                 return;
             }
@@ -68,20 +64,14 @@ export class DefinitionProvider {
                 const enumLink = scope.getEnumFileLink(fullName, containingNamespace);
                 if (enumLink) {
                     this.event.definitions.push(
-                        util.createLocationFromRange(
-                            URI.file(enumLink.file.srcPath).toString(),
-                            enumLink.item.tokens.name.location?.range
-                        )
+                        enumLink.item.tokens.name.location
                     );
                     return;
                 }
                 const enumMemberLink = scope.getEnumMemberFileLink(fullName, containingNamespace);
                 if (enumMemberLink) {
                     this.event.definitions.push(
-                        util.createLocationFromRange(
-                            URI.file(enumMemberLink.file.srcPath).toString(),
-                            enumMemberLink.item.tokens.name.location?.range
-                        )
+                        enumMemberLink.item.tokens.name.location
                     );
                     return;
                 }
@@ -89,10 +79,7 @@ export class DefinitionProvider {
                 const interfaceFileLink = scope.getInterfaceFileLink(fullName, containingNamespace);
                 if (interfaceFileLink) {
                     this.event.definitions.push(
-                        util.createLocationFromRange(
-                            URI.file(interfaceFileLink.file.srcPath).toString(),
-                            interfaceFileLink.item.tokens.name.location?.range
-                        )
+                        interfaceFileLink.item.tokens.name.location
                     );
                     return;
                 }
@@ -100,10 +87,7 @@ export class DefinitionProvider {
                 const classFileLink = scope.getClassFileLink(fullName, containingNamespace);
                 if (classFileLink) {
                     this.event.definitions.push(
-                        util.createLocationFromRange(
-                            URI.file(classFileLink.file.srcPath).toString(),
-                            classFileLink.item.tokens.name.location?.range
-                        )
+                        classFileLink.item.tokens.name.location
                     );
                     return;
                 }
diff --git a/src/bscPlugin/validation/BrsFileValidator.spec.ts b/src/bscPlugin/validation/BrsFileValidator.spec.ts
index 9517976f..94bb068c 100644
--- a/src/bscPlugin/validation/BrsFileValidator.spec.ts
+++ b/src/bscPlugin/validation/BrsFileValidator.spec.ts
@@ -128,10 +128,10 @@ describe('BrsFileValidator', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('class'),
-            range: util.createRange(2, 16, 2, 27)
+            location: { range: util.createRange(2, 16, 2, 27) }
         }, {
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('class'),
-            range: util.createRange(5, 20, 5, 30)
+            location: { range: util.createRange(5, 20, 5, 30) }
         }]);
     });
 
@@ -171,10 +171,10 @@ describe('BrsFileValidator', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('enum'),
-            range: util.createRange(2, 16, 2, 26)
+            location: { range: util.createRange(2, 16, 2, 26) }
         }, {
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('enum'),
-            range: util.createRange(6, 20, 6, 29)
+            location: { range: util.createRange(6, 20, 6, 29) }
         }]);
     });
 
@@ -209,10 +209,10 @@ describe('BrsFileValidator', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('function'),
-            range: util.createRange(2, 16, 2, 30)
+            location: { range: util.createRange(2, 16, 2, 30) }
         }, {
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('function'),
-            range: util.createRange(5, 20, 5, 33)
+            location: { range: util.createRange(5, 20, 5, 33) }
         }]);
     });
 
@@ -247,10 +247,10 @@ describe('BrsFileValidator', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('namespace'),
-            range: util.createRange(2, 16, 2, 31)
+            location: { range: util.createRange(2, 16, 2, 31) }
         }, {
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('namespace'),
-            range: util.createRange(5, 20, 5, 34)
+            location: { range: util.createRange(5, 20, 5, 34) }
         }]);
     });
 
@@ -290,10 +290,10 @@ describe('BrsFileValidator', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('interface'),
-            range: util.createRange(2, 16, 2, 31)
+            location: { range: util.createRange(2, 16, 2, 31) }
         }, {
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('interface'),
-            range: util.createRange(6, 20, 6, 34)
+            location: { range: util.createRange(6, 20, 6, 34) }
         }]);
     });
 
@@ -323,10 +323,10 @@ describe('BrsFileValidator', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('const'),
-            range: util.createRange(2, 16, 2, 27)
+            location: { range: util.createRange(2, 16, 2, 27) }
         }, {
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel('const'),
-            range: util.createRange(4, 20, 4, 30)
+            location: { range: util.createRange(4, 20, 4, 30) }
         }]);
     });
 
diff --git a/src/bscPlugin/validation/BrsFileValidator.ts b/src/bscPlugin/validation/BrsFileValidator.ts
index 9bc2c215..2836019e 100644
--- a/src/bscPlugin/validation/BrsFileValidator.ts
+++ b/src/bscPlugin/validation/BrsFileValidator.ts
@@ -66,9 +66,8 @@ export class BrsFileValidator {
             CallfuncExpression: (node) => {
                 if (node.args.length > 5) {
                     this.event.program.diagnostics.register({
-                        file: this.event.file,
                         ...DiagnosticMessages.callfuncHasToManyArgs(node.args.length),
-                        range: node.tokens.methodName.location?.range
+                        location: node.tokens.methodName.location
                     });
                 }
             },
@@ -212,9 +211,8 @@ export class BrsFileValidator {
             },
             ConditionalCompileErrorStatement: (node) => {
                 this.event.program.diagnostics.register({
-                    file: this.event.file,
                     ...DiagnosticMessages.hashError(node.tokens.message.text),
-                    range: node.location?.range
+                    location: node.location
                 });
             },
             AliasStatement: (node) => {
@@ -258,9 +256,8 @@ export class BrsFileValidator {
 
         //the statement was defined in the wrong place. Flag it.
         this.event.program.diagnostics.register({
-            file: this.event.file,
             ...DiagnosticMessages.keywordMustBeDeclaredAtNamespaceLevel(keyword),
-            range: rangeFactory?.() ?? statement.location?.range
+            location: rangeFactory ? util.createLocationFromFileRange(this.event.file, rangeFactory()) : statement.location
         });
     }
 
@@ -269,9 +266,8 @@ export class BrsFileValidator {
             //flag every parameter over the limit
             for (let i = CallExpression.MaximumArguments; i < func.parameters.length; i++) {
                 this.event.program.diagnostics.register({
-                    file: this.event.file,
                     ...DiagnosticMessages.tooManyCallableParameters(func.parameters.length, CallExpression.MaximumArguments),
-                    range: func.parameters[i]?.tokens.name?.location?.range ?? func.parameters[i]?.location?.range ?? func.location?.range
+                    location: func.parameters[i]?.tokens.name?.location ?? func.parameters[i]?.location ?? func.location
                 });
             }
         }
@@ -290,9 +286,8 @@ export class BrsFileValidator {
              */
             if (memberNames.has(memberNameLower)) {
                 this.event.program.diagnostics.register({
-                    file: this.event.file,
                     ...DiagnosticMessages.duplicateIdentifier(member.name),
-                    range: member.location?.range
+                    location: member.location
                 });
             } else {
                 memberNames.add(memberNameLower);
@@ -321,11 +316,10 @@ export class BrsFileValidator {
             (memberValue && !isLiteralExpression(memberValue))
         ) {
             this.event.program.diagnostics.register({
-                file: this.event.file,
                 ...DiagnosticMessages.enumValueMustBeType(
                     enumValueKind.replace(/literal$/i, '').toLowerCase()
                 ),
-                range: range
+                location: util.createLocationFromFileRange(this.event.file, range)
             });
         }
 
@@ -336,22 +330,20 @@ export class BrsFileValidator {
                 //member value is same as enum
                 if (memberValueKind !== enumValueKind) {
                     this.event.program.diagnostics.register({
-                        file: this.event.file,
                         ...DiagnosticMessages.enumValueMustBeType(
                             enumValueKind.replace(/literal$/i, '').toLowerCase()
                         ),
-                        range: range
+                        location: util.createLocationFromFileRange(this.event.file, range)
                     });
                 }
 
                 //default value missing
             } else {
                 this.event.program.diagnostics.register({
-                    file: this.event.file,
                     ...DiagnosticMessages.enumValueIsRequired(
                         enumValueKind.replace(/literal$/i, '').toLowerCase()
                     ),
-                    range: range
+                    location: util.createLocationFromFileRange(this.event.file, range)
                 });
             }
         }
@@ -362,9 +354,8 @@ export class BrsFileValidator {
         const isBool = ccConst.kind === TokenKind.True || ccConst.kind === TokenKind.False;
         if (!isBool && !this.event.file.ast.bsConsts.has(ccConst.text.toLowerCase())) {
             this.event.program.diagnostics.register({
-                file: this.event.file,
                 ...DiagnosticMessages.referencedConstDoesNotExist(),
-                range: ccConst.location?.range
+                location: ccConst.location
             });
             return false;
         }
@@ -397,9 +388,8 @@ export class BrsFileValidator {
                     !isAliasStatement(statement)
                 ) {
                     this.event.program.diagnostics.register({
-                        file: this.event.file,
                         ...DiagnosticMessages.unexpectedStatementOutsideFunction(),
-                        range: statement.location?.range
+                        location: statement.location
                     });
                 }
             }
@@ -438,20 +428,17 @@ export class BrsFileValidator {
                 if (isLibraryStatement(result)) {
                     this.event.program.diagnostics.register({
                         ...DiagnosticMessages.statementMustBeDeclaredAtTopOfFile('library'),
-                        range: result.location?.range,
-                        file: this.event.file
+                        location: result.location
                     });
                 } else if (isImportStatement(result)) {
                     this.event.program.diagnostics.register({
                         ...DiagnosticMessages.statementMustBeDeclaredAtTopOfFile('import'),
-                        range: result.location?.range,
-                        file: this.event.file
+                        location: result.location
                     });
                 } else if (isAliasStatement(result)) {
                     this.event.program.diagnostics.register({
                         ...DiagnosticMessages.statementMustBeDeclaredAtTopOfFile('alias'),
-                        range: result.location?.range,
-                        file: this.event.file
+                        location: result.location
                     });
                 }
             }
@@ -466,8 +453,7 @@ export class BrsFileValidator {
             const typecastStmt = topOfFileTypecastStatements[i];
             this.event.program.diagnostics.register({
                 ...DiagnosticMessages.typecastStatementMustBeDeclaredAtStart(),
-                range: typecastStmt.location?.range,
-                file: this.event.file
+                location: typecastStmt.location
             });
         }
 
@@ -482,8 +468,7 @@ export class BrsFileValidator {
             if (isBadTypecastObj) {
                 this.event.program.diagnostics.register({
                     ...DiagnosticMessages.invalidTypecastStatementApplication(util.getAllDottedGetPartsAsString(result.typecastExpression.obj)),
-                    range: result.typecastExpression.obj.location?.range,
-                    file: this.event.file
+                    location: result.typecastExpression.obj.location
                 });
             }
 
@@ -499,8 +484,7 @@ export class BrsFileValidator {
             if (!isFirst || !isAllowedBlock) {
                 this.event.program.diagnostics.register({
                     ...DiagnosticMessages.typecastStatementMustBeDeclaredAtStart(),
-                    range: result.location?.range,
-                    file: this.event.file
+                    location: result.location
                 });
             }
         }
@@ -513,8 +497,7 @@ export class BrsFileValidator {
             const actualLoopType = statement.tokens.loopType;
             if (actualLoopType && expectedLoopType?.toLowerCase() !== actualLoopType.text?.toLowerCase()) {
                 this.event.program.diagnostics.register({
-                    file: this.event.file,
-                    range: statement.tokens.loopType.location?.range,
+                    location: statement.tokens.loopType.location,
                     ...DiagnosticMessages.expectedToken(expectedLoopType)
                 });
             }
@@ -536,8 +519,7 @@ export class BrsFileValidator {
         //flag continue statements found outside of a loop
         if (!parent) {
             this.event.program.diagnostics.register({
-                file: this.event.file,
-                range: statement.location?.range,
+                location: statement.location,
                 ...DiagnosticMessages.illegalContinueStatement()
             });
         }
@@ -570,9 +552,8 @@ export class BrsFileValidator {
                 }
 
                 this.event.program.diagnostics.register({
-                    file: this.event.file,
                     ...DiagnosticMessages.noOptionalChainingInLeftHandSideOfAssignment(),
-                    range: range
+                    location: util.createLocationFromFileRange(this.event.file, range)
                 });
             }
 
diff --git a/src/bscPlugin/validation/ProgramValidator.ts b/src/bscPlugin/validation/ProgramValidator.ts
index 3a70671e..9ecce5a7 100644
--- a/src/bscPlugin/validation/ProgramValidator.ts
+++ b/src/bscPlugin/validation/ProgramValidator.ts
@@ -32,8 +32,7 @@ export class ProgramValidator {
 
             this.event.program.diagnostics.register({
                 ...DiagnosticMessages.fileNotReferencedByAnyOtherFile(),
-                file: file,
-                range: util.createRange(0, 0, 0, Number.MAX_VALUE)
+                location: util.createLocationFromFileRange(file, util.createRange(0, 0, 0, Number.MAX_VALUE))
             }, { tags: [ProgramValidatorDiagnosticsTag] });
         }
     }
diff --git a/src/bscPlugin/validation/ScopeValidator.ts b/src/bscPlugin/validation/ScopeValidator.ts
index 120f0dae..e8750784 100644
--- a/src/bscPlugin/validation/ScopeValidator.ts
+++ b/src/bscPlugin/validation/ScopeValidator.ts
@@ -1,4 +1,3 @@
-import { URI } from 'vscode-uri';
 import { DiagnosticTag, type Range } from 'vscode-languageserver';
 import { isAliasStatement, isAssignmentStatement, isAssociativeArrayType, isBinaryExpression, isBooleanType, isBrsFile, isCallExpression, isCallableType, isClassStatement, isClassType, isComponentType, isDottedGetExpression, isDynamicType, isEnumMemberType, isEnumType, isFunctionExpression, isFunctionParameterExpression, isLiteralExpression, isNamespaceStatement, isNamespaceType, isNewExpression, isNumberType, isObjectType, isPrimitiveType, isReferenceType, isStringType, isTypedFunctionType, isUnionType, isVariableExpression, isXmlScope } from '../../astUtils/reflection';
 import type { DiagnosticInfo } from '../../DiagnosticMessages';
@@ -113,6 +112,8 @@ export class ScopeValidator {
 
         this.event.scope.enumerateOwnFiles((file) => {
             if (isBrsFile(file)) {
+
+                const fileUri = util.pathToUri(file.srcPath);
                 const thisFileHasChanges = this.event.changedFiles.includes(file);
 
                 const hasUnvalidatedSegments = file.validationSegmenter.hasUnvalidatedSegments();
@@ -189,7 +190,7 @@ export class ScopeValidator {
                         continue;
                     }
                     this.currentSegmentBeingValidated = segment;
-                    this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, file: file, segment: segment, tag: ScopeValidatorDiagnosticTag.Segment });
+                    this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, fileUri: fileUri, segment: segment, tag: ScopeValidatorDiagnosticTag.Segment });
                     segmentsValidated++;
                     segment.walk(validationVisitor, {
                         walkMode: InsideSegmentWalkMode
@@ -279,23 +280,20 @@ export class ScopeValidator {
             const unquotedComponentName = componentName?.text?.replace(/"/g, '');
             if (unquotedComponentName && !platformNodeNames.has(unquotedComponentName.toLowerCase()) && !this.event.program.getComponent(unquotedComponentName)) {
                 this.addDiagnostic({
-                    file: file as BscFile,
                     ...DiagnosticMessages.unknownRoSGNode(unquotedComponentName),
-                    range: componentName.location?.range
+                    location: componentName.location
                 });
             } else if (call?.args.length !== 2) {
                 // roSgNode should only ever have 2 args in `createObject`
                 this.addDiagnostic({
-                    file: file as BscFile,
                     ...DiagnosticMessages.mismatchCreateObjectArgumentCount(firstParamStringValue, [2], call?.args.length),
-                    range: call.location?.range
+                    location: call.location
                 });
             }
         } else if (!platformComponentNames.has(firstParamStringValueLower)) {
             this.addDiagnostic({
-                file: file as BscFile,
                 ...DiagnosticMessages.unknownBrightScriptComponent(firstParamStringValue),
-                range: firstParamToken.location?.range
+                location: firstParamToken.location
             });
         } else {
             // This is valid brightscript component
@@ -310,18 +308,16 @@ export class ScopeValidator {
             if (!validArgCounts.includes(call?.args.length)) {
                 // Incorrect number of arguments included in `createObject()`
                 this.addDiagnostic({
-                    file: file as BscFile,
                     ...DiagnosticMessages.mismatchCreateObjectArgumentCount(firstParamStringValue, validArgCounts, call?.args.length),
-                    range: call.location?.range
+                    location: call.location
                 });
             }
 
             // Test for deprecation
             if (brightScriptComponent?.isDeprecated) {
                 this.addDiagnostic({
-                    file: file as BscFile,
                     ...DiagnosticMessages.deprecatedBrightScriptComponent(firstParamStringValue, brightScriptComponent.deprecatedDescription),
-                    range: call.location?.range
+                    location: call.location
                 });
             }
         }
@@ -361,9 +357,7 @@ export class ScopeValidator {
                 let minMaxParamsText = minParams === maxParams ? maxParams : `${minParams}-${maxParams}`;
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.mismatchArgumentCount(minMaxParamsText, expCallArgCount),
-                    range: expression.callee.location?.range,
-                    //TODO detect end of expression call
-                    file: file
+                    location: expression.callee.location
                 });
             }
             let paramIndex = 0;
@@ -390,9 +384,7 @@ export class ScopeValidator {
                 if (!isAllowedArgConversion && !paramType?.isTypeCompatible(argType, compatibilityData)) {
                     this.addMultiScopeDiagnostic({
                         ...DiagnosticMessages.argumentTypeMismatch(argType.toString(), paramType.toString(), compatibilityData),
-                        range: arg.location?.range,
-                        //TODO detect end of expression call
-                        file: file
+                        location: arg.location
                     });
                 }
                 paramIndex++;
@@ -422,8 +414,7 @@ export class ScopeValidator {
             if (actualReturnType && !funcType.returnType.isTypeCompatible(actualReturnType, compatibilityData)) {
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.returnTypeMismatch(actualReturnType.toString(), funcType.returnType.toString(), compatibilityData),
-                    range: returnStmt.value.location?.range,
-                    file: file
+                    location: returnStmt.value.location
                 });
 
             }
@@ -449,9 +440,8 @@ export class ScopeValidator {
         }
         if (!expectedLHSType || !expectedLHSType?.isResolvable()) {
             this.addMultiScopeDiagnostic({
-                file: file as BscFile,
                 ...DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem, typeChainScan.itemParentTypeName, this.getParentTypeDescriptor(typeChainScan)),
-                range: typeChainScan?.location?.range
+                location: typeChainScan?.location
             });
             return;
         }
@@ -470,8 +460,7 @@ export class ScopeValidator {
         if (accessibilityIsOk && !expectedLHSType?.isTypeCompatible(actualRHSType, compatibilityData)) {
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.assignmentTypeMismatch(actualRHSType.toString(), expectedLHSType.toString(), compatibilityData),
-                range: dottedSetStmt.location?.range,
-                file: file
+                location: dottedSetStmt.location
             });
         }
     }
@@ -495,8 +484,7 @@ export class ScopeValidator {
         } else if (!expectedLHSType?.isTypeCompatible(actualRHSType, compatibilityData)) {
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.assignmentTypeMismatch(actualRHSType.toString(), expectedLHSType.toString(), compatibilityData),
-                range: assignStmt.location?.range,
-                file: file
+                location: assignStmt.location
             });
         }
     }
@@ -556,8 +544,7 @@ export class ScopeValidator {
             // if the result was dynamic, that means there wasn't a valid operation
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.operatorTypeMismatch(binaryExpr.tokens.operator.text, leftType.toString(), rightType.toString()),
-                range: binaryExpr.location?.range,
-                file: file
+                location: binaryExpr.location
             });
         }
     }
@@ -592,16 +579,14 @@ export class ScopeValidator {
             if (isDynamicType(opResult)) {
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.operatorTypeMismatch(unaryExpr.tokens.operator.text, rightType.toString()),
-                    range: unaryExpr.location?.range,
-                    file: file
+                    location: unaryExpr.location
                 });
             }
         } else {
             // rhs is not a primitive, so no binary operator is allowed
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.operatorTypeMismatch(unaryExpr.tokens.operator.text, rightType.toString()),
-                range: unaryExpr.location?.range,
-                file: file
+                location: unaryExpr.location
             });
         }
     }
@@ -627,8 +612,7 @@ export class ScopeValidator {
             // rhs is not a number, so no increment operator is not allowed
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.operatorTypeMismatch(incStmt.tokens.operator.text, rightType.toString()),
-                range: incStmt.location?.range,
-                file: file
+                location: incStmt.location
             });
         }
     }
@@ -673,8 +657,7 @@ export class ScopeValidator {
         if (typeData.flags & SymbolTypeFlag.deprecated) { // eslint-disable-line no-bitwise
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.itemIsDeprecated(),
-                range: expression.tokens.name.location?.range,
-                file: file,
+                location: expression.tokens.name.location,
                 tags: [DiagnosticTag.Deprecated]
             });
         }
@@ -687,16 +670,14 @@ export class ScopeValidator {
                 if (isUsedAsType) {
                     this.addMultiScopeDiagnostic({
                         ...DiagnosticMessages.itemCannotBeUsedAsType(typeChainScan.fullChainName),
-                        range: expression.location?.range,
-                        file: file
+                        location: expression.location
                     });
                 } else if (invalidlyUsedResolvedType && !isReferenceType(invalidlyUsedResolvedType)) {
                     if (!isAliasStatement(expression.parent)) {
                         // alias rhs CAN be a type!
                         this.addMultiScopeDiagnostic({
                             ...DiagnosticMessages.itemCannotBeUsedAsVariable(invalidlyUsedResolvedType.toString()),
-                            range: expression.location?.range,
-                            file: file
+                            location: expression.location
                         });
                     }
                 } else {
@@ -704,15 +685,13 @@ export class ScopeValidator {
                     //if this is a function call, provide a different diganostic code
                     if (isCallExpression(typeChainScan.astNode.parent) && typeChainScan.astNode.parent.callee === expression) {
                         this.addMultiScopeDiagnostic({
-                            file: file as BscFile,
                             ...DiagnosticMessages.cannotFindFunction(typeChainScan.itemName, typeChainScan.fullNameOfItem, typeChainScan.itemParentTypeName, this.getParentTypeDescriptor(typeChainScan)),
-                            range: typeChainScan?.location?.range
+                            location: typeChainScan?.location
                         });
                     } else {
                         this.addMultiScopeDiagnostic({
-                            file: file as BscFile,
                             ...DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem, typeChainScan.itemParentTypeName, this.getParentTypeDescriptor(typeChainScan)),
-                            range: typeChainScan?.location?.range
+                            location: typeChainScan?.location
                         });
                     }
                 }
@@ -721,15 +700,13 @@ export class ScopeValidator {
                 const typeChainScan = util.processTypeChain(typeChain);
                 if (isCallExpression(typeChainScan.astNode.parent) && typeChainScan.astNode.parent.callee === expression) {
                     this.addMultiScopeDiagnostic({
-                        file: file as BscFile,
                         ...DiagnosticMessages.cannotFindFunction(typeChainScan.itemName, typeChainScan.fullNameOfItem, typeChainScan.itemParentTypeName, this.getParentTypeDescriptor(typeChainScan)),
-                        range: typeChainScan?.location?.range
+                        location: typeChainScan?.location
                     });
                 } else {
                     this.addMultiScopeDiagnostic({
-                        file: file as BscFile,
                         ...DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem, typeChainScan.itemParentTypeName, this.getParentTypeDescriptor(typeChainScan)),
-                        range: typeChainScan?.location?.range
+                        location: typeChainScan?.location
                     });
                 }
 
@@ -747,8 +724,7 @@ export class ScopeValidator {
 
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.itemCannotBeUsedAsVariable(classUsedAsVarEntry.toString()),
-                    range: expression.location?.range,
-                    file: file
+                    location: expression.location
                 });
                 return;
             }
@@ -762,8 +738,7 @@ export class ScopeValidator {
         if (isNamespaceType(exprType) && !isAliasStatement(expression.parent)) {
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.itemCannotBeUsedAsVariable('namespace'),
-                range: expression.location?.range,
-                file: file
+                location: expression.location
             });
         } else if (isEnumType(exprType) && !isAliasStatement(expression.parent)) {
             const enumStatement = this.event.scope.getEnum(util.getAllDottedGetPartsAsString(expression));
@@ -771,8 +746,7 @@ export class ScopeValidator {
                 // there's an enum with this name
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.itemCannotBeUsedAsVariable('enum'),
-                    range: expression.location?.range,
-                    file: file
+                    location: expression.location
                 });
             }
         } else if (isDynamicType(exprType) && isEnumType(parentTypeInfo?.type) && isDottedGetExpression(expression)) {
@@ -780,13 +754,12 @@ export class ScopeValidator {
             const typeChainScanForParent = util.processTypeChain(typeChain.slice(0, -1));
             if (enumFileLink) {
                 this.addMultiScopeDiagnostic({
-                    file: file,
                     ...DiagnosticMessages.unknownEnumValue(lastTypeInfo?.name, typeChainScanForParent.fullChainName),
-                    range: lastTypeInfo?.location?.range,
+                    location: lastTypeInfo?.location,
                     relatedInformation: [{
                         message: 'Enum declared here',
                         location: util.createLocationFromRange(
-                            URI.file(enumFileLink?.file.srcPath).toString(),
+                            util.pathToUri(enumFileLink?.file.srcPath),
                             enumFileLink?.item?.tokens.name.location?.range
                         )
                     }]
@@ -843,8 +816,7 @@ export class ScopeValidator {
                         if (!inMatchingClassStmt || childChainItem.data.memberOfAncestor) {
                             this.addMultiScopeDiagnostic({
                                 ...DiagnosticMessages.memberAccessibilityMismatch(childChainItem.name, childChainItem.data.flags, definingClassName),
-                                range: expression.location?.range,
-                                file: file
+                                location: expression.location
                             });
                             // there's an error... don't worry about the rest of the chain
                             return false;
@@ -861,8 +833,7 @@ export class ScopeValidator {
                         if (!isSubClassOfDefiningClass) {
                             this.addMultiScopeDiagnostic({
                                 ...DiagnosticMessages.memberAccessibilityMismatch(childChainItem.name, childChainItem.data.flags, definingClassName),
-                                range: expression.location?.range,
-                                file: file
+                                location: expression.location
                             });
                             // there's an error... don't worry about the rest of the chain
                             return false;
@@ -895,8 +866,7 @@ export class ScopeValidator {
 
             this.addMultiScopeDiagnostic({
                 ...DiagnosticMessages.expressionIsNotConstructable(fullName),
-                file: file,
-                range: newExpression.className.location?.range
+                location: newExpression.className.location
             });
 
         }
@@ -916,6 +886,7 @@ export class ScopeValidator {
             let ownCallables = [] as CallableContainer[];
             let ancestorNonGlobalCallables = [] as CallableContainer[];
 
+
             for (let container of callableContainers) {
                 if (container.scope === this.event.program.globalScope) {
                     globalCallables.push(container);
@@ -947,8 +918,7 @@ export class ScopeValidator {
                                 //grab the last item in the list, which should be the closest ancestor's version
                                 shadowedCallable.scope.name
                             ),
-                            range: container.callable.nameRange,
-                            file: container.callable.file
+                            location: util.createLocationFromFileRange(container.callable.file, container.callable.nameRange)
                         }, ScopeValidatorDiagnosticTag.DuplicateFunctionDeclaration);
                     }
                 }
@@ -966,7 +936,7 @@ export class ScopeValidator {
                             related.push({
                                 message: `Function declared here`,
                                 location: util.createLocationFromRange(
-                                    URI.file(ownCallable.callable.file?.srcPath).toString(),
+                                    util.pathToUri(ownCallable.callable.file?.srcPath),
                                     thatNameRange
                                 )
                             });
@@ -975,8 +945,7 @@ export class ScopeValidator {
 
                     this.addMultiScopeDiagnostic({
                         ...DiagnosticMessages.duplicateFunctionImplementation(callable.name),
-                        range: callable.nameRange,
-                        file: callable.file,
+                        location: util.createLocationFromFileRange(callable.file, callable.nameRange),
                         relatedInformation: related
                     }, ScopeValidatorDiagnosticTag.DuplicateFunctionDeclaration);
                 }
@@ -1009,15 +978,13 @@ export class ScopeValidator {
 
                 this.addMultiScopeDiagnostic({
                     ...dInfo,
-                    range: scriptImport.filePathRange,
-                    file: scriptImport.sourceFile
+                    location: util.createLocationFromFileRange(scriptImport.sourceFile, scriptImport.filePathRange)
                 }, ScopeValidatorDiagnosticTag.Imports);
                 //if the character casing of the script import path does not match that of the actual path
             } else if (scriptImport.destPath !== referencedFile.destPath) {
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.scriptImportCaseMismatch(referencedFile.destPath),
-                    range: scriptImport.filePathRange,
-                    file: scriptImport.sourceFile
+                    location: util.createLocationFromFileRange(scriptImport.sourceFile, scriptImport.filePathRange)
                 }, ScopeValidatorDiagnosticTag.Imports);
             }
         }
@@ -1043,7 +1010,8 @@ export class ScopeValidator {
      * Find various function collisions
      */
     private diagnosticDetectFunctionCollisions(file: BrsFile) {
-        this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, file: file, tag: ScopeValidatorDiagnosticTag.FunctionCollisions });
+        const fileUri = util.pathToUri(file.srcPath);
+        this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, fileUri: fileUri, tag: ScopeValidatorDiagnosticTag.FunctionCollisions });
         for (let func of file.callables) {
             const funcName = func.getName(ParseMode.BrighterScript);
             const lowerFuncName = funcName?.toLowerCase();
@@ -1053,8 +1021,7 @@ export class ScopeValidator {
                 if (globalCallableMap.has(lowerFuncName)) {
                     this.addMultiScopeDiagnostic({
                         ...DiagnosticMessages.scopeFunctionShadowedByBuiltInFunction(),
-                        range: func.nameRange,
-                        file: file
+                        location: util.createLocationFromRange(fileUri, func.nameRange)
 
                     });
                 }
@@ -1079,8 +1046,7 @@ export class ScopeValidator {
             if (varIsFunction()) {
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib'),
-                    range: varDeclaration.nameRange,
-                    file: file
+                    location: util.createLocationFromFileRange(file, varDeclaration.nameRange)
                 });
             }
         } else if (callableContainerMap.has(lowerVarName)) {
@@ -1089,12 +1055,11 @@ export class ScopeValidator {
             if (varIsFunction()) {
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.localVarFunctionShadowsParentFunction('scope'),
-                    range: varDeclaration.nameRange,
-                    file: file,
+                    location: util.createLocationFromFileRange(file, varDeclaration.nameRange),
                     relatedInformation: [{
                         message: 'Function declared here',
-                        location: util.createLocationFromRange(
-                            URI.file(callable[0].callable.file.srcPath).toString(),
+                        location: util.createLocationFromFileRange(
+                            callable[0].callable.file,
                             callable[0].callable.nameRange
                         )
                     }]
@@ -1102,12 +1067,11 @@ export class ScopeValidator {
             } else {
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.localVarShadowedByScopedFunction(),
-                    range: varDeclaration.nameRange,
-                    file: file,
+                    location: util.createLocationFromFileRange(file, varDeclaration.nameRange),
                     relatedInformation: [{
                         message: 'Function declared here',
                         location: util.createLocationFromRange(
-                            URI.file(callable[0].callable.file.srcPath).toString(),
+                            util.pathToUri(callable[0].callable.file.srcPath),
                             callable[0].callable.nameRange
                         )
                     }]
@@ -1119,12 +1083,11 @@ export class ScopeValidator {
             if (classStmtLink) {
                 this.addMultiScopeDiagnostic({
                     ...DiagnosticMessages.localVarSameNameAsClass(classStmtLink?.item?.getName(ParseMode.BrighterScript)),
-                    range: varDeclaration.nameRange,
-                    file: file,
+                    location: util.createLocationFromFileRange(file, varDeclaration.nameRange),
                     relatedInformation: [{
                         message: 'Class declared here',
                         location: util.createLocationFromRange(
-                            URI.file(classStmtLink.file.srcPath).toString(),
+                            util.pathToUri(classStmtLink.file.srcPath),
                             classStmtLink?.item.tokens.name.location?.range
                         )
                     }]
@@ -1137,7 +1100,7 @@ export class ScopeValidator {
         if (!scope.xmlFile.parser.ast?.componentElement?.interfaceElement) {
             return;
         }
-        this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, file: scope.xmlFile, tag: ScopeValidatorDiagnosticTag.XMLInterface });
+        this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, fileUri: util.pathToUri(scope.xmlFile?.srcPath), tag: ScopeValidatorDiagnosticTag.XMLInterface });
 
         const iface = scope.xmlFile.parser.ast.componentElement.interfaceElement;
         const callableContainerMap = scope.getCallableContainerMap();
@@ -1147,14 +1110,12 @@ export class ScopeValidator {
             if (!name) {
                 this.addDiagnostic({
                     ...DiagnosticMessages.xmlTagMissingAttribute(func.tokens.startTagName.text, 'name'),
-                    range: func.tokens.startTagName.location?.range,
-                    file: scope.xmlFile
+                    location: func.tokens.startTagName.location
                 }, ScopeValidatorDiagnosticTag.XMLInterface);
             } else if (!callableContainerMap.has(name.toLowerCase())) {
                 this.addDiagnostic({
                     ...DiagnosticMessages.xmlFunctionNotFound(name),
-                    range: func.getAttribute('name')?.tokens.value.location?.range,
-                    file: scope.xmlFile
+                    location: func.getAttribute('name')?.tokens.value.location
                 }, ScopeValidatorDiagnosticTag.XMLInterface);
             }
         }
@@ -1164,31 +1125,27 @@ export class ScopeValidator {
             if (!id) {
                 this.addDiagnostic({
                     ...DiagnosticMessages.xmlTagMissingAttribute(field.tokens.startTagName.text, 'id'),
-                    range: field.tokens.startTagName.location?.range,
-                    file: scope.xmlFile
+                    location: field.tokens.startTagName.location
                 }, ScopeValidatorDiagnosticTag.XMLInterface);
             }
             if (!type) {
                 if (!field.alias) {
                     this.addDiagnostic({
                         ...DiagnosticMessages.xmlTagMissingAttribute(field.tokens.startTagName.text, 'type'),
-                        range: field.tokens.startTagName.location?.range,
-                        file: scope.xmlFile
+                        location: field.tokens.startTagName.location
                     }, ScopeValidatorDiagnosticTag.XMLInterface);
                 }
             } else if (!SGFieldTypes.includes(type.toLowerCase())) {
                 this.addDiagnostic({
                     ...DiagnosticMessages.xmlInvalidFieldType(type),
-                    range: field.getAttribute('type')?.tokens.value.location?.range,
-                    file: scope.xmlFile
+                    location: field.getAttribute('type')?.tokens.value.location
                 }, ScopeValidatorDiagnosticTag.XMLInterface);
             }
             if (onChange) {
                 if (!callableContainerMap.has(onChange.toLowerCase())) {
                     this.addDiagnostic({
                         ...DiagnosticMessages.xmlFunctionNotFound(onChange),
-                        range: field.getAttribute('onchange')?.tokens.value.location?.range,
-                        file: scope.xmlFile
+                        location: field.getAttribute('onchange')?.tokens.value.location
                     }, ScopeValidatorDiagnosticTag.XMLInterface);
                 }
             }
@@ -1199,7 +1156,7 @@ export class ScopeValidator {
      * Detect when a child has imported a script that an ancestor also imported
      */
     private diagnosticDetectDuplicateAncestorScriptImports(scope: XmlScope) {
-        this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, file: scope.xmlFile, tag: ScopeValidatorDiagnosticTag.XMLImports });
+        this.event.program.diagnostics.clearByFilter({ scope: this.event.scope, fileUri: util.pathToUri(scope.xmlFile?.srcPath), tag: ScopeValidatorDiagnosticTag.XMLImports });
         if (scope.xmlFile.parentComponent) {
             //build a lookup of pkg paths -> FileReference so we can more easily look up collisions
             let parentScriptImports = scope.xmlFile.getAncestorScriptTagImports();
@@ -1218,8 +1175,7 @@ export class ScopeValidator {
                     let ancestorComponent = ancestorScriptImport.sourceFile as XmlFile;
                     let ancestorComponentName = ancestorComponent.componentName?.text ?? ancestorComponent.destPath;
                     this.addDiagnostic({
-                        file: scope.xmlFile,
-                        range: scriptImport.filePathRange,
+                        location: util.createLocationFromFileRange(scope.xmlFile, scriptImport.filePathRange),
                         ...DiagnosticMessages.unnecessaryScriptImportInChildFromParent(ancestorComponentName)
                     }, ScopeValidatorDiagnosticTag.XMLImports);
                 }
diff --git a/src/bscPlugin/validation/XmlFileValidator.ts b/src/bscPlugin/validation/XmlFileValidator.ts
index c3be21c8..ea519753 100644
--- a/src/bscPlugin/validation/XmlFileValidator.ts
+++ b/src/bscPlugin/validation/XmlFileValidator.ts
@@ -25,8 +25,7 @@ export class XmlFileValidator {
             //not a SG component
             this.event.program.diagnostics.register({
                 ...DiagnosticMessages.xmlComponentMissingComponentDeclaration(),
-                range: rootElement.location?.range,
-                file: this.event.file
+                location: rootElement.location
             });
             return;
         }
@@ -35,15 +34,13 @@ export class XmlFileValidator {
         if (!componentElement.name) {
             this.event.program.diagnostics.register({
                 ...DiagnosticMessages.xmlComponentMissingNameAttribute(),
-                range: componentElement.tokens.startTagName.location?.range,
-                file: this.event.file
+                location: componentElement.tokens.startTagName.location
             });
         }
         if (!componentElement.extends) {
             this.event.program.diagnostics.register({
                 ...DiagnosticMessages.xmlComponentMissingExtendsAttribute(),
-                range: componentElement.tokens.startTagName.location?.range,
-                file: this.event.file
+                location: componentElement.tokens.startTagName.location
             });
         }
 
@@ -56,8 +53,7 @@ export class XmlFileValidator {
         if (explicitCodebehindScriptTag) {
             this.event.program.diagnostics.register({
                 ...DiagnosticMessages.unnecessaryCodebehindScriptImport(),
-                file: this.event.file,
-                range: explicitCodebehindScriptTag.filePathRange
+                location: util.createLocationFromFileRange(this.event.file, explicitCodebehindScriptTag.filePathRange)
             });
         }
     }
diff --git a/src/diagnosticUtils.spec.ts b/src/diagnosticUtils.spec.ts
index 256ff212..73388567 100644
--- a/src/diagnosticUtils.spec.ts
+++ b/src/diagnosticUtils.spec.ts
@@ -4,7 +4,6 @@ import * as diagnosticUtils from './diagnosticUtils';
 import { Range, DiagnosticSeverity } from 'vscode-languageserver';
 import { util } from './util';
 import chalk from 'chalk';
-import { AssetFile } from './files/AssetFile';
 import { createSandbox } from 'sinon';
 import undent from 'undent';
 import type { BsDiagnostic } from './interfaces';
@@ -27,16 +26,15 @@ describe('diagnosticUtils', () => {
             //print a diagnostic that has an AssetFile. It should not explode
             diagnosticUtils.printDiagnostic(options, DiagnosticSeverity.Error, './temp/file.brs', [], {
                 message: 'Bad thing happened',
-                range: null, //important...this needs to be null for the test to pass,
-                code: 1234,
-                file: new AssetFile({ srcPath: 'src', destPath: 'dest' })
+                location: null, //important...this needs to be null for the test to pass,
+                code: 1234
             } as any);
         });
         it('does not crash when range is undefined', () => {
             //print a diagnostic that doesn't have a range...it should not explode
             diagnosticUtils.printDiagnostic(options, DiagnosticSeverity.Error, './temp/file.brs', [], {
                 message: 'Bad thing happened',
-                range: null, //important...this needs to be null for the test to pass,
+                location: null, //important...this needs to be null for the test to pass,
                 code: 1234
             } as any);
         });
@@ -45,7 +43,7 @@ describe('diagnosticUtils', () => {
             //print a diagnostic that doesn't have a range...it should not explode
             diagnosticUtils.printDiagnostic(options, DiagnosticSeverity.Error, undefined, [], {
                 message: 'Bad thing happened',
-                range: Range.create(0, 0, 2, 2),
+                location: Range.create(0, 0, 2, 2),
                 code: 1234
             } as any);
         });
@@ -252,7 +250,7 @@ describe('diagnosticUtils', () => {
 
         function testGetDiagnosticLine(range: Range, squigglyText: string, lineLength = 20) {
             expect(
-                diagnosticUtils.getDiagnosticLine({ range: range } as any, '1'.repeat(lineLength), color)
+                diagnosticUtils.getDiagnosticLine({ location: { range: range } } as any, '1'.repeat(lineLength), color)
             ).to.eql([
                 chalk.bgWhite(' ' + chalk.black((range.start.line + 1).toString()) + ' ') + ' ' + '1'.repeat(lineLength),
                 chalk.bgWhite(' ' + chalk.white(' '.repeat((range.start.line + 1).toString().length)) + ' ') + ' ' + squigglyText.padEnd(lineLength, ' ')
diff --git a/src/diagnosticUtils.ts b/src/diagnosticUtils.ts
index 3049b746..0fb043b3 100644
--- a/src/diagnosticUtils.ts
+++ b/src/diagnosticUtils.ts
@@ -76,8 +76,8 @@ export function printDiagnostic(
         chalk.cyan(filePath ?? '<unknown file>') +
         ':' +
         chalk.yellow(
-            diagnostic.range
-                ? (diagnostic.range.start.line + 1) + ':' + (diagnostic.range.start.character + 1)
+            diagnostic.location?.range
+                ? (diagnostic.location.range.start.line + 1) + ':' + (diagnostic.location.range.start.character + 1)
                 : 'line?:col?'
         ) +
         ' - ' +
@@ -91,7 +91,7 @@ export function printDiagnostic(
 
     //Get the line referenced by the diagnostic. if we couldn't find a line,
     // default to an empty string so it doesn't crash the error printing below
-    let diagnosticLine = lines[diagnostic.range?.start?.line ?? -1] ?? '';
+    let diagnosticLine = lines[diagnostic.location?.range?.start?.line ?? -1] ?? '';
     console.log(
         getDiagnosticLine(diagnostic, diagnosticLine, typeColor[severity])
     );
@@ -127,14 +127,14 @@ export function getDiagnosticLine(diagnostic: BsDiagnostic, diagnosticLine: stri
     let result = '';
 
     //only print the line information if we have some
-    if (diagnostic.range && diagnosticLine) {
-        const lineNumberText = chalk.bgWhite(' ' + chalk.black((diagnostic.range.start.line + 1).toString()) + ' ') + ' ';
-        const blankLineNumberText = chalk.bgWhite(' ' + chalk.white(' '.repeat((diagnostic.range.start.line + 1).toString().length)) + ' ') + ' ';
+    if (diagnostic.location?.range && diagnosticLine) {
+        const lineNumberText = chalk.bgWhite(' ' + chalk.black((diagnostic.location.range.start.line + 1).toString()) + ' ') + ' ';
+        const blankLineNumberText = chalk.bgWhite(' ' + chalk.white(' '.repeat((diagnostic.location.range.start.line + 1).toString().length)) + ' ') + ' ';
 
         //remove tabs in favor of spaces to make diagnostic printing more consistent
-        let leadingText = diagnosticLine.slice(0, diagnostic.range.start.character);
+        let leadingText = diagnosticLine.slice(0, diagnostic.location.range.start.character);
         let leadingTextNormalized = leadingText.replace(/\t/g, '    ');
-        let actualText = diagnosticLine.slice(diagnostic.range.start.character, diagnostic.range.end.character);
+        let actualText = diagnosticLine.slice(diagnostic.location.range.start.character, diagnostic.location.range.end.character);
         let actualTextNormalized = actualText.replace(/\t/g, '    ');
         let startIndex = leadingTextNormalized.length;
         let endIndex = leadingTextNormalized.length + actualTextNormalized.length;
diff --git a/src/files/BrsFile.Class.spec.ts b/src/files/BrsFile.Class.spec.ts
index 027d12dd..996aa50c 100644
--- a/src/files/BrsFile.Class.spec.ts
+++ b/src/files/BrsFile.Class.spec.ts
@@ -211,10 +211,10 @@ describe('BrsFile BrighterScript classes', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.classConstructorIllegalUseOfMBeforeSuperCall(),
-                range: Range.create(9, 24, 9, 25)
+                location: { range: Range.create(9, 24, 9, 25) }
             }, {
                 ...DiagnosticMessages.classConstructorIllegalUseOfMBeforeSuperCall(),
-                range: Range.create(9, 33, 9, 34)
+                location: { range: Range.create(9, 33, 9, 34) }
             }]);
         });
     });
@@ -1405,16 +1405,16 @@ describe('BrsFile BrighterScript classes', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.duplicateIdentifier('name'),
-            range: Range.create(3, 23, 3, 27)
+            location: { range: Range.create(3, 23, 3, 27) }
         }, {
             ...DiagnosticMessages.duplicateIdentifier('name'),
-            range: Range.create(4, 27, 4, 31)
+            location: { range: Range.create(4, 27, 4, 31) }
         }, {
             ...DiagnosticMessages.duplicateIdentifier('age'),
-            range: Range.create(8, 27, 8, 30)
+            location: { range: Range.create(8, 27, 8, 30) }
         }, {
             ...DiagnosticMessages.duplicateIdentifier('age'),
-            range: Range.create(10, 23, 10, 26)
+            location: { range: Range.create(10, 23, 10, 26) }
         }]);
     });
 
@@ -1565,7 +1565,7 @@ describe('BrsFile BrighterScript classes', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.cannotFindName('Animal'),
-                range: Range.create(1, 35, 1, 41)
+                location: { range: Range.create(1, 35, 1, 41) }
             }]);
         });
 
diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts
index 1db7f778..d43ed2c3 100644
--- a/src/files/BrsFile.spec.ts
+++ b/src/files/BrsFile.spec.ts
@@ -18,7 +18,6 @@ import type { FunctionStatement } from '../parser/Statement';
 import { ImportStatement } from '../parser/Statement';
 import { createToken, createVariableExpression } from '../astUtils/creators';
 import * as fsExtra from 'fs-extra';
-import { URI } from 'vscode-uri';
 import undent from 'undent';
 import { tempDir, rootDir } from '../testHelpers.spec';
 import { SymbolTypeFlag } from '../SymbolTypeFlag';
@@ -117,7 +116,7 @@ describe('BrsFile', () => {
     });
 
     it('flags namespaces used as variables', () => {
-        program.setFile('source/main.bs', `
+        const file = program.setFile('source/main.bs', `
             sub main()
                 alpha.beta.charlie.test()
                 print alpha
@@ -137,13 +136,13 @@ describe('BrsFile', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.itemCannotBeUsedAsVariable('namespace'),
-            range: util.createRange(3, 22, 3, 27)
+            location: util.createLocationFromFileRange(file, util.createRange(3, 22, 3, 27))
         }, {
             ...DiagnosticMessages.itemCannotBeUsedAsVariable('namespace'),
-            range: util.createRange(4, 22, 4, 32)
+            location: util.createLocationFromFileRange(file, util.createRange(4, 22, 4, 32))
         }, {
             ...DiagnosticMessages.itemCannotBeUsedAsVariable('namespace'),
-            range: util.createRange(5, 22, 5, 40)
+            location: util.createLocationFromFileRange(file, util.createRange(5, 22, 5, 40))
         }]);
     });
 
@@ -411,15 +410,14 @@ describe('BrsFile', () => {
                 `) as BrsFile;
                 program.diagnostics.register({
                     code: 'LINT1005',
-                    file: file,
                     message: 'Something is not right',
-                    range: util.createRange(2, 16, 2, 26)
+                    location: util.createLocationFromFileRange(file, util.createRange(2, 16, 2, 26))
                 });
                 expectZeroDiagnostics(program);
             });
 
             it('adds diagnostics for unknown numeric diagnostic codes', () => {
-                program.setFile('source/main.brs', `
+                const file = program.setFile('source/main.brs', `
                     sub main()
                         print "hi" 'bs:disable-line: 123456 999999   aaaab
                     end sub
@@ -428,10 +426,10 @@ describe('BrsFile', () => {
                 program.validate();
                 expectDiagnostics(program, [{
                     ...DiagnosticMessages.unknownDiagnosticCode(123456),
-                    range: Range.create(2, 53, 2, 59)
+                    location: util.createLocationFromFileRange(file, Range.create(2, 53, 2, 59))
                 }, {
                     ...DiagnosticMessages.unknownDiagnosticCode(999999),
-                    range: Range.create(2, 60, 2, 66)
+                    location: util.createLocationFromFileRange(file, Range.create(2, 60, 2, 66))
                 }]);
             });
 
@@ -456,7 +454,7 @@ describe('BrsFile', () => {
             });
 
             it('works for specific codes', () => {
-                program.setFile('source/main.brs', `
+                const file = program.setFile('source/main.brs', `
                     sub main()
                         'should not have any errors
                         DoSomething(1) 'bs:disable-line:1002
@@ -470,7 +468,7 @@ describe('BrsFile', () => {
                 program.validate();
 
                 expectDiagnostics(program, [{
-                    range: Range.create(5, 24, 5, 35)
+                    location: util.createLocationFromFileRange(file, Range.create(5, 24, 5, 35))
                 }]);
             });
 
@@ -1339,8 +1337,8 @@ describe('BrsFile', () => {
                 end function
             `);
             expectHasDiagnostics(program);
-            expect(program.getDiagnostics()[0].file).to.equal(file);
-            expect(program.getDiagnostics()[0].range.start.line).to.equal(1);
+            expect(program.getDiagnostics()[0].location.uri).to.equal(util.pathToUri(file?.srcPath));
+            expect(program.getDiagnostics()[0].location.range.start.line).to.equal(1);
         });
 
         it('supports using the `next` keyword in a for loop', () => {
@@ -4048,7 +4046,7 @@ describe('BrsFile', () => {
             program.validate();
             //print alpha.beta.char|lie
             expect(program.getDefinition(file.srcPath, Position.create(2, 41))).to.eql([{
-                uri: URI.file(file.srcPath).toString(),
+                uri: util.pathToUri(file.srcPath),
                 range: util.createRange(5, 26, 5, 33)
             }]);
         });
@@ -4067,7 +4065,7 @@ describe('BrsFile', () => {
             program.validate();
             //print alpha.beta.char|lie
             expect(program.getDefinition(file.srcPath, Position.create(2, 40))).to.eql([{
-                uri: URI.file(file.srcPath).toString(),
+                uri: util.pathToUri(file.srcPath),
                 range: util.createRange(5, 25, 5, 31)
             }]);
         });
@@ -4084,7 +4082,7 @@ describe('BrsFile', () => {
             program.validate();
             // sub test(selectedMovie as Mo|vie)
             expect(program.getDefinition(file.srcPath, Position.create(1, 44))).to.eql([{
-                uri: URI.file(file.srcPath).toString(),
+                uri: util.pathToUri(file.srcPath),
                 range: util.createRange(4, 26, 4, 31)
             }]);
         });
@@ -4103,7 +4101,7 @@ describe('BrsFile', () => {
             program.validate();
             //sub test(selectedMovie as interfaces.Mo|vie)
             expect(program.getDefinition(file.srcPath, Position.create(1, 55))).to.eql([{
-                uri: URI.file(file.srcPath).toString(),
+                uri: util.pathToUri(file.srcPath),
                 range: util.createRange(5, 30, 5, 35)
             }]);
         });
@@ -4120,7 +4118,7 @@ describe('BrsFile', () => {
             program.validate();
             //sub test(selectedMovie as Mo|vie)
             expect(program.getDefinition(file.srcPath, Position.create(1, 44))).to.eql([{
-                uri: URI.file(file.srcPath).toString(),
+                uri: util.pathToUri(file.srcPath),
                 range: util.createRange(4, 22, 4, 27)
             }]);
         });
@@ -4139,7 +4137,7 @@ describe('BrsFile', () => {
             program.validate();
             //sub test(selectedMovie as classes.Mo|vie)
             expect(program.getDefinition(file.srcPath, Position.create(1, 52))).to.eql([{
-                uri: URI.file(file.srcPath).toString(),
+                uri: util.pathToUri(file.srcPath),
                 range: util.createRange(5, 26, 5, 31)
             }]);
         });
@@ -4170,14 +4168,14 @@ describe('BrsFile', () => {
             program.validate();
             //print alpha.beta.char|lie
             expect(program.getDefinition(file.srcPath, Position.create(2, 48))).to.eql([{
-                uri: URI.file(file.srcPath).toString(),
+                uri: util.pathToUri(file.srcPath),
                 range: util.createRange(6, 24, 6, 31)
             }]);
         });
     });
 
     it('catches mismatched `end` keywords for functions', () => {
-        program.setFile('source/main.brs', `
+        const file = program.setFile('source/main.brs', `
             function speak()
             end sub
             sub walk()
@@ -4186,10 +4184,10 @@ describe('BrsFile', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.mismatchedEndCallableKeyword('function', 'sub'),
-            range: util.createRange(2, 12, 2, 19)
+            location: util.createLocationFromFileRange(file, util.createRange(2, 12, 2, 19))
         }, {
             ...DiagnosticMessages.mismatchedEndCallableKeyword('sub', 'function'),
-            range: util.createRange(4, 12, 4, 24)
+            location: util.createLocationFromFileRange(file, util.createRange(4, 12, 4, 24))
         }]);
     });
 
@@ -5271,7 +5269,7 @@ describe('BrsFile', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.tooManyCallableParameters(64, 63),
-            range: util.createRange(1, 638, 1, 641)
+            location: { range: util.createRange(1, 638, 1, 641) }
         }]);
     });
 
@@ -5283,10 +5281,10 @@ describe('BrsFile', () => {
         program.validate();
         expectDiagnostics(program, [{
             ...DiagnosticMessages.tooManyCallableParameters(65, 63),
-            range: util.createRange(1, 638, 1, 641)
+            location: { range: util.createRange(1, 638, 1, 641) }
         }, {
             ...DiagnosticMessages.tooManyCallableParameters(65, 63),
-            range: util.createRange(1, 648, 1, 651)
+            location: { range: util.createRange(1, 648, 1, 651) }
         }]);
     });
 
@@ -5315,7 +5313,7 @@ describe('BrsFile', () => {
             {
                 ...DiagnosticMessages.itemIsDeprecated(),
                 // url.|setPort|(80)
-                range: util.createRange(3, 20, 3, 27)
+                location: { range: util.createRange(3, 20, 3, 27) }
             }
         ]);
     });
diff --git a/src/files/BrsFile.ts b/src/files/BrsFile.ts
index 99479108..a7f0c6e8 100644
--- a/src/files/BrsFile.ts
+++ b/src/files/BrsFile.ts
@@ -345,7 +345,7 @@ export class BrsFile implements BscFile {
      * @param fileContents the raw source code to parse
      */
     public parse(fileContents: string) {
-        const diagnostics = [];
+        const diagnostics = [] as Array<BsDiagnostic>;
 
         try {
             this.fileContents = fileContents;
@@ -382,15 +382,11 @@ export class BrsFile implements BscFile {
                 ...this._parser.diagnostics as BsDiagnostic[]
             );
 
-            //attach this file to every diagnostic
-            for (let diagnostic of diagnostics) {
-                diagnostic.file = this;
-            }
+
         } catch (e) {
             this._parser = new Parser();
             diagnostics.push({
-                file: this,
-                range: util.createRange(0, 0, 0, Number.MAX_VALUE),
+                location: util.createLocationFromFileRange(this, util.createRange(0, 0, 0, Number.MAX_VALUE)),
                 ...DiagnosticMessages.genericParserMessage('Critical error parsing file: ' + JSON.stringify(serializeError(e)))
             });
         }
diff --git a/src/files/XmlFile.spec.ts b/src/files/XmlFile.spec.ts
index d6192b33..ed494fb6 100644
--- a/src/files/XmlFile.spec.ts
+++ b/src/files/XmlFile.spec.ts
@@ -7,7 +7,7 @@ import { DiagnosticMessages } from '../DiagnosticMessages';
 import type { BsDiagnostic, FileReference } from '../interfaces';
 import { Program } from '../Program';
 import { XmlFile } from './XmlFile';
-import { standardizePath as s } from '../util';
+import util, { standardizePath as s } from '../util';
 import { expectDiagnostics, expectDiagnosticsIncludes, expectZeroDiagnostics, getTestTranspile, trim, trimMap } from '../testHelpers.spec';
 import { ProgramBuilder } from '../ProgramBuilder';
 import { LogLevel } from '../logging';
@@ -159,16 +159,13 @@ describe('XmlFile', () => {
                     <script type="text/brightscript" uri="ChildScene.brs" />
                 </component>`
             );
-            const diagnostics = program.getDiagnostics();
-            expect(diagnostics).to.be.lengthOf(2);
-            expect(diagnostics[0]).to.deep.include({ // expecting opening tag but got prolog
+            expectDiagnostics(program, [{ // expecting opening tag but got prolog
                 code: DiagnosticMessages.xmlGenericParseError('').code,
-                range: Range.create(1, 16, 1, 22)
-            });
-            expect(diagnostics[1]).to.deep.include({
+                location: { range: Range.create(1, 16, 1, 22) }
+            }, {
                 ...DiagnosticMessages.xmlGenericParseError('Syntax error: whitespace found before the XML prolog'),
-                range: Range.create(0, 0, 1, 16)
-            });
+                location: { range: Range.create(0, 0, 1, 16) }
+            }]);
         });
 
         it('Adds error when an unknown tag is found in xml', () => {
@@ -182,16 +179,13 @@ describe('XmlFile', () => {
                     <unexpectedToo />
                 </component>
             `);
-            const diagnostics = program.getDiagnostics();
-            expect(diagnostics).to.be.lengthOf(2);
-            expect(diagnostics[0]).to.deep.include({
+            expectDiagnostics(program, [{
                 ...DiagnosticMessages.xmlUnexpectedTag('unexpected'),
-                range: Range.create(3, 9, 3, 19)
-            });
-            expect(diagnostics[1]).to.deep.include({
+                location: { range: Range.create(3, 9, 3, 19) }
+            }, {
                 ...DiagnosticMessages.xmlUnexpectedTag('unexpectedToo'),
-                range: Range.create(5, 5, 5, 18)
-            });
+                location: { range: Range.create(5, 5, 5, 18) }
+            }]);
         });
 
         it('Adds error when no component is declared in xml', () => {
@@ -200,7 +194,7 @@ describe('XmlFile', () => {
             expectDiagnostics(program, [
                 {
                     ...DiagnosticMessages.xmlUnexpectedTag('script'),
-                    range: Range.create(0, 1, 0, 7)
+                    location: { range: Range.create(0, 1, 0, 7) }
                 },
                 DiagnosticMessages.xmlComponentMissingComponentDeclaration()
             ]);
@@ -216,7 +210,7 @@ describe('XmlFile', () => {
             program.validate();
             expectDiagnosticsIncludes(program, [{
                 message: DiagnosticMessages.xmlComponentMissingNameAttribute().message,
-                range: Range.create(1, 1, 1, 10)
+                location: { range: Range.create(1, 1, 1, 10) }
             }]);
         });
 
@@ -232,7 +226,7 @@ describe('XmlFile', () => {
             expect(diagnostics[0].code).to.equal(DiagnosticMessages.xmlGenericParseError('').code); //unexpected character '1'
             expect(diagnostics[1]).to.deep.include(<BsDiagnostic>{
                 code: DiagnosticMessages.xmlComponentMissingNameAttribute().code,
-                range: Range.create(1, 1, 1, 10)
+                location: util.createLocationFromFileRange(file, Range.create(1, 1, 1, 10))
             });
         });
 
@@ -1045,9 +1039,8 @@ describe('XmlFile', () => {
                 for (const file of event.files) {
                     if (isXmlFile(file)) {
                         program.diagnostics.register({
-                            file: file,
                             message: 'Test diagnostic',
-                            range: Range.create(0, 0, 0, 0),
+                            location: util.createLocationFromFileRange(file, Range.create(0, 0, 0, 0)),
                             code: 9999
                         });
                     }
diff --git a/src/files/XmlFile.ts b/src/files/XmlFile.ts
index 373593e8..d1e2aeac 100644
--- a/src/files/XmlFile.ts
+++ b/src/files/XmlFile.ts
@@ -259,11 +259,8 @@ export class XmlFile implements BscFile {
             srcPath: this.srcPath,
             destPath: this.destPath
         });
-        const diagnostics = this.parser.diagnostics.map(diagnostic => ({
-            ...diagnostic,
-            file: this
-        }));
-        this.program?.diagnostics.register(diagnostics);
+
+        this.program?.diagnostics.register(this.parser.diagnostics);
         this.getCommentFlags(this.parser.tokens as any[]);
     }
 
diff --git a/src/interfaces.ts b/src/interfaces.ts
index d0ffd1d4..efa9863d 100644
--- a/src/interfaces.ts
+++ b/src/interfaces.ts
@@ -1,4 +1,4 @@
-import type { Range, Diagnostic, CodeAction, Position, CompletionItem, Location, DocumentSymbol, WorkspaceSymbol } from 'vscode-languageserver-protocol';
+import type { Range, CodeAction, Position, CompletionItem, Location, DocumentSymbol, WorkspaceSymbol, DiagnosticSeverity, CodeDescription, DiagnosticTag, DiagnosticRelatedInformation } from 'vscode-languageserver-protocol';
 import type { Scope } from './Scope';
 import type { BrsFile } from './files/BrsFile';
 import type { XmlFile } from './files/XmlFile';
@@ -23,10 +23,53 @@ import { TokenKind } from './lexer/TokenKind';
 import type { BscTypeKind } from './types/BscTypeKind';
 import { createToken } from './astUtils/creators';
 
-export interface BsDiagnostic extends Diagnostic {
-    file: BscFile;
+export interface BsDiagnostic {
+    /**
+     * The location at which the message applies
+     */
+    location: Location;
+    /**
+     * The diagnostic's severity. Can be omitted. If omitted it is up to the
+     * client to interpret diagnostics as error, warning, info or hint.
+     */
+    severity?: DiagnosticSeverity;
+    /**
+     * The diagnostic's code, which usually appear in the user interface.
+     */
+    code?: number | string;
+    /**
+     * An optional property to describe the error code.
+     * Requires the code field (above) to be present/not null.
+     *
+     * @since 3.16.0
+     */
+    codeDescription?: CodeDescription;
     /**
-     * A generic data container where additional details of the diagnostic can be stored. These are stripped out before being sent to a languageclient, and not printed to the console.
+     * A human-readable string describing the source of this
+     * diagnostic, e.g. 'typescript' or 'super lint'. It usually
+     * appears in the user interface.
+     */
+    source?: string;
+    /**
+     * The diagnostic's message. It usually appears in the user interface
+     */
+    message: string;
+    /**
+     * Additional metadata about the diagnostic.
+     *
+     * @since 3.15.0
+     */
+    tags?: DiagnosticTag[];
+    /**
+     * An array of related diagnostic information, e.g. when symbol-names within
+     * a scope collide all definitions can be marked via this property.
+     */
+    relatedInformation?: DiagnosticRelatedInformation[];
+    /**
+     * A data entry field that is preserved between a `textDocument/publishDiagnostics`
+     * notification and `textDocument/codeAction` request.
+     *
+     * @since 3.16.0
      */
     data?: any;
 }
diff --git a/src/lexer/Lexer.ts b/src/lexer/Lexer.ts
index 38229a4c..ad5f860a 100644
--- a/src/lexer/Lexer.ts
+++ b/src/lexer/Lexer.ts
@@ -2,9 +2,10 @@
 import { TokenKind, ReservedWords, Keywords, PreceedingRegexTypes, AllowedTriviaTokens } from './TokenKind';
 import type { Token } from './Token';
 import { isAlpha, isDecimalDigit, isAlphaNumeric, isHexDigit } from './Characters';
-import type { Location, Range, Diagnostic } from 'vscode-languageserver';
+import type { Location } from 'vscode-languageserver';
 import { DiagnosticMessages } from '../DiagnosticMessages';
 import util from '../util';
+import type { BsDiagnostic } from '../interfaces';
 
 export class Lexer {
     /**
@@ -50,7 +51,7 @@ export class Lexer {
     /**
      * The errors produced from `source.`
      */
-    public diagnostics: Diagnostic[];
+    public diagnostics: BsDiagnostic[];
 
     /**
      * The options used to scan this file
@@ -389,7 +390,7 @@ export class Lexer {
         } else {
             this.diagnostics.push({
                 ...DiagnosticMessages.unexpectedCharacter(c),
-                range: this.rangeOf()
+                location: this.locationOf()
             });
         }
     }
@@ -503,7 +504,7 @@ export class Lexer {
                 // BrightScript doesn't support multi-line strings
                 this.diagnostics.push({
                     ...DiagnosticMessages.unterminatedStringAtEndOfLine(),
-                    range: this.rangeOf()
+                    location: this.locationOf()
                 });
                 isUnterminated = true;
                 break;
@@ -516,7 +517,7 @@ export class Lexer {
             // terminating a string with EOF is also not allowed
             this.diagnostics.push({
                 ...DiagnosticMessages.unterminatedStringAtEndOfFile(),
-                range: this.rangeOf()
+                location: this.locationOf()
             });
             isUnterminated = true;
         }
@@ -637,7 +638,7 @@ export class Lexer {
 
                     this.diagnostics.push({
                         ...DiagnosticMessages.unexpectedConditionalCompilationString(),
-                        range: this.rangeOf()
+                        location: this.locationOf()
                     });
                 }
 
@@ -777,7 +778,7 @@ export class Lexer {
             this.advance(); // consume the "."
             this.diagnostics.push({
                 ...DiagnosticMessages.fractionalHexLiteralsAreNotSupported(),
-                range: this.rangeOf()
+                location: this.locationOf()
             });
             return;
         }
@@ -993,7 +994,7 @@ export class Lexer {
             default:
                 this.diagnostics.push({
                     ...DiagnosticMessages.unexpectedConditionalCompilationString(),
-                    range: this.rangeOf()
+                    location: this.locationOf()
                 });
         }
     }
@@ -1101,17 +1102,9 @@ export class Lexer {
         this.columnBegin = this.columnEnd;
     }
 
-    private rangeOf(): Range {
-        if (this.options.trackLocations) {
-            return util.createRange(this.lineBegin, this.columnBegin, this.lineEnd, this.columnEnd);
-        } else {
-            return undefined;
-        }
-    }
-
     /**
-     * Creates a `Range` at the lexer's current position
-     * @returns the range of `text`
+     * Creates a `Location` at the lexer's current position
+     * @returns the location of `text`
      */
     private locationOf(): Location {
         if (this.options.trackLocations) {
diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts
index 012c2f5b..320cc7d4 100644
--- a/src/parser/Parser.spec.ts
+++ b/src/parser/Parser.spec.ts
@@ -151,7 +151,7 @@ describe('parser', () => {
                 sub main()
                     call()a
                 end sub
-            `).diagnostics.map(x => rangeToArray(x.range))).to.eql([
+            `).diagnostics.map(x => rangeToArray(x.location.range))).to.eql([
                 [2, 26, 2, 27],
                 [2, 27, 2, 28]
             ]);
@@ -167,7 +167,7 @@ describe('parser', () => {
                 DiagnosticMessages.expectedStatementOrFunctionCallButReceivedExpression().message
             );
 
-            expect(diagnostics[0]?.range).to.eql(
+            expect(diagnostics[0]?.location.range).to.eql(
                 Range.create(3, 20, 3, 32)
             );
         });
diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts
index b0a5f51f..aad056db 100644
--- a/src/parser/Parser.ts
+++ b/src/parser/Parser.ts
@@ -98,13 +98,13 @@ import {
     VariableExpression,
     XmlAttributeGetExpression
 } from './Expression';
-import type { Diagnostic, Range } from 'vscode-languageserver';
+import type { Range } from 'vscode-languageserver';
 import type { Logger } from '../logging';
 import { createLogger } from '../logging';
 import { isAnnotationExpression, isCallExpression, isCallfuncExpression, isDottedGetExpression, isIfStatement, isIndexedGetExpression, isVariableExpression, isConditionalCompileStatement, isLiteralBoolean, isTypecastExpression } from '../astUtils/reflection';
 import { createStringLiteral, createToken } from '../astUtils/creators';
 import type { Expression, Statement } from './AstNode';
-import type { DeepWriteable } from '../interfaces';
+import type { BsDiagnostic, DeepWriteable } from '../interfaces';
 
 export class Parser {
     /**
@@ -139,7 +139,7 @@ export class Parser {
     /**
      * The list of diagnostics found during the parse process
      */
-    public diagnostics: Diagnostic[];
+    public diagnostics: BsDiagnostic[];
 
     /**
      * The depth of the calls to function declarations. Helps some checks know if they are at the root or not.
@@ -276,8 +276,8 @@ export class Parser {
         if (this.options.mode !== ParseMode.BrighterScript) {
             let diagnostic = {
                 ...DiagnosticMessages.bsFeatureNotSupportedInBrsFiles(featureName),
-                range: this.peek().location?.range
-            } as Diagnostic;
+                location: this.peek().location
+            };
             this.diagnostics.push(diagnostic);
         }
     }
@@ -404,7 +404,7 @@ export class Parser {
                 if (!ignoreDiagnostics) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.expectedIdentifierAfterKeyword(asToken.text),
-                        range: asToken.location?.range
+                        location: asToken.location
                     });
                 }
                 //consume the statement separator
@@ -413,7 +413,7 @@ export class Parser {
                 if (!ignoreDiagnostics) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.expectedIdentifierAfterKeyword(asToken.text),
-                        range: asToken.location?.range
+                        location: asToken.location
                     });
                 }
             } else {
@@ -437,7 +437,7 @@ export class Parser {
                 if (params.length >= CallExpression.MaximumArguments) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.tooManyCallableParameters(params.length, CallExpression.MaximumArguments),
-                        range: this.peek().location?.range
+                        location: this.peek().location
                     });
                 }
 
@@ -484,7 +484,7 @@ export class Parser {
             if (this.checkEndOfStatement()) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.expectedIdentifierAfterKeyword(extendsToken.text),
-                    range: extendsToken.location?.range
+                    location: extendsToken.location
                 });
             } else {
                 parentInterfaceName = this.typeExpression();
@@ -639,7 +639,7 @@ export class Parser {
             if (this.checkEndOfStatement()) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.expectedIdentifierAfterKeyword(extendsKeyword.text),
-                    range: extendsKeyword.location?.range
+                    location: extendsKeyword.location
                 });
             } else {
                 parentClassName = this.typeExpression();
@@ -677,7 +677,7 @@ export class Parser {
                     if (overrideKeyword && funcDeclaration.tokens.name.text.toLowerCase() === 'new') {
                         this.diagnostics.push({
                             ...DiagnosticMessages.cannotUseOverrideKeywordOnConstructorFunction(),
-                            range: overrideKeyword.location?.range
+                            location: overrideKeyword.location
                         });
                     }
 
@@ -697,7 +697,7 @@ export class Parser {
                     if (overrideKeyword) {
                         this.diagnostics.push({
                             ...DiagnosticMessages.classFieldCannotBeOverridden(),
-                            range: overrideKeyword.location?.range
+                            location: overrideKeyword.location
                         });
                     }
 
@@ -720,7 +720,7 @@ export class Parser {
         if (endingKeyword.kind !== TokenKind.EndClass) {
             this.diagnostics.push({
                 ...DiagnosticMessages.couldNotFindMatchingEndKeyword('class'),
-                range: endingKeyword.location?.range
+                location: endingKeyword.location
             });
         }
 
@@ -815,7 +815,7 @@ export class Parser {
             } else {
                 this.diagnostics.push({
                     ...DiagnosticMessages.missingCallableKeyword(),
-                    range: this.peek().location?.range
+                    location: this.peek().location
                 });
                 //TODO we should probably eliminate this entirely, since it's not present in the source code
                 functionType = {
@@ -855,7 +855,7 @@ export class Parser {
                     //don't throw this error; let the parser continue
                     this.diagnostics.push({
                         ...DiagnosticMessages.functionNameCannotEndWithTypeDesignator(functionTypeText, name.text, lastChar),
-                        range: name.location?.range
+                        location: name.location
                     });
                 }
 
@@ -863,7 +863,7 @@ export class Parser {
                 if (checkIdentifier && DisallowedFunctionIdentifiersText.has(name.text.toLowerCase())) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.cannotUseReservedWordAsIdentifier(name.text),
-                        range: name.location?.range
+                        location: name.location
                     });
                 }
             }
@@ -886,7 +886,7 @@ export class Parser {
                 if (haveFoundOptional && !param.defaultValue) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.requiredParameterMayNotFollowOptionalParameter(param.tokens.name.text),
-                        range: param.location?.range
+                        location: param.location
                     });
                 }
 
@@ -909,7 +909,7 @@ export class Parser {
             if (endFunctionType.kind !== expectedEndKind) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.mismatchedEndCallableKeyword(functionTypeText, endFunctionType.text),
-                    range: endFunctionType.location?.range
+                    location: endFunctionType.location
                 });
             }
 
@@ -945,7 +945,7 @@ export class Parser {
         if (!this.checkAny(TokenKind.Identifier, ...this.allowedLocalIdentifiers)) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedParameterNameButFound(this.peek().text),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -983,7 +983,7 @@ export class Parser {
         if (DisallowedLocalIdentifiersText.has(name.text.toLowerCase())) {
             this.diagnostics.push({
                 ...DiagnosticMessages.cannotUseReservedWordAsIdentifier(name.text),
-                range: name.location?.range
+                location: name.location
             });
         }
         let asToken: Token;
@@ -1218,7 +1218,7 @@ export class Parser {
         if (!whileBlock || this.peek().kind !== TokenKind.EndWhile) {
             this.diagnostics.push({
                 ...DiagnosticMessages.couldNotFindMatchingEndKeyword('while'),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             if (!whileBlock) {
                 throw this.lastDiagnosticAsError();
@@ -1266,7 +1266,7 @@ export class Parser {
         if (!body || !this.checkAny(TokenKind.EndFor, TokenKind.Next)) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedEndForOrNextToTerminateForLoop(),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             if (!body) {
                 throw this.lastDiagnosticAsError();
@@ -1299,7 +1299,7 @@ export class Parser {
         } else {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedInAfterForEach(name.text),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -1309,7 +1309,7 @@ export class Parser {
         if (!target) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedExpressionAfterForEachIn(),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -1320,7 +1320,7 @@ export class Parser {
         if (!body) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedEndForOrNextToTerminateForLoop(),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -1363,7 +1363,7 @@ export class Parser {
             //the `end namespace` keyword is missing. add a diagnostic, but keep parsing
             this.diagnostics.push({
                 ...DiagnosticMessages.couldNotFindMatchingEndKeyword('namespace'),
-                range: keyword.location?.range
+                location: keyword.location
             });
         }
 
@@ -1435,7 +1435,7 @@ export class Parser {
             let token = this.advance();
             this.diagnostics.push({
                 ...DiagnosticMessages.unexpectedToken(token.text),
-                range: token.location?.range
+                location: token.location
             });
         }
     }
@@ -1508,7 +1508,10 @@ export class Parser {
         }
         this.diagnostics.push({
             ...DiagnosticMessages.expectedIdentifierAfterKeyword('typecast'),
-            range: util.createBoundingRange(typecastToken, this.peek())
+            location: {
+                uri: typecastToken.location.uri,
+                range: util.createBoundingRange(typecastToken, this.peek())
+            }
         });
         throw this.lastDiagnosticAsError();
     }
@@ -1661,7 +1664,10 @@ export class Parser {
                 } else {
                     this.diagnostics.push({
                         ...DiagnosticMessages.unterminatedTemplateExpression(),
-                        range: util.createBoundingRange(openingBacktick, this.peek())
+                        location: {
+                            uri: openingBacktick.location.uri,
+                            range: util.createBoundingRange(openingBacktick, this.peek())
+                        }
                     });
                     throw this.lastDiagnosticAsError();
                 }
@@ -1677,7 +1683,10 @@ export class Parser {
             //error - missing backtick
             this.diagnostics.push({
                 ...DiagnosticMessages.unterminatedTemplateStringAtEndOfFile(),
-                range: util.createBoundingRange(openingBacktick, this.peek())
+                location: {
+                    uri: openingBacktick.location.uri,
+                    range: util.createBoundingRange(openingBacktick, this.peek())
+                }
             });
             throw this.lastDiagnosticAsError();
 
@@ -1715,7 +1724,7 @@ export class Parser {
         if (peek.kind !== TokenKind.Catch) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedCatchBlockInTryCatch(),
-                range: this.peek()?.location?.range
+                location: this.peek()?.location
             });
         } else {
             const catchToken = this.advance();
@@ -1736,7 +1745,7 @@ export class Parser {
         if (this.peek().kind !== TokenKind.EndTry) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedEndTryToTerminateTryCatch(),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
         } else {
             endTryToken = this.advance();
@@ -1758,7 +1767,7 @@ export class Parser {
         if (this.checkAny(TokenKind.Newline, TokenKind.Colon)) {
             this.diagnostics.push({
                 ...DiagnosticMessages.missingExceptionExpressionAfterThrowKeyword(),
-                range: throwToken.location?.range
+                location: throwToken.location
             });
         } else {
             expression = this.expression();
@@ -1796,7 +1805,7 @@ export class Parser {
         if (expressions.length === 0) {
             this.diagnostics.push({
                 ...DiagnosticMessages.missingExpressionsInDimStatement(),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
         }
         let rightSquareBracket = this.tryConsume(DiagnosticMessages.missingRightSquareBracketAfterDimIdentifier(), TokenKind.RightSquareBracket);
@@ -1819,7 +1828,7 @@ export class Parser {
                 if (this.current > 1 && this.tokens[this.current - 2].kind !== TokenKind.Then && this.nestedInlineConditionalCount === 0) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.unexpectedColonBeforeIfStatement(),
-                        range: prev.location?.range
+                        location: prev.location
                     });
                 }
             }
@@ -1854,7 +1863,7 @@ export class Parser {
             if (!thenBranch) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.expectedStatementToFollowConditionalCondition(ifToken.text),
-                    range: this.peek().location?.range
+                    location: this.peek().location
                 });
                 throw this.lastDiagnosticAsError();
             } else {
@@ -1873,7 +1882,7 @@ export class Parser {
                     if (!elseBranch.isInline) {
                         this.diagnostics.push({
                             ...DiagnosticMessages.expectedInlineIfStatement(),
-                            range: elseBranch.location?.range
+                            location: elseBranch.location
                         });
                     }
 
@@ -1881,7 +1890,7 @@ export class Parser {
                     //expecting inline else branch
                     this.diagnostics.push({
                         ...DiagnosticMessages.expectedInlineIfStatement(),
-                        range: this.peek().location?.range
+                        location: this.peek().location
                     });
                     throw this.lastDiagnosticAsError();
                 } else {
@@ -1896,7 +1905,7 @@ export class Parser {
                     //missing `else` branch
                     this.diagnostics.push({
                         ...DiagnosticMessages.expectedStatementToFollowElse(),
-                        range: this.peek().location?.range
+                        location: this.peek().location
                     });
                     throw this.lastDiagnosticAsError();
                 }
@@ -1914,7 +1923,7 @@ export class Parser {
                     //newline is required
                     this.diagnostics.push({
                         ...DiagnosticMessages.expectedFinalNewline(),
-                        range: this.peek().location?.range
+                        location: this.peek().location
                     });
                 }
             }
@@ -1951,7 +1960,7 @@ export class Parser {
                     //missing endif
                     this.diagnostics.push({
                         ...DiagnosticMessages.expectedEndIfToCloseIfStatement(ifToken.location?.range.start),
-                        range: ifToken.location?.range
+                        location: ifToken.location
                     });
                 }
             }
@@ -1987,7 +1996,7 @@ export class Parser {
             //this whole if statement is bogus...add error to the if token and hard-fail
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedEndIfElseIfOrElseToTerminateThenBlock(),
-                range: ifToken.location?.range
+                location: ifToken.location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -2005,7 +2014,7 @@ export class Parser {
         if (!this.checkAny(TokenKind.True, TokenKind.False, TokenKind.Identifier)) {
             this.diagnostics.push({
                 ...DiagnosticMessages.invalidHashIfValue(),
-                range: this.peek()?.location?.range
+                location: this.peek()?.location
             });
         }
 
@@ -2060,7 +2069,7 @@ export class Parser {
                 //missing #endif
                 this.diagnostics.push({
                     ...DiagnosticMessages.expectedHashEndIfToCloseHashIf(hashIfToken.location?.range.start.line),
-                    range: hashIfToken.location?.range
+                    location: hashIfToken.location
                 });
             }
         }
@@ -2094,7 +2103,7 @@ export class Parser {
             //this whole if statement is bogus...add error to the if token and hard-fail
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedTerminatorOnConditionalCompileBlock(),
-                range: hashIfToken.location?.range
+                location: hashIfToken.location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -2170,7 +2179,7 @@ export class Parser {
             ) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.unsafeUnmatchedTerminatorInConditionalCompileBlock(peek.text),
-                    range: peek.location?.range
+                    location: peek.location
                 });
                 throw this.lastDiagnosticAsError();
             } else {
@@ -2189,7 +2198,7 @@ export class Parser {
         if (ReservedWords.has(constName?.text.toLowerCase())) {
             this.diagnostics.push({
                 ...DiagnosticMessages.constNameCannotBeReservedWord(),
-                range: constName?.location?.range
+                location: constName?.location
             });
 
             this.lastDiagnosticAsError();
@@ -2201,7 +2210,7 @@ export class Parser {
             if (assignment.tokens.as || assignment.typeExpression) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.unexpectedToken(assignment.tokens.as?.text || assignment.typeExpression?.getName(ParseMode.BrighterScript)),
-                    range: assignment.tokens.as?.location?.range ?? assignment.typeExpression?.location?.range
+                    location: assignment.tokens.as?.location ?? assignment.typeExpression?.location
                 });
                 this.lastDiagnosticAsError();
             }
@@ -2212,7 +2221,7 @@ export class Parser {
             } else {
                 this.diagnostics.push({
                     ...DiagnosticMessages.invalidHashConstValue(),
-                    range: assignment.value.location?.range
+                    location: assignment.value.location
                 });
                 this.lastDiagnosticAsError();
             }
@@ -2223,7 +2232,7 @@ export class Parser {
         if (!this.check(TokenKind.Newline)) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedNewlineInConditionalCompile(),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -2243,7 +2252,7 @@ export class Parser {
         if (!this.check(TokenKind.Newline)) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedNewlineInConditionalCompile(),
-                range: this.peek().location?.range
+                location: this.peek().location
             });
             throw this.lastDiagnosticAsError();
         }
@@ -2255,7 +2264,7 @@ export class Parser {
             if (!silent) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.expectedNewlineOrColon(),
-                    range: this.peek().location?.range
+                    location: this.peek().location
                 });
             }
             return false;
@@ -2269,7 +2278,7 @@ export class Parser {
             if (isIfStatement(stat) && !stat.isInline) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.expectedInlineIfStatement(),
-                    range: stat.location?.range
+                    location: stat.location
                 });
             }
         }
@@ -2306,7 +2315,7 @@ export class Parser {
                 const colon = this.previous();
                 this.diagnostics.push({
                     ...DiagnosticMessages.unexpectedToken(colon.text),
-                    range: colon.location?.range
+                    location: colon.location
                 });
             }
         }
@@ -2322,13 +2331,13 @@ export class Parser {
             if (this.checkAny(TokenKind.PlusPlus, TokenKind.MinusMinus)) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.consecutiveIncrementDecrementOperatorsAreNotAllowed(),
-                    range: this.peek().location?.range
+                    location: this.peek().location
                 });
                 throw this.lastDiagnosticAsError();
             } else if (isCallExpression(expr)) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.incrementDecrementOperatorsAreNotAllowedAsResultOfFunctionCall(),
-                    range: expressionStart.location?.range
+                    location: expressionStart.location
                 });
                 throw this.lastDiagnosticAsError();
             }
@@ -2344,7 +2353,7 @@ export class Parser {
         //at this point, it's probably an error. However, we recover a little more gracefully by creating an inclosing ExpressionStatement
         this.diagnostics.push({
             ...DiagnosticMessages.expectedStatementOrFunctionCallButReceivedExpression(),
-            range: expressionStart.location?.range
+            location: expressionStart.location
         });
         return new ExpressionStatement({ expression: expr });
     }
@@ -2596,7 +2605,7 @@ export class Parser {
             for (const annotation of this.pendingAnnotations) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.unusedAnnotation(),
-                    range: annotation.location?.range
+                    location: annotation.location
                 });
             }
         }
@@ -2902,7 +2911,7 @@ export class Parser {
                 if (args.length >= CallExpression.MaximumArguments) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.tooManyCallableArguments(args.length, CallExpression.MaximumArguments),
-                        range: this.peek()?.location?.range
+                        location: this.peek()?.location
                     });
                     throw this.lastDiagnosticAsError();
                 }
@@ -3076,7 +3085,7 @@ export class Parser {
                 } else {
                     this.diagnostics.push({
                         ...DiagnosticMessages.unexpectedToken(this.peek().text),
-                        range: this.peek()?.location?.range
+                        location: this.peek()?.location
                     });
                     throw this.lastDiagnosticAsError();
                 }
@@ -3140,7 +3149,7 @@ export class Parser {
             } else {
                 this.diagnostics.push({
                     ...DiagnosticMessages.unexpectedAAKey(),
-                    range: this.peek().location?.range
+                    location: this.peek().location
                 });
                 throw this.lastDiagnosticAsError();
             }
@@ -3286,7 +3295,7 @@ export class Parser {
         }
         this.diagnostics.push({
             ...diagnostic,
-            range: this.peek()?.location?.range
+            location: this.peek()?.location
         });
     }
 
@@ -3310,7 +3319,7 @@ export class Parser {
         if (!optional && !consumed) {
             this.diagnostics.push({
                 ...DiagnosticMessages.expectedNewlineOrColon(),
-                range: this.peek()?.location?.range
+                location: this.peek()?.location
             });
         }
         return consumed;
diff --git a/src/parser/SGParser.spec.ts b/src/parser/SGParser.spec.ts
index 03609de5..9c70b6c4 100644
--- a/src/parser/SGParser.spec.ts
+++ b/src/parser/SGParser.spec.ts
@@ -1,7 +1,7 @@
 import { expect } from '../chai-config.spec';
 import { Range } from 'vscode-languageserver';
 import { DiagnosticMessages } from '../DiagnosticMessages';
-import { getTestTranspile, trim } from '../testHelpers.spec';
+import { expectDiagnostics, getTestTranspile, trim } from '../testHelpers.spec';
 import SGParser from './SGParser';
 import { createSandbox } from 'sinon';
 import { Program } from '../Program';
@@ -102,14 +102,13 @@ describe('SGParser', () => {
             </component>
         `);
         expect(parser.diagnostics).to.be.lengthOf(2);
-        expect(parser.diagnostics[0]).to.deep.include({
+        expectDiagnostics(parser, [{
             ...DiagnosticMessages.xmlUnexpectedTag('unexpected'),
-            range: Range.create(3, 9, 3, 19)
-        });
-        expect(parser.diagnostics[1]).to.deep.include({
+            location: { range: Range.create(3, 9, 3, 19) }
+        }, {
             ...DiagnosticMessages.xmlUnexpectedTag('unexpectedToo'),
-            range: Range.create(5, 5, 5, 18)
-        });
+            location: { range: Range.create(5, 5, 5, 18) }
+        }]);
     });
 
     it('Adds error when a leaf tag is found to have children', () => {
@@ -127,15 +126,13 @@ describe('SGParser', () => {
                 </script>
             </component>
         `);
-        expect(parser.diagnostics).to.be.lengthOf(2);
-        expect(parser.diagnostics[0]).to.deep.include({
+        expectDiagnostics(parser, [{
             ...DiagnosticMessages.xmlUnexpectedChildren('field'),
-            range: Range.create(3, 9, 3, 14)
-        });
-        expect(parser.diagnostics[1]).to.deep.include({
+            location: { range: Range.create(3, 9, 3, 14) }
+        }, {
             ...DiagnosticMessages.xmlUnexpectedChildren('script'),
-            range: Range.create(7, 5, 7, 11)
-        });
+            location: { range: Range.create(7, 5, 7, 11) }
+        }]);
     });
 
     it('Adds error when whitespace appears before the prolog', () => {
@@ -146,14 +143,12 @@ describe('SGParser', () => {
                 <script type="text/brightscript" uri="ChildScene.brs" />
             </component>`
         );
-        expect(parser.diagnostics).to.be.lengthOf(2);
-        expect(parser.diagnostics[0]).to.deep.include({ // expecting opening tag but got prolog
+        expectDiagnostics(parser, [{ // expecting opening tag but got prolog
             code: DiagnosticMessages.xmlGenericParseError('').code,
-            range: Range.create(1, 12, 1, 18)
-        });
-        expect(parser.diagnostics[1]).to.deep.include({
+            location: { range: Range.create(1, 12, 1, 18) }
+        }, {
             ...DiagnosticMessages.xmlGenericParseError('Syntax error: whitespace found before the XML prolog'),
-            range: Range.create(0, 0, 1, 12)
-        });
+            location: { range: Range.create(0, 0, 1, 12) }
+        }]);
     });
 });
diff --git a/src/parser/SGParser.ts b/src/parser/SGParser.ts
index 7b3c68c1..4a0e09d3 100644
--- a/src/parser/SGParser.ts
+++ b/src/parser/SGParser.ts
@@ -1,11 +1,12 @@
 import type { AttributeCstNode, ContentCstNode, DocumentCstNode, ElementCstNode } from '@xml-tools/parser';
 import * as parser from '@xml-tools/parser';
 import { DiagnosticMessages } from '../DiagnosticMessages';
-import type { Diagnostic } from 'vscode-languageserver';
 import util from '../util';
 import { SGAst, SGProlog, SGChildren, SGComponent, SGInterfaceField, SGInterfaceFunction, SGInterface, SGNode, SGScript, SGAttribute } from './SGTypes';
 import type { SGElement, SGToken, SGReferences } from './SGTypes';
 import { isSGComponent } from '../astUtils/xml';
+import type { BsDiagnostic } from '../interfaces';
+import type { Location, Range } from 'vscode-languageserver';
 
 export default class SGParser {
 
@@ -19,7 +20,7 @@ export default class SGParser {
     /**
      * The list of diagnostics found during the parse process
      */
-    public diagnostics = [] as Diagnostic[];
+    public diagnostics = [] as BsDiagnostic[];
 
     /**
      * The options used to parse the file
@@ -99,12 +100,12 @@ export default class SGParser {
             for (const err of lexErrors) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.xmlGenericParseError(`Syntax error: ${err.message}`),
-                    range: util.createRange(
+                    location: this.getLocationFromRange(util.createRange(
                         err.line - 1,
                         err.column,
                         err.line - 1,
                         err.column + err.length
-                    )
+                    ))
                 });
             }
         }
@@ -113,7 +114,7 @@ export default class SGParser {
             const token = err.token;
             this.diagnostics.push({
                 ...DiagnosticMessages.xmlGenericParseError(`Syntax error: ${err.message}`),
-                range: !isNaN(token.startLine) ? this.rangeFromToken(token) : util.createRange(0, 0, 0, Number.MAX_VALUE)
+                location: this.getLocationFromRange(!isNaN(token.startLine) ? this.rangeFromToken(token) : util.createRange(0, 0, 0, Number.MAX_VALUE))
             });
         }
 
@@ -128,7 +129,7 @@ export default class SGParser {
             ) {
                 this.diagnostics.push({
                     ...DiagnosticMessages.xmlGenericParseError('Syntax error: whitespace found before the XML prolog'),
-                    range: this.rangeFromToken(token1)
+                    location: this.getLocationFromRange(this.rangeFromToken(token1))
                 });
             }
         }
@@ -140,7 +141,7 @@ export default class SGParser {
                 //error: not a component
                 this.diagnostics.push({
                     ...DiagnosticMessages.xmlUnexpectedTag(root.tokens.startTagName.text),
-                    range: root.tokens.startTagName.location?.range
+                    location: root.tokens.startTagName.location
                 });
             }
             this.ast = new SGAst({ prologElement: prolog, rootElement: root });
@@ -207,7 +208,7 @@ export default class SGParser {
             case 'field':
                 if (this.hasElements(content)) {
                     this.diagnostics.push({
-                        range: startTagName.location?.range,
+                        location: startTagName.location,
                         ...DiagnosticMessages.xmlUnexpectedChildren(startTagName.text)
                     });
                 }
@@ -215,7 +216,7 @@ export default class SGParser {
             case 'function':
                 if (this.hasElements(content)) {
                     this.diagnostics.push({
-                        range: startTagName.location?.range,
+                        location: startTagName.location,
                         ...DiagnosticMessages.xmlUnexpectedChildren(startTagName.text)
                     });
                 }
@@ -223,7 +224,7 @@ export default class SGParser {
             case 'script':
                 if (this.hasElements(content)) {
                     this.diagnostics.push({
-                        range: startTagName.location?.range,
+                        location: startTagName.location,
                         ...DiagnosticMessages.xmlUnexpectedChildren(startTagName.text)
                     });
                 }
@@ -272,7 +273,7 @@ export default class SGParser {
                         //unexpected tag
                         this.diagnostics.push({
                             ...DiagnosticMessages.xmlUnexpectedTag(name.image),
-                            range: this.rangeFromToken(name)
+                            location: this.getLocationFromRange(this.rangeFromToken(name))
                         });
                     }
                     tags.push(this.mapElement(entry));
@@ -390,6 +391,10 @@ export default class SGParser {
             scriptTagImports: []
         };
     }
+
+    private getLocationFromRange(range: Range): Location {
+        return util.createLocationFromRange(util.pathToUri(this.options.srcPath), range);
+    }
 }
 
 //not exposed by @xml-tools/parser
diff --git a/src/util.spec.ts b/src/util.spec.ts
index 236b5301..e475ea41 100644
--- a/src/util.spec.ts
+++ b/src/util.spec.ts
@@ -7,8 +7,6 @@ import * as fsExtra from 'fs-extra';
 import { createSandbox } from 'sinon';
 import { DiagnosticMessages } from './DiagnosticMessages';
 import { tempDir, rootDir, expectTypeToBe } from './testHelpers.spec';
-import { Program } from './Program';
-import type { BsDiagnostic } from './interfaces';
 import { TypeChainEntry } from './interfaces';
 import { NamespaceType } from './types/NamespaceType';
 import { ClassType } from './types/ClassType';
@@ -21,7 +19,6 @@ import { createDottedIdentifier, createVariableExpression } from './astUtils/cre
 import { Parser } from './parser/Parser';
 import type { FunctionStatement } from './parser/Statement';
 import { ComponentType } from './types/ComponentType';
-import type { BrsFile } from './files/BrsFile';
 
 const sinon = createSandbox();
 
@@ -73,31 +70,6 @@ describe('util', () => {
         });
     });
 
-    describe('diagnosticIsSuppressed', () => {
-        it('does not crash when diagnostic is missing location information', () => {
-            const program = new Program({});
-            const file = program.setFile('source/main.brs', '') as BrsFile;
-            const diagnostic: BsDiagnostic = {
-                file: file,
-                message: 'crash',
-                //important part of the test. range must be missing
-                range: undefined as any
-            };
-
-            file.commentFlags.push({
-                affectedRange: util.createRange(1, 2, 3, 4),
-                codes: [1, 2, 3],
-                file: file,
-                range: util.createRange(1, 2, 3, 4)
-            });
-            program.diagnostics.register(diagnostic);
-
-            util.diagnosticIsSuppressed(diagnostic);
-
-            //test passes if there's no crash
-        });
-    });
-
     describe('sanitizePkgPath', () => {
         it('replaces more than one windows slash in a path', () => {
             expect(util.sanitizePkgPath('source\\folder1\\folder2\\file.brs')).to.eql('pkg:/source/folder1/folder2/file.brs');
@@ -935,8 +907,7 @@ describe('util', () => {
             expect(
                 util.toDiagnostic({
                     ...DiagnosticMessages.cannotFindName('someVar'),
-                    file: undefined as any,
-                    range: util.createRange(1, 2, 3, 4),
+                    location: { uri: null, range: util.createRange(1, 2, 3, 4) },
                     relatedInformation: [{
                         message: 'Alpha',
                         location: undefined as any
@@ -954,8 +925,7 @@ describe('util', () => {
             expect(
                 util.toDiagnostic({
                     ...DiagnosticMessages.cannotFindName('someVar'),
-                    file: undefined as any,
-                    range: util.createRange(1, 2, 3, 4),
+                    location: { uri: null, range: util.createRange(1, 2, 3, 4) },
                     relatedInformation: [{
                         message: 'Alpha',
                         location: util.createLocationFromRange(
diff --git a/src/util.ts b/src/util.ts
index 7ad10fb2..b0fe4168 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -47,6 +47,7 @@ import { AssociativeArrayType } from './types/AssociativeArrayType';
 import { ComponentType } from './types/ComponentType';
 import { FunctionType } from './types/FunctionType';
 import type { AssignmentStatement } from './parser/Statement';
+import type { BscFile } from './files/BscFile';
 
 export class Util {
     public clearConsole() {
@@ -204,10 +205,10 @@ export class Util {
                 let err = parseErrors[0];
                 let diagnostic = {
                     ...DiagnosticMessages.bsConfigJsonHasSyntaxErrors(printParseErrorCode(parseErrors[0].error)),
-                    file: {
-                        srcPath: configFilePath
-                    },
-                    range: this.getRangeFromOffsetLength(projectFileContents, err.offset, err.length)
+                    location: {
+                        uri: this.pathToUri(configFilePath),
+                        range: this.getRangeFromOffsetLength(projectFileContents, err.offset, err.length)
+                    }
                 } as BsDiagnostic;
                 throw diagnostic; //eslint-disable-line @typescript-eslint/no-throw-literal
             }
@@ -744,7 +745,7 @@ export class Util {
     /**
      * Does the string appear to be a uri (i.e. does it start with `file:`)
      */
-    private isUriLike(filePath: string) {
+    public isUriLike(filePath: string) {
         return filePath?.indexOf('file:') === 0;// eslint-disable-line @typescript-eslint/prefer-string-starts-ends-with
     }
 
@@ -867,22 +868,6 @@ export class Util {
         }
     }
 
-    /**
-     * Determine whether this diagnostic should be supressed or not, based on brs comment-flags
-     */
-    public diagnosticIsSuppressed(diagnostic: BsDiagnostic) {
-        const diagnosticCode = typeof diagnostic.code === 'string' ? diagnostic.code.toLowerCase() : diagnostic.code;
-        for (let flag of diagnostic.file?.commentFlags ?? []) {
-            //this diagnostic is affected by this flag
-            if (diagnostic.range && this.rangeContains(flag.affectedRange, diagnostic.range.start)) {
-                //if the flag acts upon this diagnostic's code
-                if (flag.codes === null || (diagnosticCode !== undefined && flag.codes.includes(diagnosticCode))) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
 
     /**
      * Walks up the chain to find the closest bsconfig.json file
@@ -1102,6 +1087,13 @@ export class Util {
         };
     }
 
+    /**
+     * Helper for creating `Location` objects from a file and range
+     */
+    public createLocationFromFileRange(file: BscFile, range: Range): Location {
+        return this.createLocationFromRange(this.pathToUri(file?.srcPath), range);
+    }
+
     /**
      * Helper for creating `Location` objects by passing each range value in directly. Prefer using this function because vscode-languageserver's `Location.create()` is significantly slower at scale
      */
@@ -1784,9 +1776,13 @@ export class Util {
                 location: util.createLocationFromRange('   ', util.createRange(0, 0, 0, 0))
             });
         }
+
+        const range = (diagnostic as BsDiagnostic).location?.range ??
+            (diagnostic as Diagnostic).range;
+
         let result = {
             severity: diagnostic.severity,
-            range: diagnostic.range,
+            range: range,
             message: diagnostic.message,
             relatedInformation: relatedInformation.map(x => {
 
@@ -1795,7 +1791,7 @@ export class Util {
                 if (!clone.location) {
                     // use the fallback location if available
                     if (relatedInformationFallbackLocation) {
-                        clone.location = util.createLocationFromRange(relatedInformationFallbackLocation, diagnostic.range);
+                        clone.location = util.createLocationFromRange(relatedInformationFallbackLocation, range);
                     } else {
                         //remove this related information so it doesn't bring crash the language server
                         return undefined;
@@ -2074,8 +2070,7 @@ export class Util {
         if (fileDepth >= 8) {
             file.program?.diagnostics.register({
                 ...DiagnosticMessages.detectedTooDeepFileSource(fileDepth),
-                file: file,
-                range: this.createRange(0, 0, 0, Number.MAX_VALUE)
+                location: util.createLocationFromFileRange(file, this.createRange(0, 0, 0, Number.MAX_VALUE))
             });
         }
     }
diff --git a/src/validators/ClassValidator.ts b/src/validators/ClassValidator.ts
index d78bae68..23252f00 100644
--- a/src/validators/ClassValidator.ts
+++ b/src/validators/ClassValidator.ts
@@ -56,8 +56,7 @@ export class BsClassValidator {
                         if (expressionNameLower === 'm') {
                             this.diagnostics.push({
                                 ...DiagnosticMessages.classConstructorIllegalUseOfMBeforeSuperCall(),
-                                file: classStatement.file,
-                                range: expression.location?.range
+                                location: expression.location
                             });
                         }
                         if (isCallExpression(parent) && expressionNameLower === 'super') {
@@ -75,8 +74,7 @@ export class BsClassValidator {
                 if (!superCall && !(classStatement.file as BrsFile).isTypedef) {
                     this.diagnostics.push({
                         ...DiagnosticMessages.classConstructorMissingSuperCall(),
-                        file: classStatement.file,
-                        range: newMethod.location?.range
+                        location: newMethod.location
                     });
                 }
             }
@@ -97,8 +95,7 @@ export class BsClassValidator {
                     this.diagnostics.push({
                         ...DiagnosticMessages.circularReferenceDetected(
                             Array.from(names.values()).concat(className), this.scope.name),
-                        file: cls.file,
-                        range: cls.tokens.name.location?.range
+                        location: cls.tokens.name.location
                     });
                     break;
                 }
@@ -133,8 +130,7 @@ export class BsClassValidator {
                     if (methods[lowerMemberName] || fields[lowerMemberName]) {
                         this.diagnostics.push({
                             ...DiagnosticMessages.duplicateIdentifier(memberName.text),
-                            file: classStatement.file,
-                            range: memberName.location?.range
+                            location: memberName.location
                         });
                     }
 
@@ -151,8 +147,7 @@ export class BsClassValidator {
                                     ancestorMemberKind,
                                     ancestorAndMember.classStatement.getName(ParseMode.BrighterScript)
                                 ),
-                                file: classStatement.file,
-                                range: member.location?.range
+                                location: member.location
                             });
                         }
 
@@ -175,8 +170,7 @@ export class BsClassValidator {
                                         childFieldType.toString(),
                                         ancestorMemberType.toString()
                                     ),
-                                    file: classStatement.file,
-                                    range: member.location?.range
+                                    location: member.location
                                 });
                             }
                         }
@@ -194,8 +188,7 @@ export class BsClassValidator {
                                 ...DiagnosticMessages.missingOverrideKeyword(
                                     ancestorAndMember.classStatement.getName(ParseMode.BrighterScript)
                                 ),
-                                file: classStatement.file,
-                                range: member.location?.range
+                                location: member.location
                             });
                         }
 
@@ -213,8 +206,7 @@ export class BsClassValidator {
                                     ancestorAndMember.member.accessModifier?.text || 'public',
                                     ancestorAndMember.classStatement.getName(ParseMode.BrighterScript)
                                 ),
-                                file: classStatement.file,
-                                range: member.location?.range
+                                location: member.location
                             });
                         }
                     }


Test patch:
diff --git a/src/files/tests/optionalChaning.spec.ts b/src/files/tests/optionalChaning.spec.ts
index d1ad0b0f..d79c9612 100644
--- a/src/files/tests/optionalChaning.spec.ts
+++ b/src/files/tests/optionalChaning.spec.ts
@@ -116,7 +116,7 @@ describe('optional chaining', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.noOptionalChainingInLeftHandSideOfAssignment(),
-                range: util.createRange(2, 20, 2, 24)
+                location: { range: util.createRange(2, 20, 2, 24) }
             }]);
         });
 
@@ -129,7 +129,7 @@ describe('optional chaining', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.noOptionalChainingInLeftHandSideOfAssignment(),
-                range: util.createRange(2, 20, 2, 29)
+                location: { range: util.createRange(2, 20, 2, 29) }
             }]);
         });
 
@@ -143,7 +143,7 @@ describe('optional chaining', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.noOptionalChainingInLeftHandSideOfAssignment(),
-                range: util.createRange(3, 20, 3, 27)
+                location: { range: util.createRange(3, 20, 3, 27) }
             }]);
         });
 
@@ -157,7 +157,7 @@ describe('optional chaining', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.noOptionalChainingInLeftHandSideOfAssignment(),
-                range: util.createRange(3, 20, 3, 30)
+                location: { range: util.createRange(3, 20, 3, 30) }
             }]);
         });
 
@@ -172,10 +172,10 @@ describe('optional chaining', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.noOptionalChainingInLeftHandSideOfAssignment(),
-                range: util.createRange(3, 20, 3, 39)
+                location: { range: util.createRange(3, 20, 3, 39) }
             }, {
                 ...DiagnosticMessages.noOptionalChainingInLeftHandSideOfAssignment(),
-                range: util.createRange(4, 20, 4, 33)
+                location: { range: util.createRange(4, 20, 4, 33) }
             }]);
         });
     });
diff --git a/src/parser/tests/expression/Call.spec.ts b/src/parser/tests/expression/Call.spec.ts
index bbc3c393..82b95bdc 100644
--- a/src/parser/tests/expression/Call.spec.ts
+++ b/src/parser/tests/expression/Call.spec.ts
@@ -37,7 +37,7 @@ describe('parser call expressions', () => {
         expect(ast.statements).to.be.length.greaterThan(0);
 
         //ALL of the diagnostics should be on the `DoThin` line
-        let lineNumbers = diagnostics.map(x => x.range.start.line);
+        let lineNumbers = diagnostics.map(x => x.location.range.start.line);
         for (let lineNumber of lineNumbers) {
             expect(lineNumber).to.equal(2);
         }
@@ -60,7 +60,7 @@ describe('parser call expressions', () => {
         ]);
         expect(ast.statements).to.be.length.greaterThan(0);
         //the error should be BEFORE the `name = "bob"` statement
-        expect(diagnostics[0].range.end.character).to.be.lessThan(25);
+        expect(diagnostics[0].location.range.end.character).to.be.lessThan(25);
     });
 
     it('allows closing parentheses on separate line', () => {
diff --git a/src/parser/tests/statement/Enum.spec.ts b/src/parser/tests/statement/Enum.spec.ts
index 0da2aa5c..b6e458ea 100644
--- a/src/parser/tests/statement/Enum.spec.ts
+++ b/src/parser/tests/statement/Enum.spec.ts
@@ -11,7 +11,6 @@ import type { BrsFile } from '../../../files/BrsFile';
 import { CancellationTokenSource } from 'vscode-languageserver-protocol';
 import { WalkMode } from '../../../astUtils/visitors';
 import { isEnumStatement } from '../../../astUtils/reflection';
-import { URI } from 'vscode-uri';
 import { rootDir } from '../../../testHelpers.spec';
 
 const sinon = createSandbox();
@@ -227,14 +226,14 @@ describe('EnumStatement', () => {
                 ...DiagnosticMessages.nameCollision('Enum', 'Enum', 'Direction'),
                 relatedInformation: [{
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/source/main.bs`).toString(),
+                        util.pathToUri(s`${rootDir}/source/main.bs`),
                         util.createRange(5, 21, 5, 30)
                     ),
                     message: 'Enum declared here'
                 },
                 {
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/source/main.bs`).toString(),
+                        util.pathToUri(s`${rootDir}/source/main.bs`),
                         util.createRange(1, 21, 1, 30)
                     ),
                     message: `In scope 'source'`
@@ -243,14 +242,14 @@ describe('EnumStatement', () => {
                 ...DiagnosticMessages.nameCollision('Enum', 'Enum', 'Direction'),
                 relatedInformation: [{
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/source/main.bs`).toString(),
+                        util.pathToUri(s`${rootDir}/source/main.bs`),
                         util.createRange(1, 21, 1, 30)
                     ),
                     message: 'Enum declared here'
                 },
                 {
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/source/main.bs`).toString(),
+                        util.pathToUri(s`${rootDir}/source/main.bs`),
                         util.createRange(5, 21, 5, 30)
                     ),
                     message: `In scope 'source'`
@@ -274,14 +273,14 @@ describe('EnumStatement', () => {
                 ...DiagnosticMessages.nameCollision('Enum', 'Enum', 'Direction'),
                 relatedInformation: [{
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/source/lib.bs`).toString(),
+                        util.pathToUri(s`${rootDir}/source/lib.bs`),
                         util.createRange(1, 21, 1, 30)
                     ),
                     message: 'Enum declared here'
                 },
                 {
                     location: util.createLocationFromRange(
-                        URI.file(s`${rootDir}/source/main.bs`).toString(),
+                        util.pathToUri(s`${rootDir}/source/main.bs`),
                         util.createRange(1, 21, 1, 30)
                     ),
                     message: `In scope 'source'`
@@ -320,7 +319,7 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.duplicateIdentifier('name'),
-                range: util.createRange(3, 20, 3, 24)
+                location: { range: util.createRange(3, 20, 3, 24) }
             }]);
         });
 
@@ -334,7 +333,7 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.enumValueMustBeType('integer'),
-                range: util.createRange(3, 24, 3, 27)
+                location: { range: util.createRange(3, 24, 3, 27) }
             }]);
         });
 
@@ -348,7 +347,7 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.enumValueMustBeType('string'),
-                range: util.createRange(3, 24, 3, 25)
+                location: { range: util.createRange(3, 24, 3, 25) }
             }]);
         });
 
@@ -362,7 +361,7 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.enumValueIsRequired('string'),
-                range: util.createRange(3, 20, 3, 21)
+                location: { range: util.createRange(3, 20, 3, 21) }
             }]);
         });
 
@@ -397,7 +396,7 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.enumValueMustBeType('integer'),
-                range: util.createRange(2, 33, 2, 35)
+                location: { range: util.createRange(2, 33, 2, 35) }
             }]);
         });
 
@@ -422,7 +421,7 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.enumValueIsRequired('string'),
-                range: util.createRange(2, 20, 2, 21)
+                location: { range: util.createRange(2, 20, 2, 21) }
             }]);
         });
 
@@ -441,10 +440,10 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.unknownEnumValue('DOWN', 'Direction'),
-                range: util.createRange(7, 36, 7, 40)
+                location: { range: util.createRange(7, 36, 7, 40) }
             }, {
                 ...DiagnosticMessages.unknownEnumValue('down', 'Direction'),
-                range: util.createRange(8, 36, 8, 40)
+                location: { range: util.createRange(8, 36, 8, 40) }
             }]);
         });
 
@@ -465,10 +464,10 @@ describe('EnumStatement', () => {
             program.validate();
             expectDiagnostics(program, [{
                 ...DiagnosticMessages.unknownEnumValue('DOWN', 'Enums.Direction'),
-                range: util.createRange(2, 42, 2, 46)
+                location: { range: util.createRange(2, 42, 2, 46) }
             }, {
                 ...DiagnosticMessages.unknownEnumValue('down', 'Enums.Direction'),
-                range: util.createRange(3, 42, 3, 46)
+                location: { range: util.createRange(3, 42, 3, 46) }
             }]);
         });
     });
diff --git a/src/parser/tests/statement/Misc.spec.ts b/src/parser/tests/statement/Misc.spec.ts
index d186838a..b46b94f0 100644
--- a/src/parser/tests/statement/Misc.spec.ts
+++ b/src/parser/tests/statement/Misc.spec.ts
@@ -37,7 +37,7 @@ describe('parser', () => {
             let { diagnostics } = Parser.parse(tokens);
             expect(diagnostics).to.be.lengthOf(1);
             //specifically check for the error location, because the identifier location was wrong in the past
-            expect(diagnostics[0].range).to.deep.include(
+            expect(diagnostics[0].location.range).to.deep.include(
                 Range.create(1, 4, 1, 8)
             );
         });
diff --git a/src/testHelpers.spec.ts b/src/testHelpers.spec.ts
index fdebd84a..cfbfecce 100644
--- a/src/testHelpers.spec.ts
+++ b/src/testHelpers.spec.ts
@@ -1,7 +1,7 @@
 import type { BsDiagnostic } from './interfaces';
 import * as assert from 'assert';
 import chalk from 'chalk';
-import type { CodeDescription, CompletionItem, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, integer, Range } from 'vscode-languageserver';
+import type { CodeDescription, CompletionItem, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, integer, Location } from 'vscode-languageserver';
 import { createSandbox } from 'sinon';
 import { expect } from './chai-config.spec';
 import type { CodeActionShorthand } from './CodeActionUtil';
@@ -21,7 +21,7 @@ export const stagingDir = s`${tempDir}/stagingDir`;
 
 export const trim = undent;
 
-type DiagnosticCollection = { getDiagnostics(): Array<Diagnostic> } | { diagnostics?: Diagnostic[] } | Diagnostic[];
+type DiagnosticCollection = { getDiagnostics(): Array<BsDiagnostic>; getFile?: (path: string, normalize: boolean) => BscFile } | { diagnostics?: BsDiagnostic[] } | BsDiagnostic[];
 
 function getDiagnostics(arg: DiagnosticCollection): BsDiagnostic[] {
     if (Array.isArray(arg)) {
@@ -39,10 +39,10 @@ function sortDiagnostics(diagnostics: BsDiagnostic[]) {
     return diagnostics.sort(
         firstBy<BsDiagnostic>('code')
             .thenBy<BsDiagnostic>('message')
-            .thenBy<BsDiagnostic>((a, b) => (a.range?.start?.line ?? 0) - (b.range?.start?.line ?? 0))
-            .thenBy<BsDiagnostic>((a, b) => (a.range?.start?.character ?? 0) - (b.range?.start?.character ?? 0))
-            .thenBy<BsDiagnostic>((a, b) => (a.range?.end?.line ?? 0) - (b.range?.end?.line ?? 0))
-            .thenBy<BsDiagnostic>((a, b) => (a.range?.end?.character ?? 0) - (b.range?.end?.character ?? 0))
+            .thenBy<BsDiagnostic>((a, b) => (a.location?.range?.start?.line ?? 0) - (b.location?.range?.start?.line ?? 0))
+            .thenBy<BsDiagnostic>((a, b) => (a.location?.range?.start?.character ?? 0) - (b.location?.range?.start?.character ?? 0))
+            .thenBy<BsDiagnostic>((a, b) => (a.location?.range?.end?.line ?? 0) - (b.location?.range?.end?.line ?? 0))
+            .thenBy<BsDiagnostic>((a, b) => (a.location?.range?.end?.character ?? 0) - (b.location?.range?.end?.character ?? 0))
     );
 }
 
@@ -60,7 +60,7 @@ function cloneObject<TOriginal, TTemplate>(original: TOriginal, template: TTempl
 }
 
 interface PartialDiagnostic {
-    range?: Range;
+    location?: Partial<Location>;
     severity?: DiagnosticSeverity;
     code?: integer | string;
     codeDescription?: Partial<CodeDescription>;
@@ -79,8 +79,12 @@ function cloneDiagnostic(actualDiagnosticInput: BsDiagnostic, expectedDiagnostic
     const actualDiagnostic = cloneObject(
         actualDiagnosticInput,
         expectedDiagnostic,
-        ['message', 'code', 'range', 'severity', 'relatedInformation']
+        ['message', 'code', 'severity', 'relatedInformation']
     );
+    //clone Location if available
+    if (expectedDiagnostic.location) {
+        actualDiagnostic.location = cloneObject(actualDiagnosticInput.location, expectedDiagnostic.location, ['uri', 'range']) as any;
+    }
     //deep clone relatedInformation if available
     if (actualDiagnostic.relatedInformation) {
         for (let j = 0; j < actualDiagnostic.relatedInformation.length; j++) {
@@ -91,14 +95,6 @@ function cloneDiagnostic(actualDiagnosticInput: BsDiagnostic, expectedDiagnostic
             ) as any;
         }
     }
-    //deep clone file info if available
-    if (actualDiagnostic.file) {
-        actualDiagnostic.file = cloneObject(
-            actualDiagnostic.file,
-            expectedDiagnostic?.file,
-            ['srcPath', 'destPath', 'pkgPath']
-        ) as any;
-    }
     return actualDiagnostic;
 }
 
@@ -177,9 +173,11 @@ export function expectZeroDiagnostics(arg: DiagnosticCollection) {
         for (const diagnostic of diagnostics) {
             //escape any newlines
             diagnostic.message = diagnostic.message.replace(/\r/g, '\\r').replace(/\n/g, '\\n');
-            message += `\n        • bs${diagnostic.code} "${diagnostic.message}" at ${diagnostic.file?.srcPath ?? ''}#(${diagnostic.range?.start.line}:${diagnostic.range?.start.character})-(${diagnostic.range?.end.line}:${diagnostic.range?.end.character})`;
+            message += `\n        • bs${diagnostic.code} "${diagnostic.message}" at ${diagnostic.location?.uri ?? ''}#(${diagnostic.location.range?.start.line}:${diagnostic.location.range?.start.character})-(${diagnostic.location.range?.end.line}:${diagnostic.location.range?.end.character})`;
             //print the line containing the error (if we can find it)srcPath
-            const line = (diagnostic.file as BrsFile)?.fileContents?.split(/\r?\n/g)?.[diagnostic.range?.start.line];
+            const file = (arg as any).getFile(diagnostic.location.uri);
+
+            const line = (file as BrsFile)?.fileContents?.split(/\r?\n/g)?.[diagnostic.location.range?.start.line];
             if (line) {
                 message += '\n' + getDiagnosticLine(diagnostic, line, chalk.red);
             }