Repository: rokucommunity/brighterscript
Pull request #1089 (2024-03-06 14:51:06)

Formal support and tests for various Variable and name shadowing rules
- Added a docs page to explain
- Modified some logic and added tests to specify exactly what is allowed and and what causes an error
- refactored code to reduce use of `FunctionScope` 
- realized there was a typo in a file name (`SymbolTableFlag.ts` -> `SymbolTypeFlag.ts`)

Addresses #1041


Patch:
diff --git a/docs/readme.md b/docs/readme.md
index 44a41093..d8b21ce9 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -111,7 +111,10 @@ authStatus = user <> invalid ? "logged in" : "not logged in"
 
 ## [Union Types](union-types.md)
 ```brighterscript
-sub log(data as string or number)
+sub logData(data as string or number)
     print data.toStr()
 end sub
-```
\ No newline at end of file
+```
+
+## [Variable Shadowing](variable-shadowing.md)
+Name resolution rules for various types of shadowing.
\ No newline at end of file
diff --git a/docs/variable-shadowing.md b/docs/variable-shadowing.md
new file mode 100644
index 00000000..a392b30d
--- /dev/null
+++ b/docs/variable-shadowing.md
@@ -0,0 +1,104 @@
+# Variable Shadowing and Name Collisions in BrighterScript
+
+BrighterScript allows various kinds of [variable name shadowing](https://en.wikipedia.org/wiki/Variable_shadowing). In general, variables and types refer to items defined in the current scope/namespace if available.
+
+However, due to the limitations of BrightScript, there are some name collisions that lead to unpredictable behavior, and so they cause diagnostic errors.
+
+## Name Resolution Rules
+
+**1. Local variables CAN shadow names of global functions.**
+
+✅
+
+```brighterscipt
+sub test()
+    log = "value" ' identifier shadows global function log() - no error
+    upTime = 0 ' identifier shadows global function Uptime() - no error
+end sub
+```
+
+**2. Local variables CANNOT shadow names of functions or classes defined at the same scope.**
+
+❌
+
+```brighterscipt
+sub test()
+    pi = "Apple" ' identifier shadows function pi() - causes validation error
+    data = 1234 ' identifier shadows class Data - causes validation error
+end sub
+
+function pi() as float
+    return 3.14
+function
+
+class Data
+    value = {}
+end class
+```
+
+**3. Custom types and definitions (enums, classes, interfaces, functions, consts) CANNOT have the same name if they are in the same namespace.**
+
+❌
+
+```brighterscipt
+function someName()
+end function
+
+class SomeName  ' class shadows local function - causes validation error
+    sub foo()
+    end sub
+end class
+
+namespace alpha
+    class OtherName  ' class in namespace shadows function in same namespace - causes validation error
+        sub foo()
+        end sub
+    end class
+
+    function otherName()
+    end function
+end namespace
+```
+
+**4. Functions and classes outside of namespaces CANNOT shadow standard global functions (eg. `ParseJson`, `LCase`, etc.)**
+
+❌
+
+```brighterscipt
+class log  ' class shadows global function - causes validation error
+    sub foo()
+    end sub
+end class
+
+
+```
+
+**5. Definitions inside namespaces CAN shadow standard global functions, or functions at a difference namespace-level. In this way, the outer item is unavailable, and only the item defined at the current scope is accessible.**
+
+✅
+
+```brighterscipt
+class SomeName()
+end class
+
+namespace alpha
+    class SomeName  ' class in namespace shadows function in upper scope
+        sub foo()
+            print "foo"
+        end sub
+    end class
+
+    sub test()
+        myKlass = new SomeName() ' refers to alpha.SomeName
+        myKlass.foo() ' will print "foo"
+    end sub
+
+    sub log(data) ' function defined as alpha.log - this is ok
+        print "LOG: ";data
+    end sub
+
+    sub foo()
+        log("Hello world") ' refers to alpha.log - will print "LOG: Hello world"
+    end sub
+end namespace
+```
diff --git a/src/AstValidationSegmenter.ts b/src/AstValidationSegmenter.ts
index 3f0bbbb2..a04cef57 100644
--- a/src/AstValidationSegmenter.ts
+++ b/src/AstValidationSegmenter.ts
@@ -1,11 +1,11 @@
 import type { DottedGetExpression, TypeExpression, VariableExpression } from './parser/Expression';
-import { SymbolTypeFlag } from './SymbolTableFlag';
 import { isBody, isClassStatement, isInterfaceStatement, isNamespaceStatement, isVariableExpression } from './astUtils/reflection';
 import { ChildrenSkipper, WalkMode, createVisitor } from './astUtils/visitors';
 import type { GetTypeOptions, TypeChainEntry } from './interfaces';
 import type { AstNode } from './parser/AstNode';
 import { util } from './util';
 import type { NamespaceStatement } from './parser/Statement';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 
 // eslint-disable-next-line no-bitwise
 export const InsideSegmentWalkMode = WalkMode.visitStatements | WalkMode.visitExpressions | WalkMode.recurseChildFunctions;
diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts
index 2337f9b2..81cc6e99 100644
--- a/src/DiagnosticMessages.ts
+++ b/src/DiagnosticMessages.ts
@@ -3,7 +3,7 @@ import { DiagnosticSeverity } from 'vscode-languageserver';
 import type { BsDiagnostic, TypeCompatibilityData } from './interfaces';
 import { TokenKind } from './lexer/TokenKind';
 import util from './util';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 
 /**
  * An object that keeps track of all possible error messages.
diff --git a/src/Program.spec.ts b/src/Program.spec.ts
index 7c16f0fd..dcb72e1e 100644
--- a/src/Program.spec.ts
+++ b/src/Program.spec.ts
@@ -21,7 +21,7 @@ import * as path from 'path';
 import type { SinonSpy } from 'sinon';
 import { createSandbox } from 'sinon';
 import type { AfterFileAddEvent, AfterFileRemoveEvent, AfterProvideFileEvent, BeforeFileAddEvent, BeforeFileRemoveEvent, BeforeProvideFileEvent, CompilerPlugin, ProvideFileEvent } from './interfaces';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import { StringType } from './types/StringType';
 import { TypedFunctionType } from './types/TypedFunctionType';
 import { DynamicType } from './types/DynamicType';
diff --git a/src/Program.ts b/src/Program.ts
index 605f739b..8893ae52 100644
--- a/src/Program.ts
+++ b/src/Program.ts
@@ -28,7 +28,7 @@ import { SignatureHelpUtil } from './bscPlugin/SignatureHelpUtil';
 import { DiagnosticSeverityAdjuster } from './DiagnosticSeverityAdjuster';
 import { IntegerType } from './types/IntegerType';
 import { StringType } from './types/StringType';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import { BooleanType } from './types/BooleanType';
 import { DoubleType } from './types/DoubleType';
 import { DynamicType } from './types/DynamicType';
diff --git a/src/Scope.spec.ts b/src/Scope.spec.ts
index f2b83805..5be4a386 100644
--- a/src/Scope.spec.ts
+++ b/src/Scope.spec.ts
@@ -11,7 +11,7 @@ import type { BrsFile } from './files/BrsFile';
 import type { NamespaceStatement } from './parser/Statement';
 import type { CompilerPlugin, OnScopeValidateEvent } from './interfaces';
 import { DiagnosticOrigin } from './interfaces';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import { EnumMemberType, EnumType } from './types/EnumType';
 import { ClassType } from './types/ClassType';
 import { BooleanType } from './types/BooleanType';
@@ -145,7 +145,7 @@ describe('Scope', () => {
         ]);
     });
 
-    it('does flag namespaced const and un-namespaced function collision', () => {
+    it('does not flag namespaced const and un-namespaced function collision', () => {
         program.setFile('source/main.bs', `
             namespace alpha
                 const options = {}
