Repository: rokucommunity/brighterscript
Pull request #1169 (2024-05-09 16:48:27)

Move function calls to separate diagnostic
Moves "cannot find name" diagnostics for function calls into their own diagnostic code. This will help dev teams more slowly migrate to "best practices" in their projects by ignoring this specific diagnostic when functions are imported into only some of the scopes the script is used in. 

**Before:**
![image](https://github.com/rokucommunity/brighterscript/assets/2544493/3dc02691-48af-4abf-ae5b-0f7d3eebf70d)

**After:**
![image](https://github.com/rokucommunity/brighterscript/assets/2544493/05adf3b9-88f6-4c9b-9b48-6e056adc0567)



Patch:
diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts
index ceda048c..764aa9f3 100644
--- a/src/DiagnosticMessages.ts
+++ b/src/DiagnosticMessages.ts
@@ -724,6 +724,20 @@ export let DiagnosticMessages = {
         message: `Optional chaining may not be used in the left-hand side of an assignment`,
         code: 1139,
         severity: DiagnosticSeverity.Error
+    }),
+    /**
+     *
+     * @param name for function calls where we can't find the name of the function
+     * @param fullName if a namespaced name, this is the full name `alpha.beta.charlie`, otherwise it's the same as `name`
+     */
+    cannotFindFunction: (name: string, fullName?: string) => ({
+        message: `Cannot find function '${name}'`,
+        code: 1140,
+        data: {
+            name: name,
+            fullName: fullName ?? name
+        },
+        severity: DiagnosticSeverity.Error
     })
 };
 
diff --git a/src/Program.spec.ts b/src/Program.spec.ts
index 3d06aa1b..34ebd498 100644
--- a/src/Program.spec.ts
+++ b/src/Program.spec.ts
@@ -507,7 +507,7 @@ describe('Program', () => {
 
             program.validate();
             expectDiagnostics(program, [
-                DiagnosticMessages.cannotFindName('DoSomething')
+                DiagnosticMessages.cannotFindFunction('DoSomething')
             ]);
         });
 
@@ -1576,7 +1576,7 @@ describe('Program', () => {
 
             //there should be an error when calling DoParentThing, since it doesn't exist on child or parent
             expectDiagnostics(program, [
-                DiagnosticMessages.cannotFindName('DoParentThing')
+                DiagnosticMessages.cannotFindFunction('DoParentThing')
             ]);
 
             //add the script into the parent
@@ -1734,7 +1734,7 @@ describe('Program', () => {
             ];
 
             expectDiagnostics(program, [
-                DiagnosticMessages.cannotFindName('C')
+                DiagnosticMessages.cannotFindFunction('C')
             ]);
         });
     });
diff --git a/src/Scope.spec.ts b/src/Scope.spec.ts
index f38376a0..bce1d0f3 100644
--- a/src/Scope.spec.ts
+++ b/src/Scope.spec.ts
@@ -932,7 +932,7 @@ describe('Scope', () => {
             //validate the scope
             program.validate();
             expectDiagnostics(program, [
-                DiagnosticMessages.cannotFindName('DoB')
+                DiagnosticMessages.cannotFindFunction('DoB')
             ]);
         });
 
@@ -948,7 +948,7 @@ describe('Scope', () => {
             //validate the scope
             program.validate();
             expectDiagnostics(program, [
-                DiagnosticMessages.cannotFindName('DoC')
+                DiagnosticMessages.cannotFindFunction('DoC')
             ]);
         });
 