@@ -155,9 +155,7 @@ describe('Scope', () => {
             end sub
         `);
         program.validate();
-        expectDiagnostics(program, [
-            DiagnosticMessages.nameCollision('Const', 'Function', 'options').message
-        ]);
+        expectZeroDiagnostics(program);
     });
 
     it('flags parameter with same name as namespace', () => {
@@ -1265,7 +1263,7 @@ describe('Scope', () => {
         });
 
         describe('name collisions', () => {
-            it('should validate when class and interfaces have name collisions', () => {
+            it('should validate when class have name collisions with global function', () => {
                 program.setFile('source/main.bs', `
                     class Log
                     end class
@@ -1277,11 +1275,21 @@ describe('Scope', () => {
                 `);
                 program.validate();
                 expectDiagnosticsIncludes(program, [
-                    DiagnosticMessages.nameCollision('Class', 'Global Function', 'Log').message,
-                    DiagnosticMessages.nameCollision('Interface', 'Global Function', 'Lcase').message
+                    DiagnosticMessages.nameCollision('Class', 'Global Function', 'Log').message
                 ]);
             });
 
+            it('should not validate when interfaces have name collisions with global function', () => {
+                program.setFile('source/main.bs', `
+                    interface Lcase
+                        name as string
+                    end interface
+
+                `);
+                program.validate();
+                expectZeroDiagnostics(program);
+            });
+
             it('should validate when a namespace has a name collision with a class', () => {
                 program.setFile('source/main.bs', `
                     namespace SomeKlass
@@ -1342,7 +1350,7 @@ describe('Scope', () => {
                 ]);
             });
 
-            it('should validate when a const has a name collision with something else', () => {
+            it('should not validate when a const has a name collision with something else in different namespace', () => {
                 program.setFile('source/main.bs', `
                     namespace SomeEnum
                         const MY_CONST = "hello"
@@ -1353,6 +1361,20 @@ describe('Scope', () => {
                 `);
                 program.validate();
                 let diagnostics = program.getDiagnostics();
+                expectZeroDiagnostics(diagnostics);
+            });
+
+            it('should validate when a const has a name collision with something else in same namespace', () => {
+                program.setFile('source/main.bs', `
+                    namespace SomeEnum
+                        const MY_CONST = "hello"
+
+                        function MY_CONST()
+                        end function
+                    end namespace
+                `);
+                program.validate();
+                let diagnostics = program.getDiagnostics();
                 expectDiagnosticsIncludes(diagnostics, [
                     DiagnosticMessages.nameCollision('Const', 'Function', 'MY_CONST').message
                 ]);
@@ -3757,6 +3779,275 @@ describe('Scope', () => {
 
     });
 
+    describe('shadowing', () => {
+        it('allows namespaces shadowing global function names', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                namespace log
+                    sub doLog(x)
+                        ? x
+                    end sub
+                end namespace
+            `);
+            program.setFile<BrsFile>('source/file2.bs', `
+                sub foo()
+                    log.doLog("hello")
+                end sub
+            `);
+
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+        it('allows enums shadowing global function names', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                enum log
+                    debug = "DEBUG"
+                    error = "ERROR"
+                end enum
+            `);
+            program.setFile<BrsFile>('source/file2.bs', `
+                sub foo(message)
+                    print log.debug;" ";message
+                end sub
+            `);
+
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+        it('allows interfaces shadowing global function names', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                interface log
+                    abs
+                    function formatJson(input)
+                end interface
+            `);
+            program.setFile<BrsFile>('source/file2.bs', `
+                sub foo(logger as log)
+                    print logger.abs;" ";logger.formatJson("message")
+                end sub
+            `);
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+        it('allows consts shadowing global function names', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                const log = "hello"   ' const shadows global function
+            `);
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+
+        it('disallows enum/const shadowing', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                enum SomeName
+                    opt1
+                    opt2
+                end enum
+
+                const SomeName = "hello"    ' const and enum have same name
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.nameCollision('Enum', 'Const', 'SomeName').message,
+                DiagnosticMessages.nameCollision('Const', 'Enum', 'SomeName').message
+            ]);
+        });
+
+        it('disallows enum/namespace shadowing', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                enum SomeName
+                    opt1
+                    opt2
+                end enum
+
+                namespace SomeName
+                    sub foo()
+                    end sub
+                end namespace
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.nameCollision('Enum', 'Namespace', 'SomeName').message,
+                DiagnosticMessages.nameCollision('Namespace', 'Enum', 'SomeName').message
+            ]);
+        });
+
+        it('disallows interface/namespace shadowing', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                interface SomeName  ' interface has same name as namespace
+                    opt1
+                    opt2
+                end interface
+
+                namespace SomeName
+                    sub foo()
+                    end sub
+                end namespace
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.nameCollision('Interface', 'Namespace', 'SomeName').message,
+                DiagnosticMessages.nameCollision('Namespace', 'Interface', 'SomeName').message
+            ]);
+        });
+
+        it('disallows class/namespace shadowing', () => {
+            program.setFile<BrsFile>('source/file1.bs', `
+                class SomeName  ' class has same name as namespace
+                    opt1
+                    opt2
+                end class
+
+                namespace SomeName
+                    sub foo()
+                    end sub
+                end namespace
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.nameCollision('Class', 'Namespace', 'SomeName').message,
+                DiagnosticMessages.nameCollision('Namespace', 'Class', 'SomeName').message
+            ]);
+        });
+
+        it('allows namespaced functions shadowing upper scope function names', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                namespace alpha
+                    sub log(input)
+                      print "in namespace"
+                    end sub
+
+                    sub test()
+                        log(44) ' prints "in namespace"
+                    end sub
+                end namespace
+            `);
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+        it('allows namespaced functions shadowing upper scope function names', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                sub someFunc()
+                    print "outside"
+                end sub
+
+                namespace alpha
+                    sub someFunc()
+                      print "inside"
+                    end sub
+
+                    sub test()
+                        someFunc() ' prints "inside"
+                    end sub
+                end namespace
+            `);
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+        it('disallows class/global function shadowing', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                class log  ' class shadows global function
+                    sub foo()
+                    end sub
+                end class
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.nameCollision('Class', 'Global Function', 'log')
+            ]);
+        });
+
+        it('disallows class/local function shadowing', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                function someName()
+                end function
+
+                class SomeName  ' class shadows local function
+                    sub foo()
+                    end sub
+                end class
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.functionCannotHaveSameNameAsClass('someName'),
+                DiagnosticMessages.nameCollision('Class', 'Function', 'SomeName')
+            ]);
+        });
+
+
+        it('allows function having same name as class in namespace', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                function someName()
+                end function
+
+                namespace alpha
+                    class SomeName  ' class in namespace shadows function in upper scope
+                        sub foo()
+                        end sub
+                    end class
+                end namespace
+            `);
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+        it('allows class in namespace having same name as global function', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                namespace alpha
+                    class log ' class in namespace shadows global function
+                        text = "hello"
+                    end class
+
+                    sub foo()
+                       myLog = new Log()
+                       print myLog.text ' prints "hello"
+                    end sub
+                end namespace
+            `);
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
+        it('disallows reusing a class name as "for each" variable in a function', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                class Person
+                    name as string
+                end class
+
+                sub foo(people as Person[])
+                    for each person in people
+                        print person.name
+                    end for
+                end sub
+            `);
+            program.validate();
+            expectDiagnosticsIncludes(program, DiagnosticMessages.localVarSameNameAsClass('Person').message);
+        });
+
+        it('disallows reusing a class name as "for each" variable in a method', () => {
+            program.setFile<BrsFile>('source/file.bs', `
+                class Person
+                    name as string
+                    children as Person[]
+
+                    sub test()
+                        for each person in m.children
+                            print person.name
+                        end for
+                    end sub
+                end class
+            `);
+            program.validate();
+            expectDiagnosticsIncludes(program, DiagnosticMessages.localVarSameNameAsClass('Person').message);
+        });
+    });
+
+
     describe('performance', () => {
 
         // eslint-disable-next-line func-names, prefer-arrow-callback
diff --git a/src/Scope.ts b/src/Scope.ts
index 0ac6eacd..cf5c1053 100644
--- a/src/Scope.ts
+++ b/src/Scope.ts
@@ -4,10 +4,10 @@ import chalk from 'chalk';
 import type { DiagnosticInfo } from './DiagnosticMessages';
 import { DiagnosticMessages } from './DiagnosticMessages';
 import { DiagnosticOrigin } from './interfaces';
-import type { CallableContainer, BsDiagnosticWithOrigin, FileReference, CallableContainerMap, FileLink, Callable, NamespaceContainer, ScopeValidationOptions, BsDiagnostic } from './interfaces';
+import type { CallableContainer, BsDiagnosticWithOrigin, FileReference, FileLink, Callable, NamespaceContainer, ScopeValidationOptions, BsDiagnostic } from './interfaces';
 import type { Program } from './Program';
 import { BsClassValidator } from './validators/ClassValidator';
-import type { NamespaceStatement, ClassStatement, EnumStatement, InterfaceStatement, EnumMemberStatement, ConstStatement } from './parser/Statement';
+import { type NamespaceStatement, type ClassStatement, type EnumStatement, type InterfaceStatement, type EnumMemberStatement, type ConstStatement } from './parser/Statement';
 import { ParseMode } from './parser/Parser';
 import { util } from './util';
 import { globalCallableMap } from './globalCallables';
@@ -16,9 +16,9 @@ import { URI } from 'vscode-uri';
 import { LogLevel } from './Logger';
 import type { BrsFile } from './files/BrsFile';
 import type { DependencyGraph, DependencyChangedEvent } from './DependencyGraph';
-import { isBrsFile, isXmlFile, isEnumMemberStatement, isNamespaceStatement, isNamespaceType, isReferenceType, isCallableType, isFunctionStatement } from './astUtils/reflection';
+import { isBrsFile, isXmlFile, isEnumMemberStatement, isNamespaceStatement, isNamespaceType, isReferenceType, isCallableType, isFunctionStatement, isEnumStatement, isConstStatement, isInterfaceStatement } from './astUtils/reflection';
 import { SymbolTable } from './SymbolTable';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import type { BscFile } from './files/BscFile';
 import type { BscType } from './types/BscType';
 import { NamespaceType } from './types/NamespaceType';
@@ -26,9 +26,9 @@ import { referenceTypeFactory } from './types/ReferenceType';
 import { unionTypeFactory } from './types/UnionType';
 import { AssociativeArrayType } from './types/AssociativeArrayType';
 import type { AstNode, Statement } from './parser/AstNode';
-import { AstNodeKind } from './parser/AstNode';
 import { WalkMode, createVisitor } from './astUtils/visitors';
 import type { Token } from './lexer/Token';
+import type { Range } from 'vscode-languageserver';
 
 /**
  * Assign some few factories to the SymbolTable to prevent cyclical imports. This file seems like the most intuitive place to do the linking
@@ -69,6 +69,7 @@ export class Scope {
      * "namea", "namea.nameb", "namea.nameb.namec"
      */
     public get namespaceLookup() {
+
         let allFilesValidated = true;
         for (const file of this.getAllFiles()) {
             if (isBrsFile(file) && !file.hasTypedef) {
@@ -83,7 +84,6 @@ export class Scope {
             // Since the files have not been validated, all namespace info might not have been available
             return this.buildNamespaceLookup();
         }
-
         return this.cache.getOrAdd('namespaceLookup', () => this.buildNamespaceLookup());
     }
 
@@ -248,14 +248,19 @@ export class Scope {
         return result;
     }
 
-    public getAllFileLinks(name: string, containingNamespace?: string): FileLink<Statement>[] {
+    public getAllFileLinks(name: string, containingNamespace?: string, includeNameShadowsOutsideNamespace = false): FileLink<Statement>[] {
         let links: FileLink<Statement>[] = [];
 
-        links.push(this.getClassFileLink(name) ?? this.getClassFileLink(name, containingNamespace),
-            this.getInterfaceFileLink(name) ?? this.getInterfaceFileLink(name, containingNamespace),
-            this.getConstFileLink(name) ?? this.getConstFileLink(name, containingNamespace),
-            this.getEnumFileLink(name) ?? this.getEnumFileLink(name, containingNamespace));
-
+        links.push(this.getClassFileLink(name, containingNamespace),
+            this.getInterfaceFileLink(name, containingNamespace),
+            this.getConstFileLink(name, containingNamespace),
+            this.getEnumFileLink(name, containingNamespace));
+        if (includeNameShadowsOutsideNamespace && containingNamespace) {
+            links.push(this.getClassFileLink(name),
+                this.getInterfaceFileLink(name),
+                this.getConstFileLink(name),
+                this.getEnumFileLink(name));
+        }
 
         const nameSpaces = this.getNamespacesWithRoot(name, containingNamespace);
         if (nameSpaces?.length > 0) {
@@ -267,7 +272,7 @@ export class Scope {
         const fullNameLower = (containingNamespace ? `${containingNamespace}.${name}` : name).toLowerCase();
         const callable = this.getCallableByName(name);
         if (callable) {
-            if (!callable.hasNamespace || callable.getName(ParseMode.BrighterScript).toLowerCase() === fullNameLower) {
+            if ((!callable.hasNamespace && includeNameShadowsOutsideNamespace) || callable.getName(ParseMode.BrighterScript).toLowerCase() === fullNameLower) {
                 // this callable has no namespace, or has same namespace
                 links.push({ item: callable.functionStatement, file: callable.file as BrsFile });
             }
@@ -594,6 +599,37 @@ export class Scope {
         });
     }
 
+    public getCallableContainerMap() {
+        return this.cache.getOrAdd('callableContainerMap', () => {
+            let callables = this.getAllCallables();
+
+            //sort the callables by filepath and then method name, so the errors will be consistent
+            // eslint-disable-next-line prefer-arrow-callback
+            callables = callables.sort((a, b) => {
+                const pathA = a.callable.file.srcPath;
+                const pathB = b.callable.file.srcPath;
+                //sort by path
+                if (pathA < pathB) {
+                    return -1;
+                } else if (pathA > pathB) {
+                    return 1;
+                }
+                //sort by function name
+                const funcA = b.callable.name;
+                const funcB = b.callable.name;
+                if (funcA < funcB) {
+                    return -1;
+                } else if (funcA > funcB) {
+                    return 1;
+                }
+                return 0;
+            });
+
+            //get a list of all callables, indexed by their lower case names
+            return util.getCallableContainersByLowerName(callables);
+        });
+    }
+
     /**
      * Iterate over Brs files not shadowed by typedefs
      */
@@ -709,32 +745,7 @@ export class Scope {
             //clear the scope's errors list (we will populate them from this method)
             this.clearScopeLevelDiagnostics();
 
-            let callables = this.getAllCallables();
-
-            //sort the callables by filepath and then method name, so the errors will be consistent
-            // eslint-disable-next-line prefer-arrow-callback
-            callables = callables.sort((a, b) => {
-                const pathA = a.callable.file.srcPath;
-                const pathB = b.callable.file.srcPath;
-                //sort by path
-                if (pathA < pathB) {
-                    return -1;
-                } else if (pathA > pathB) {
-                    return 1;
-                }
-                //sort by function name
-                const funcA = b.callable.name;
-                const funcB = b.callable.name;
-                if (funcA < funcB) {
-                    return -1;
-                } else if (funcA > funcB) {
-                    return 1;
-                }
-                return 0;
-            });
 
-            //get a list of all callables, indexed by their lower case names
-            let callableContainerMap = util.getCallableContainersByLowerName(callables);
             //Since statements from files are shared across multiple scopes, we need to link those statements to the current scope
             this.linkSymbolTable();
             const scopeValidateEvent = {
@@ -745,7 +756,7 @@ export class Scope {
             };
             this.program.plugins.emit('beforeScopeValidate', scopeValidateEvent);
             this.program.plugins.emit('onScopeValidate', scopeValidateEvent);
-            this._validate(callableContainerMap);
+            this._validate();
             this.program.plugins.emit('afterScopeValidate', scopeValidateEvent);
             //unlink all symbol tables from this scope (so they don't accidentally stick around)
             this.unlinkSymbolTable();
@@ -754,9 +765,9 @@ export class Scope {
         });
     }
 
-    protected _validate(callableContainerMap: CallableContainerMap) {
+    protected _validate() {
         //find all duplicate function declarations
-        this.diagnosticFindDuplicateFunctionDeclarations(callableContainerMap);
+        this.diagnosticFindDuplicateFunctionDeclarations();
 
         //detect missing and incorrect-case script imports
         this.diagnosticValidateScriptImportPaths();
@@ -766,7 +777,6 @@ export class Scope {
 
         //do many per-file checks
         this.enumerateBrsFiles((file) => {
-            this.diagnosticDetectShadowedLocalVars(file, callableContainerMap);
             this.diagnosticDetectFunctionCollisions(file);
             this.detectVariableNamespaceCollisions(file);
             this.detectNameCollisions(file);
@@ -848,7 +858,6 @@ export class Scope {
 
         // eslint-disable-next-line no-bitwise
         let getTypeOptions = { flags: SymbolTypeFlag.runtime | SymbolTypeFlag.typetime };
-
         for (const [nsName, nsContainer] of this.namespaceLookup) {
             let currentNSType: BscType = null;
             let parentNSType: BscType = null;
@@ -879,7 +888,7 @@ export class Scope {
                         this.symbolsAddedDuringLinking.push({ symbolTable: this.symbolTable, name: nsContainer.lastPartName, flags: getTypeOptions.flags });
                     }
                 } else {
-                    this.program.logger.error(`Invalid existing type for namespace ${nsName} - ${currentNSType.toString()}`);
+                    // Something else already used the name this namespace is using.
                     continue;
                 }
             } else {
@@ -1016,9 +1025,24 @@ export class Scope {
             },
             EnumStatement: (enumStmt) => {
                 this.validateNameCollision(file, enumStmt, enumStmt.tokens.name);
+            },
+            AssignmentStatement: (assignStmt) => {
+                // Note: this also includes For statements
+                this.detectShadowedLocalVar(file, {
+                    name: assignStmt.tokens.name.text,
+                    type: assignStmt.getType({ flags: SymbolTypeFlag.runtime }),
+                    nameRange: assignStmt.tokens.name.range
+                });
+            },
+            ForEachStatement: (forEachStmt) => {
+                this.detectShadowedLocalVar(file, {
+                    name: forEachStmt.tokens.item.text,
+                    type: forEachStmt.getType({ flags: SymbolTypeFlag.runtime }),
+                    nameRange: forEachStmt.tokens.item.range
+                });
             }
         }), {
-            walkMode: WalkMode.visitStatements
+            walkMode: WalkMode.visitAllRecursive
         });
     }
 
@@ -1031,6 +1055,7 @@ export class Scope {
         const nameRange = nameIdentifier.range;
 
         const containingNamespace = node.findAncestor<NamespaceStatement>(isNamespaceStatement)?.getName(ParseMode.BrighterScript);
+        const containingNamespaceLower = containingNamespace?.toLowerCase();
         const links = this.getAllFileLinks(name, containingNamespace);
         for (let link of links) {
             if (!link || link.item === node) {
@@ -1042,10 +1067,17 @@ export class Scope {
                 continue;
             }
             if (isFunctionStatement(link.item) || link.file?.destPath === 'global') {
-                // the thing found is a function OR from global (which is also a function)
-                if (isNamespaceStatement(node)) {
-                    // these are not callable functions in transpiled code - ignore them
-                    continue;
+                const linkItemNamespaceLower = link.item?.findAncestor<NamespaceStatement>(isNamespaceStatement)?.getName(ParseMode.BrighterScript)?.toLowerCase();
+                if (!(containingNamespaceLower && linkItemNamespaceLower) || linkItemNamespaceLower !== containingNamespaceLower) {
+
+                    // the thing found is a function OR from global (which is also a function)
+                    if (isNamespaceStatement(node) ||
+                        isEnumStatement(node) ||
+                        isConstStatement(node) ||
+                        isInterfaceStatement(node)) {
+                        // these are not callable functions in transpiled code - ignore them
+                        continue;
+                    }
                 }
             }
 
@@ -1054,16 +1086,8 @@ export class Scope {
 
             let thatNameRange = (link.item as any)?.tokens?.name?.range ?? link.item?.range;
 
-            // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
-            switch (link.item?.kind) {
-                case AstNodeKind.ClassStatement: {
-                    thatNameRange = (link.item as ClassStatement).tokens.name?.range;
-                    break;
-                }
-                case AstNodeKind.NamespaceStatement: {
-                    thatNameRange = (link.item as NamespaceStatement).getNameParts()?.[0]?.range;
-                    break;
-                }
+            if (isNamespaceStatement(link.item)) {
+                thatNameRange = (link.item as NamespaceStatement).getNameParts()?.[0]?.range;
             }
 
             const relatedInformation = thatNameRange ? [{
@@ -1093,71 +1117,88 @@ export class Scope {
         }));
     }
 
-    /**
-     * Detect local variables (function scope) that have the same name as scope calls
-     */
-    private diagnosticDetectShadowedLocalVars(file: BrsFile, callableContainerMap: CallableContainerMap) {
+
+    private detectShadowedLocalVar(file: BrsFile, varDeclaration: { name: string; type: BscType; nameRange: Range }) {
+        const varName = varDeclaration.name;
+        const lowerVarName = varName.toLowerCase();
         const classMap = this.getClassMap();
-        //loop through every function scope
-        for (let funcScope of file.functionScopes) {
-            //every var declaration in this function scope
-            for (let varDeclaration of funcScope.variableDeclarations) {
-                const varName = varDeclaration.name;
-                const lowerVarName = varName.toLowerCase();
-
-                const varIsFunction = () => {
-                    return isCallableType(varDeclaration.getType());
-                };
-
-                if (
-                    //has same name as stdlib
-                    globalCallableMap.has(lowerVarName)
-                ) {
-                    //local var function with same name as stdlib function
-                    if (varIsFunction()) {
-                        this.diagnostics.push({
-                            ...DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib'),
-                            range: varDeclaration.nameRange,
-                            file: file,
-                            origin: DiagnosticOrigin.Scope
-                        });
-                    }
-                } else if (callableContainerMap.has(lowerVarName)) {
-                    //is same name as a callable
-                    if (varIsFunction()) {
-                        this.diagnostics.push({
-                            ...DiagnosticMessages.localVarFunctionShadowsParentFunction('scope'),
-                            range: varDeclaration.nameRange,
-                            file: file,
-                            origin: DiagnosticOrigin.Scope
-                        });
-                    } else {
-                        this.diagnostics.push({
-                            ...DiagnosticMessages.localVarShadowedByScopedFunction(),
-                            range: varDeclaration.nameRange,
-                            file: file,
-                            origin: DiagnosticOrigin.Scope
-                        });
-                    }
-                    //has the same name as an in-scope class
-                } else if (classMap.has(lowerVarName)) {
-                    this.diagnostics.push({
-                        ...DiagnosticMessages.localVarSameNameAsClass(classMap.get(lowerVarName)?.item.getName(ParseMode.BrighterScript)),
-                        range: varDeclaration.nameRange,
-                        file: file,
-                        origin: DiagnosticOrigin.Scope
-                    });
-                }
+        const callableContainerMap = this.getCallableContainerMap();
+
+        const varIsFunction = () => {
+            return isCallableType(varDeclaration.type);
+        };
+
+        if (
+            //has same name as stdlib
+            globalCallableMap.has(lowerVarName)
+        ) {
+            //local var function with same name as stdlib function
+            if (varIsFunction()) {
+                this.diagnostics.push({
+                    ...DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib'),
+                    range: varDeclaration.nameRange,
+                    file: file,
+                    origin: DiagnosticOrigin.Scope
+                });
+            }
+        } else if (callableContainerMap.has(lowerVarName)) {
+            const callable = callableContainerMap.get(lowerVarName);
+            //is same name as a callable
+            if (varIsFunction()) {
+                this.diagnostics.push({
+                    ...DiagnosticMessages.localVarFunctionShadowsParentFunction('scope'),
+                    range: varDeclaration.nameRange,
+                    file: file,
+                    origin: DiagnosticOrigin.Scope,
+                    relatedInformation: [{
+                        message: 'Function declared here',
+                        location: util.createLocation(
+                            URI.file(callable[0].callable.file.srcPath).toString(),
+                            callable[0].callable.nameRange
+                        )
+                    }]
+                });
+            } else {
+                this.diagnostics.push({
+                    ...DiagnosticMessages.localVarShadowedByScopedFunction(),
+                    range: varDeclaration.nameRange,
+                    file: file,
+                    origin: DiagnosticOrigin.Scope,
+                    relatedInformation: [{
+                        message: 'Function declared here',
+                        location: util.createLocation(
+                            URI.file(callable[0].callable.file.srcPath).toString(),
+                            callable[0].callable.nameRange
+                        )
+                    }]
+                });
             }
+            //has the same name as an in-scope class
+        } else if (classMap.has(lowerVarName)) {
+            const classStmtLink = classMap.get(lowerVarName);
+            this.diagnostics.push({
+                ...DiagnosticMessages.localVarSameNameAsClass(classStmtLink?.item?.getName(ParseMode.BrighterScript)),
+                range: varDeclaration.nameRange,
+                file: file,
+                origin: DiagnosticOrigin.Scope,
+                relatedInformation: [{
+                    message: 'Class declared here',
+                    location: util.createLocation(
+                        URI.file(classStmtLink.file.srcPath).toString(),
+                        classStmtLink?.item.tokens.name.range
+                    )
+                }]
+            });
         }
     }
 
     /**
      * Create diagnostics for any duplicate function declarations
      */
-    private diagnosticFindDuplicateFunctionDeclarations(callableContainersByLowerName: CallableContainerMap) {
+    private diagnosticFindDuplicateFunctionDeclarations() {
+
         //for each list of callables with the same name
-        for (let [lowerName, callableContainers] of callableContainersByLowerName) {
+        for (let [lowerName, callableContainers] of this.getCallableContainerMap()) {
 
             let globalCallables = [] as CallableContainer[];
             let nonGlobalCallables = [] as CallableContainer[];
diff --git a/src/SymbolTable.spec.ts b/src/SymbolTable.spec.ts
index a8c4974d..698eff1c 100644
--- a/src/SymbolTable.spec.ts
+++ b/src/SymbolTable.spec.ts
@@ -3,7 +3,7 @@ import { expect } from './chai-config.spec';
 import { StringType } from './types/StringType';
 import { IntegerType } from './types/IntegerType';
 import { BooleanType } from './types/BooleanType';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import { expectTypeToBe } from './testHelpers.spec';
 
 describe('SymbolTable', () => {
diff --git a/src/SymbolTable.ts b/src/SymbolTable.ts
index fa1d2eba..2856942c 100644
--- a/src/SymbolTable.ts
+++ b/src/SymbolTable.ts
@@ -5,7 +5,7 @@ import type { ReferenceType } from './types/ReferenceType';
 import type { UnionType } from './types/UnionType';
 import { getUniqueType } from './types/helpers';
 import { isAnyReferenceType, isReferenceType } from './astUtils/reflection';
-import type { SymbolTypeFlag } from './SymbolTableFlag';
+import type { SymbolTypeFlag } from './SymbolTypeFlag';
 
 /**
  * Stores the types associated with variables and functions in the Brighterscript code
diff --git a/src/SymbolTableFlag.ts b/src/SymbolTypeFlag.ts
similarity index 100%
rename from src/SymbolTableFlag.ts
rename to src/SymbolTypeFlag.ts
diff --git a/src/XmlScope.spec.ts b/src/XmlScope.spec.ts
index 6d7ce6e3..0ed337c0 100644
--- a/src/XmlScope.spec.ts
+++ b/src/XmlScope.spec.ts
@@ -8,7 +8,7 @@ import { standardizePath as s, util } from './util';
 let rootDir = s`${process.cwd()}/rootDir`;
 import { createSandbox } from 'sinon';
 import { ComponentType } from './types/ComponentType';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import { AssociativeArrayType } from './types/AssociativeArrayType';
 import { ArrayType, BooleanType, DoubleType, DynamicType, FloatType, IntegerType, StringType, TypedFunctionType, UnionType } from './types';
 const sinon = createSandbox();
diff --git a/src/XmlScope.ts b/src/XmlScope.ts
index 5d1f6b2e..7fea08af 100644
--- a/src/XmlScope.ts
+++ b/src/XmlScope.ts
@@ -1,13 +1,13 @@
 import { Scope } from './Scope';
 import { DiagnosticMessages } from './DiagnosticMessages';
 import type { XmlFile } from './files/XmlFile';
-import type { CallableContainerMap, FileReference } from './interfaces';
+import type { FileReference } from './interfaces';
 import { DiagnosticOrigin } from './interfaces';
 import type { Program } from './Program';
 import type { SGElement } from './parser/SGTypes';
 import { SGFieldTypes } from './parser/SGTypes';
 import util from './util';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import type { BscFile } from './files/BscFile';
 import { DynamicType } from './types/DynamicType';
 import type { BaseFunctionType } from './types/BaseFunctionType';
@@ -81,23 +81,23 @@ export class XmlScope extends Scope {
         return result;
     }
 
-    protected _validate(callableContainerMap: CallableContainerMap) {
+    protected _validate() {
         //validate brs files
-        super._validate(callableContainerMap);
+        super._validate();
 
         //detect when the child imports a script that its ancestor also imports
         this.diagnosticDetectDuplicateAncestorScriptImports();
 
         //validate component interface
-        this.diagnosticValidateInterface(callableContainerMap);
+        this.diagnosticValidateInterface();
     }
 
-    private diagnosticValidateInterface(callableContainerMap: CallableContainerMap) {
+    private diagnosticValidateInterface() {
         if (!this.xmlFile.parser.ast?.componentElement?.interfaceElement) {
             return;
         }
         const iface = this.xmlFile.parser.ast.componentElement.interfaceElement;
-
+        const callableContainerMap = this.getCallableContainerMap();
         //validate functions
         for (const func of iface.functions) {
             const name = func.name;
diff --git a/src/bscPlugin/completions/CompletionsProcessor.ts b/src/bscPlugin/completions/CompletionsProcessor.ts
index cd1903ca..eb4e9ab2 100644
--- a/src/bscPlugin/completions/CompletionsProcessor.ts
+++ b/src/bscPlugin/completions/CompletionsProcessor.ts
@@ -9,7 +9,7 @@ import { ParseMode } from '../../parser/Parser';
 import type { CompletionItem, Position } from 'vscode-languageserver';
 import { CompletionItemKind, TextEdit } from 'vscode-languageserver';
 import type { BscSymbol } from '../../SymbolTable';
-import { SymbolTypeFlag } from '../../SymbolTableFlag';
+import { SymbolTypeFlag } from '../../SymbolTypeFlag';
 import type { XmlFile } from '../../files/XmlFile';
 import type { Program } from '../../Program';
 import type { BrsFile } from '../../files/BrsFile';
diff --git a/src/bscPlugin/hover/HoverProcessor.ts b/src/bscPlugin/hover/HoverProcessor.ts
index 8c7e8806..bb29ac51 100644
--- a/src/bscPlugin/hover/HoverProcessor.ts
+++ b/src/bscPlugin/hover/HoverProcessor.ts
@@ -7,7 +7,7 @@ import { TokenKind } from '../../lexer/TokenKind';
 import { BrsTranspileState } from '../../parser/BrsTranspileState';
 import { ParseMode } from '../../parser/Parser';
 import util from '../../util';
-import { SymbolTypeFlag } from '../../SymbolTableFlag';
+import { SymbolTypeFlag } from '../../SymbolTypeFlag';
 import type { AstNode, Expression } from '../../parser/AstNode';
 import type { Scope } from '../../Scope';
 import type { FunctionScope } from '../../FunctionScope';
diff --git a/src/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.ts b/src/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.ts
index c06e8e6f..601722de 100644
--- a/src/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.ts
+++ b/src/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.ts
@@ -8,7 +8,7 @@ import type { Locatable } from '../../lexer/Token';
 import { ParseMode } from '../../parser/Parser';
 import type { NamespaceStatement } from '../../parser/Statement';
 import util from '../../util';
-import { SymbolTypeFlag } from '../../SymbolTableFlag';
+import { SymbolTypeFlag } from '../../SymbolTypeFlag';
 import type { BscType } from '../../types/BscType';
 
 export class BrsFileSemanticTokensProcessor {
diff --git a/src/bscPlugin/validation/BrsFileValidator.ts b/src/bscPlugin/validation/BrsFileValidator.ts
index a0b4540a..061d632b 100644
--- a/src/bscPlugin/validation/BrsFileValidator.ts
+++ b/src/bscPlugin/validation/BrsFileValidator.ts
@@ -8,7 +8,7 @@ import type { AstNode, Expression, Statement } from '../../parser/AstNode';
 import type { FunctionExpression, LiteralExpression } from '../../parser/Expression';
 import { ParseMode } from '../../parser/Parser';
 import type { ContinueStatement, EnumMemberStatement, EnumStatement, ForEachStatement, ForStatement, ImportStatement, LibraryStatement, WhileStatement } from '../../parser/Statement';
-import { SymbolTypeFlag } from '../../SymbolTableFlag';
+import { SymbolTypeFlag } from '../../SymbolTypeFlag';
 import { AssociativeArrayType } from '../../types/AssociativeArrayType';
 import { DynamicType } from '../../types/DynamicType';
 import util from '../../util';
diff --git a/src/bscPlugin/validation/ScopeValidator.spec.ts b/src/bscPlugin/validation/ScopeValidator.spec.ts
index 797099eb..a9da2526 100644
--- a/src/bscPlugin/validation/ScopeValidator.spec.ts
+++ b/src/bscPlugin/validation/ScopeValidator.spec.ts
@@ -8,7 +8,7 @@ import { IntegerType } from '../../types/IntegerType';
 import { StringType } from '../../types/StringType';
 import type { BrsFile } from '../../files/BrsFile';
 import { FloatType } from '../../types';
-import { SymbolTypeFlag } from '../../SymbolTableFlag';
+import { SymbolTypeFlag } from '../../SymbolTypeFlag';
 
 describe('ScopeValidator', () => {
 
diff --git a/src/bscPlugin/validation/ScopeValidator.ts b/src/bscPlugin/validation/ScopeValidator.ts
index 0f17b9a1..1f2db499 100644
--- a/src/bscPlugin/validation/ScopeValidator.ts
+++ b/src/bscPlugin/validation/ScopeValidator.ts
@@ -5,7 +5,7 @@ import { DiagnosticMessages } from '../../DiagnosticMessages';
 import type { BrsFile } from '../../files/BrsFile';
 import { DiagnosticOrigin } from '../../interfaces';
 import type { BsDiagnostic, BsDiagnosticWithOrigin, ExtraSymbolData, OnScopeValidateEvent, TypeChainEntry, TypeCompatibilityData } from '../../interfaces';
-import { SymbolTypeFlag } from '../../SymbolTableFlag';
+import { SymbolTypeFlag } from '../../SymbolTypeFlag';
 import type { AssignmentStatement, ClassStatement, DottedSetStatement, EnumStatement, NamespaceStatement, ReturnStatement } from '../../parser/Statement';
 import util from '../../util';
 import { nodes, components } from '../../roku-types';
diff --git a/src/files/BrsFile.Class.spec.ts b/src/files/BrsFile.Class.spec.ts
index c29abcca..20fedfd7 100644
--- a/src/files/BrsFile.Class.spec.ts
+++ b/src/files/BrsFile.Class.spec.ts
@@ -1677,19 +1677,24 @@ describe('BrsFile BrighterScript classes', () => {
         ]);
     });
 
-    it('catches namespaced class name which is the same as a global class', () => {
+    it('allows namespaced class name which is the same as a global class', () => {
         program.setFile('source/main.bs', `
             namespace NameA.NameB
                 class Animal
+                    name as string
                 end class
+
+                sub printThisAnimalName(ani as Animal) ' this refers to NameA.NameB.Animal
+                    print ani.name
+                end sub
             end namespace
+
             class Animal
+                doesNotHaveName as string
             end class
         `);
         program.validate();
-        expectDiagnostics(program, [
-            DiagnosticMessages.nameCollision('Class', 'Class', 'Animal').message
-        ]);
+        expectZeroDiagnostics(program);
     });
 
     it('catches class with same name as function', () => {
diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts
index 50908c1a..fd9c6af9 100644
--- a/src/files/BrsFile.spec.ts
+++ b/src/files/BrsFile.spec.ts
@@ -21,7 +21,7 @@ import * as fsExtra from 'fs-extra';
 import { URI } from 'vscode-uri';
 import undent from 'undent';
 import { tempDir, rootDir } from '../testHelpers.spec';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { ClassType, EnumType, FloatType, InterfaceType } from '../types';
 import type { StandardizedFileEntry } from 'roku-deploy';
 import * as fileUrl from 'file-url';
diff --git a/src/files/BrsFile.ts b/src/files/BrsFile.ts
index 4a59fe3b..22f3edc1 100644
--- a/src/files/BrsFile.ts
+++ b/src/files/BrsFile.ts
@@ -30,7 +30,7 @@ import type { UnresolvedSymbol } from '../AstValidationSegmenter';
 import { AstValidationSegmenter } from '../AstValidationSegmenter';
 import type { BscSymbol } from '../SymbolTable';
 import { SymbolTable } from '../SymbolTable';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import type { BscFileLike } from '../astUtils/CachedLookups';
 import { CachedLookups } from '../astUtils/CachedLookups';
 import { Editor } from '../astUtils/Editor';
diff --git a/src/interfaces.ts b/src/interfaces.ts
index b484c3aa..5e256f26 100644
--- a/src/interfaces.ts
+++ b/src/interfaces.ts
@@ -17,7 +17,7 @@ import type { BscFile } from './files/BscFile';
 import type { FileFactory } from './files/Factory';
 import type { LazyFileData } from './files/LazyFileData';
 import type { SymbolTable } from './SymbolTable';
-import type { SymbolTypeFlag } from './SymbolTableFlag';
+import type { SymbolTypeFlag } from './SymbolTypeFlag';
 import { createToken } from './astUtils/creators';
 import { TokenKind } from './lexer/TokenKind';
 
diff --git a/src/parser/Expression.ts b/src/parser/Expression.ts
index 9f9ac755..b4e4a1c7 100644
--- a/src/parser/Expression.ts
+++ b/src/parser/Expression.ts
@@ -28,8 +28,8 @@ import { ArrayType } from '../types/ArrayType';
 import { AssociativeArrayType } from '../types/AssociativeArrayType';
 import type { ComponentType } from '../types/ComponentType';
 import { createToken } from '../astUtils/creators';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
 import { TypedFunctionType } from '../types';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 
 export type ExpressionVisitor = (expression: Expression, parent: Expression) => void;
 
diff --git a/src/parser/Parser.Class.spec.ts b/src/parser/Parser.Class.spec.ts
index af208b7c..9e2d0a42 100644
--- a/src/parser/Parser.Class.spec.ts
+++ b/src/parser/Parser.Class.spec.ts
@@ -9,7 +9,7 @@ import { NewExpression } from './Expression';
 import { expectDiagnosticsIncludes, expectZeroDiagnostics } from '../testHelpers.spec';
 import { isClassStatement } from '../astUtils/reflection';
 import { StringType } from '../types/StringType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 
 describe('parser class', () => {
     it('throws exception when used in brightscript scope', () => {
diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts
index 370260b6..3a8c79d2 100644
--- a/src/parser/Parser.spec.ts
+++ b/src/parser/Parser.spec.ts
@@ -12,7 +12,7 @@ import { isAssignmentStatement, isBinaryExpression, isBlock, isCallExpression, i
 import { expectDiagnosticsIncludes, expectTypeToBe, expectZeroDiagnostics } from '../testHelpers.spec';
 import { createVisitor, WalkMode } from '../astUtils/visitors';
 import type { Expression, Statement } from './AstNode';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { IntegerType } from '../types/IntegerType';
 import { FloatType } from '../types/FloatType';
 import { StringType } from '../types/StringType';
diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts
index 77659b58..3fbde335 100644
--- a/src/parser/Statement.ts
+++ b/src/parser/Statement.ts
@@ -25,7 +25,7 @@ import { InterfaceType } from '../types/InterfaceType';
 import { VoidType } from '../types/VoidType';
 import { TypedFunctionType } from '../types/TypedFunctionType';
 import { ArrayType } from '../types/ArrayType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 
 export class EmptyStatement extends Statement {
     constructor(options?: { range?: Range }
diff --git a/src/types/ArrayType.spec.ts b/src/types/ArrayType.spec.ts
index f5a6b9cd..9732101c 100644
--- a/src/types/ArrayType.spec.ts
+++ b/src/types/ArrayType.spec.ts
@@ -4,7 +4,7 @@ import { ArrayType } from './ArrayType';
 import { DynamicType } from './DynamicType';
 import { BooleanType } from './BooleanType';
 import { StringType } from './StringType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { TypedFunctionType } from './TypedFunctionType';
 import { expectTypeToBe } from '../testHelpers.spec';
 import { IntegerType } from './IntegerType';
diff --git a/src/types/ArrayType.ts b/src/types/ArrayType.ts
index 0966b470..cce27f1b 100644
--- a/src/types/ArrayType.ts
+++ b/src/types/ArrayType.ts
@@ -1,5 +1,5 @@
 
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { isArrayType, isDynamicType, isObjectType } from '../astUtils/reflection';
 import type { TypeCompatibilityData } from '../interfaces';
 import { BscType } from './BscType';
diff --git a/src/types/AssociativeArrayType.ts b/src/types/AssociativeArrayType.ts
index fc255f1e..b0c286df 100644
--- a/src/types/AssociativeArrayType.ts
+++ b/src/types/AssociativeArrayType.ts
@@ -1,4 +1,4 @@
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { isAssociativeArrayType, isClassType, isDynamicType, isObjectType } from '../astUtils/reflection';
 import type { GetTypeOptions, TypeCompatibilityData } from '../interfaces';
 import { BscType } from './BscType';
diff --git a/src/types/BscType.ts b/src/types/BscType.ts
index 5374ec62..469e05bb 100644
--- a/src/types/BscType.ts
+++ b/src/types/BscType.ts
@@ -1,6 +1,6 @@
 import type { GetSymbolTypeOptions, SymbolTableProvider } from '../SymbolTable';
 import { SymbolTable } from '../SymbolTable';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { BuiltInInterfaceAdder } from './BuiltInInterfaceAdder';
 import type { ExtraSymbolData, TypeCompatibilityData } from '../interfaces';
 import { isArrayType, isInheritableType, isReferenceType } from '../astUtils/reflection';
diff --git a/src/types/BuiltInInterfaceAdder.spec.ts b/src/types/BuiltInInterfaceAdder.spec.ts
index 34c4cc98..55a1a6a0 100644
--- a/src/types/BuiltInInterfaceAdder.spec.ts
+++ b/src/types/BuiltInInterfaceAdder.spec.ts
@@ -2,7 +2,7 @@ import { BuiltInInterfaceAdder } from './BuiltInInterfaceAdder';
 import { StringType } from './StringType';
 import { expectTypeToBe } from '../testHelpers.spec';
 import { IntegerType } from './IntegerType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { TypedFunctionType } from './TypedFunctionType';
 import { BooleanType } from './BooleanType';
 import { FunctionType } from './FunctionType';
diff --git a/src/types/BuiltInInterfaceAdder.ts b/src/types/BuiltInInterfaceAdder.ts
index dfd37d40..b6aeaa98 100644
--- a/src/types/BuiltInInterfaceAdder.ts
+++ b/src/types/BuiltInInterfaceAdder.ts
@@ -3,7 +3,7 @@ import { components, events, interfaces, nodes } from '../roku-types';
 import { Cache } from '../Cache';
 import type { TypedFunctionType } from './TypedFunctionType';
 import type { SymbolTable } from '../SymbolTable';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import type { BscType } from './BscType';
 import { isArrayType, isAssociativeArrayType, isBooleanType, isCallableType, isComponentType, isDoubleType, isEnumMemberType, isFloatType, isIntegerType, isInterfaceType, isInvalidType, isLongIntegerType, isStringType } from '../astUtils/reflection';
 import type { ComponentType } from './ComponentType';
diff --git a/src/types/ClassType.spec.ts b/src/types/ClassType.spec.ts
index 4f07993d..df4210a9 100644
--- a/src/types/ClassType.spec.ts
+++ b/src/types/ClassType.spec.ts
@@ -6,7 +6,7 @@ import { expectTypeToBe } from '../testHelpers.spec';
 import { ReferenceType } from './ReferenceType';
 import { isReferenceType } from '../astUtils/reflection';
 import { IntegerType } from './IntegerType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 
 describe('ClassType', () => {
 
diff --git a/src/types/ComponentType.ts b/src/types/ComponentType.ts
index 00192550..623a2a73 100644
--- a/src/types/ComponentType.ts
+++ b/src/types/ComponentType.ts
@@ -1,5 +1,5 @@
 import type { GetSymbolTypeOptions } from '../SymbolTable';
-import type { SymbolTypeFlag } from '../SymbolTableFlag';
+import type { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { SymbolTable } from '../SymbolTable';
 import { isComponentType, isDynamicType, isObjectType } from '../astUtils/reflection';
 import type { ExtraSymbolData, TypeCompatibilityData } from '../interfaces';
diff --git a/src/types/EnumType.ts b/src/types/EnumType.ts
index 4183fe92..7498694f 100644
--- a/src/types/EnumType.ts
+++ b/src/types/EnumType.ts
@@ -1,4 +1,4 @@
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { isDynamicType, isEnumMemberType, isEnumType, isObjectType } from '../astUtils/reflection';
 import type { TypeCompatibilityData } from '../interfaces';
 import { BscType } from './BscType';
diff --git a/src/types/InheritableType.ts b/src/types/InheritableType.ts
index 891937c4..a02e369c 100644
--- a/src/types/InheritableType.ts
+++ b/src/types/InheritableType.ts
@@ -1,6 +1,6 @@
 import type { GetTypeOptions, TypeCompatibilityData } from '../interfaces';
 import { isInheritableType, isReferenceType } from '../astUtils/reflection';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { BscType } from './BscType';
 import type { ReferenceType } from './ReferenceType';
 
diff --git a/src/types/InterfaceType.spec.ts b/src/types/InterfaceType.spec.ts
index aa93891a..5665fd22 100644
--- a/src/types/InterfaceType.spec.ts
+++ b/src/types/InterfaceType.spec.ts
@@ -7,7 +7,7 @@ import { InterfaceType } from './InterfaceType';
 import { ObjectType } from './ObjectType';
 import { StringType } from './StringType';
 import type { ReferenceType } from './ReferenceType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { AssociativeArrayType } from './AssociativeArrayType';
 import { ArrayType } from './ArrayType';
 import { BooleanType } from './BooleanType';
diff --git a/src/types/InterfaceType.ts b/src/types/InterfaceType.ts
index bf3ecdb4..5c65668b 100644
--- a/src/types/InterfaceType.ts
+++ b/src/types/InterfaceType.ts
@@ -1,5 +1,5 @@
 import type { TypeCompatibilityData } from '../interfaces';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { isDynamicType, isInterfaceType, isObjectType } from '../astUtils/reflection';
 import type { BscType } from './BscType';
 import { BscTypeKind } from './BscTypeKind';
diff --git a/src/types/ObjectType.ts b/src/types/ObjectType.ts
index 8463a5a3..0bff8c6b 100644
--- a/src/types/ObjectType.ts
+++ b/src/types/ObjectType.ts
@@ -1,4 +1,4 @@
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { isObjectType } from '../astUtils/reflection';
 import type { GetTypeOptions, TypeCompatibilityData } from '../interfaces';
 import { BscType } from './BscType';
diff --git a/src/types/ReferenceType.spec.ts b/src/types/ReferenceType.spec.ts
index 6888c128..142a6bf2 100644
--- a/src/types/ReferenceType.spec.ts
+++ b/src/types/ReferenceType.spec.ts
@@ -1,6 +1,6 @@
 import { expect } from 'chai';
 import { SymbolTable } from '../SymbolTable';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { expectTypeToBe } from '../testHelpers.spec';
 import { DynamicType } from './DynamicType';
 import { IntegerType } from './IntegerType';
diff --git a/src/types/ReferenceType.ts b/src/types/ReferenceType.ts
index b583fa5d..781ce3f6 100644
--- a/src/types/ReferenceType.ts
+++ b/src/types/ReferenceType.ts
@@ -1,6 +1,6 @@
 import type { GetTypeOptions, TypeChainEntry, TypeCompatibilityData } from '../interfaces';
 import type { GetSymbolTypeOptions, SymbolTypeGetterProvider } from '../SymbolTable';
-import type { SymbolTypeFlag } from '../SymbolTableFlag';
+import type { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { isAnyReferenceType, isComponentType, isDynamicType, isReferenceType } from '../astUtils/reflection';
 import { BscType } from './BscType';
 import { DynamicType } from './DynamicType';
diff --git a/src/types/UnionType.spec.ts b/src/types/UnionType.spec.ts
index 24bac9d6..e4a4056f 100644
--- a/src/types/UnionType.spec.ts
+++ b/src/types/UnionType.spec.ts
@@ -4,7 +4,7 @@ import { IntegerType } from './IntegerType';
 import { UnionType } from './UnionType';
 import { FloatType } from './FloatType';
 import { InterfaceType } from './InterfaceType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { BooleanType } from './BooleanType';
 import { expectTypeToBe } from '../testHelpers.spec';
 import { isReferenceType } from '../astUtils/reflection';
diff --git a/src/types/UnionType.ts b/src/types/UnionType.ts
index ce9ea457..608fbc38 100644
--- a/src/types/UnionType.ts
+++ b/src/types/UnionType.ts
@@ -6,7 +6,7 @@ import { findTypeUnion, getUniqueType, isEnumTypeCompatible } from './helpers';
 import { BscTypeKind } from './BscTypeKind';
 import type { TypeCacheEntry } from '../SymbolTable';
 import { SymbolTable } from '../SymbolTable';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { BuiltInInterfaceAdder } from './BuiltInInterfaceAdder';
 
 export function unionTypeFactory(types: BscType[]) {
diff --git a/src/types/helper.spec.ts b/src/types/helper.spec.ts
index ace96306..df265bc5 100644
--- a/src/types/helper.spec.ts
+++ b/src/types/helper.spec.ts
@@ -9,7 +9,7 @@ import { StringType } from './StringType';
 import { UnionType, unionTypeFactory } from './UnionType';
 import { findTypeIntersection, findTypeUnion, getUniqueType, getUniqueTypesFromArray } from './helpers';
 import { InterfaceType } from './InterfaceType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import { DoubleType } from './DoubleType';
 import { BooleanType } from './BooleanType';
 import { EnumType, EnumMemberType } from './EnumType';
diff --git a/src/util.spec.ts b/src/util.spec.ts
index 6a29402a..de379ed7 100644
--- a/src/util.spec.ts
+++ b/src/util.spec.ts
@@ -13,7 +13,7 @@ import { TypeChainEntry } from './interfaces';
 import { NamespaceType } from './types/NamespaceType';
 import { ClassType } from './types/ClassType';
 import { ReferenceType } from './types/ReferenceType';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import { BooleanType, DoubleType, DynamicType, FloatType, IntegerType, InvalidType, LongIntegerType, StringType, TypedFunctionType, VoidType } from './types';
 import { TokenKind } from './lexer/TokenKind';
 import { createToken } from './astUtils/creators';
diff --git a/src/util.ts b/src/util.ts
index 1725d4fb..5239ab39 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -36,7 +36,7 @@ import type { AstNode, Expression, Statement } from './parser/AstNode';
 import { AstNodeKind } from './parser/AstNode';
 import type { UnresolvedSymbol } from './AstValidationSegmenter';
 import type { SymbolTable } from './SymbolTable';
-import { SymbolTypeFlag } from './SymbolTableFlag';
+import { SymbolTypeFlag } from './SymbolTypeFlag';
 import { createIdentifier, createToken } from './astUtils/creators';
 import { MAX_RELATED_INFOS_COUNT } from './diagnosticUtils';
 import type { BscType } from './types/BscType';
diff --git a/src/validators/ClassValidator.ts b/src/validators/ClassValidator.ts
index 2f5a02fd..cb90a59e 100644
--- a/src/validators/ClassValidator.ts
+++ b/src/validators/ClassValidator.ts
@@ -11,7 +11,7 @@ import type { BrsFile } from '../files/BrsFile';
 import { TokenKind } from '../lexer/TokenKind';
 import { DynamicType } from '../types/DynamicType';
 import type { BscType } from '../types/BscType';
-import { SymbolTypeFlag } from '../SymbolTableFlag';
+import { SymbolTypeFlag } from '../SymbolTypeFlag';
 import type { BscFile } from '../files/BscFile';
 
 export class BsClassValidator {


Test patch:
diff --git a/src/parser/tests/expression/TypeExpression.spec.ts b/src/parser/tests/expression/TypeExpression.spec.ts
index 69cb2e08..4d0d9c3e 100644
--- a/src/parser/tests/expression/TypeExpression.spec.ts
+++ b/src/parser/tests/expression/TypeExpression.spec.ts
@@ -3,7 +3,7 @@ import { ParseMode } from '../../Parser';
 import { parse } from '../../Parser.spec';
 import { expectTypeToBe, expectZeroDiagnostics } from '../../../testHelpers.spec';
 import { isFunctionStatement, isReferenceType, isTypeExpression } from '../../../astUtils/reflection';
-import { SymbolTypeFlag } from '../../../SymbolTableFlag';
+import { SymbolTypeFlag } from '../../../SymbolTypeFlag';
 import { IntegerType } from '../../../types/IntegerType';
 import { StringType } from '../../../types/StringType';
 import { UnionType } from '../../../types/UnionType';