diff --git a/src/bscPlugin/codeActions/CodeActionsProcessor.ts b/src/bscPlugin/codeActions/CodeActionsProcessor.ts
index 3776e338..423445e6 100644
--- a/src/bscPlugin/codeActions/CodeActionsProcessor.ts
+++ b/src/bscPlugin/codeActions/CodeActionsProcessor.ts
@@ -18,7 +18,7 @@ export class CodeActionsProcessor {
 
     public process() {
         for (const diagnostic of this.event.diagnostics) {
-            if (diagnostic.code === DiagnosticCodeMap.cannotFindName) {
+            if (diagnostic.code === DiagnosticCodeMap.cannotFindName || diagnostic.code === DiagnosticCodeMap.cannotFindFunction) {
                 this.suggestCannotFindName(diagnostic as any);
             } else if (diagnostic.code === DiagnosticCodeMap.classCouldNotBeFound) {
                 this.suggestClassImports(diagnostic as any);
diff --git a/src/bscPlugin/validation/ScopeValidator.ts b/src/bscPlugin/validation/ScopeValidator.ts
index b5ff60ac..900263cf 100644
--- a/src/bscPlugin/validation/ScopeValidator.ts
+++ b/src/bscPlugin/validation/ScopeValidator.ts
@@ -1,5 +1,5 @@
 import { URI } from 'vscode-uri';
-import { isBrsFile, isLiteralExpression, isNamespaceStatement, isXmlScope } from '../../astUtils/reflection';
+import { isBrsFile, isCallExpression, isLiteralExpression, isNamespaceStatement, isXmlScope } from '../../astUtils/reflection';
 import { Cache } from '../../Cache';
 import { DiagnosticMessages } from '../../DiagnosticMessages';
 import type { BrsFile } from '../../files/BrsFile';
@@ -100,11 +100,20 @@ export class ScopeValidator {
                 !symbolTable?.hasSymbol(firstPart.name?.text) &&
                 !namespaceContainer
             ) {
-                this.addMultiScopeDiagnostic({
-                    file: file as BscFile,
-                    ...DiagnosticMessages.cannotFindName(firstPart.name?.text),
-                    range: firstPart.name.range
-                });
+                //flag functions differently than all other variables
+                if (isCallExpression(firstPart.parent) && firstPart.parent.callee === firstPart) {
+                    this.addMultiScopeDiagnostic({
+                        file: file as BscFile,
+                        ...DiagnosticMessages.cannotFindFunction(firstPart.name?.text),
+                        range: firstPart.name.range
+                    });
+                } else {
+                    this.addMultiScopeDiagnostic({
+                        file: file as BscFile,
+                        ...DiagnosticMessages.cannotFindName(firstPart.name?.text),
+                        range: firstPart.name.range
+                    });
+                }
                 //skip to the next expression
                 continue;
             }
@@ -152,11 +161,20 @@ export class ScopeValidator {
                             }]
                         });
                     } else {
-                        this.addMultiScopeDiagnostic({
-                            ...DiagnosticMessages.cannotFindName(part.name.text, entityName),
-                            range: part.name.range,
-                            file: file
-                        });
+                        //flag functions differently than all other variables
+                        if (isCallExpression(firstPart.parent) && firstPart.parent.callee === firstPart) {
+                            this.addMultiScopeDiagnostic({
+                                ...DiagnosticMessages.cannotFindFunction(part.name.text, entityName),
+                                range: part.name.range,
+                                file: file
+                            });
+                        } else {
+                            this.addMultiScopeDiagnostic({
+                                ...DiagnosticMessages.cannotFindName(part.name.text, entityName),
+                                range: part.name.range,
+                                file: file
+                            });
+                        }
                     }
                     //no need to add another diagnostic for future unknown items
                     continue outer;
diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts
index 580b0769..c71c8345 100644
--- a/src/files/BrsFile.spec.ts
+++ b/src/files/BrsFile.spec.ts
@@ -1648,7 +1648,7 @@ describe('BrsFile', () => {
             `);
             program.validate();
             expectDiagnostics(program, [
-                DiagnosticMessages.cannotFindName('DoesNotExist')
+                DiagnosticMessages.cannotFindFunction('DoesNotExist')
             ]);
         });
 


Test patch:
diff --git a/src/files/tests/imports.spec.ts b/src/files/tests/imports.spec.ts
index 79343327..07240ada 100644
--- a/src/files/tests/imports.spec.ts
+++ b/src/files/tests/imports.spec.ts
@@ -156,7 +156,7 @@ describe('import statements', () => {
         program.validate();
 
         expectDiagnostics(program, [
-            DiagnosticMessages.cannotFindName('Waddle')
+            DiagnosticMessages.cannotFindFunction('Waddle')
         ]);
 
         //add the missing function