Repository: rokucommunity/brighterscript
Pull request #900 (2023-09-19 18:36:54)

Interface param support
Adds support for defining method parameters on interfaces.

markwpearce: Wow... I didn;t know this was even an issue haha

Patch:
diff --git a/docs/interfaces.md b/docs/interfaces.md
index 7465a305..c3883c56 100644
--- a/docs/interfaces.md
+++ b/docs/interfaces.md
@@ -18,7 +18,7 @@ end sub
 transpiles to
 
 ```BrightScript
-sub test(bob as object)
+sub test(bob as dynamic)
     print bob.name, bob.getName()
 end sub
 ```
@@ -41,7 +41,7 @@ end sub
   <summary>View the transpiled BrightScript code</summary>
 
 ```BrightScript
-sub walkThePuppy(puppy as object) as object
+sub walkThePuppy(puppy as dynamic) as dynamic
     puppy.walk()
     return puppy
 end sub
@@ -67,7 +67,7 @@ end sub
 transpiles to
 
 ```BrightScript
-sub test(bob as object)
+sub test(bob as dynamic)
     print bob.name, bob.getName()
 end sub
 ```
@@ -90,5 +90,21 @@ end interface
 
 ```BrightScript
 
+```
+</details>
+
+## Methods
+Interfaces can describe complex methods as well
+```brighterscript
+interface Dog
+    sub barkAt(nemesis as Cat)
+end interface
+```
+
+<details>
+  <summary>View the transpiled BrightScript code</summary>
+
+```BrightScript
+
 ```
 </details>
\ No newline at end of file
diff --git a/src/Scope.spec.ts b/src/Scope.spec.ts
index 22682331..71d2ceea 100644
--- a/src/Scope.spec.ts
+++ b/src/Scope.spec.ts
@@ -1014,6 +1014,51 @@ describe('Scope', () => {
             ]);
         });
 
+        it('detects calling interface function with too few args', () => {
+            program.setFile('source/file.bs', `
+                sub init(arg as Tester)
+                    arg.test()
+                end sub
+                interface Tester
+                    sub test(param1)
+                end interface
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.mismatchArgumentCount(1, 0).message
+            ]);
+        });
+
+        it('detects calling interface function with too many args', () => {
+            program.setFile('source/file.bs', `
+                sub init(arg as Tester)
+                    arg.test(1, 2)
+                end sub
+                interface Tester
+                    sub test(param1)
+                end interface
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.mismatchArgumentCount(1, 2).message
+            ]);
+        });
+
+        it('detects calling interface function with mismatch argument type', () => {
+            program.setFile('source/file.bs', `
+                sub init(arg as Tester)
+                    arg.test(1)
+                end sub
+                interface Tester
+                    sub test(param1 as string)
+                end interface
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.argumentTypeMismatch('integer', 'string').message
+            ]);
+        });
+
         it('detects calling class constructors with too many parameters', () => {
             program.setFile('source/main.bs', `
                 function noop0()
diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts
index 18398202..7d8a26e7 100644
--- a/src/parser/Parser.ts
+++ b/src/parser/Parser.ts
@@ -427,7 +427,19 @@ export class Parser {
         const name = this.identifier(...AllowedProperties);
         const leftParen = this.consumeToken(TokenKind.LeftParen);
 
-        const params = [];
+        let params = [] as FunctionParameterExpression[];
+        if (!this.check(TokenKind.RightParen)) {
+            do {
+                if (params.length >= CallExpression.MaximumArguments) {
+                    this.diagnostics.push({
+                        ...DiagnosticMessages.tooManyCallableParameters(params.length, CallExpression.MaximumArguments),
+                        range: this.peek().range
+                    });
+                }
+
+                params.push(this.functionParameter());
+            } while (this.match(TokenKind.Comma));
+        }
         const rightParen = this.consumeToken(TokenKind.RightParen);
         // let asToken = null as Token;
         // let returnTypeExpression: TypeExpression;


Test patch:
diff --git a/src/parser/tests/statement/InterfaceStatement.spec.ts b/src/parser/tests/statement/InterfaceStatement.spec.ts
index b39caea5..7d952d10 100644
--- a/src/parser/tests/statement/InterfaceStatement.spec.ts
+++ b/src/parser/tests/statement/InterfaceStatement.spec.ts
@@ -87,4 +87,13 @@ describe('InterfaceStatement', () => {
             'this comment was throwing exception during transpile
         `);
     });
+
+    it('allows parameters in interface method signatures', () => {
+        testGetTypedef(`
+            interface Person
+                sub someFunc(name as string, age as integer) as string
+                someField as string
+            end interface
+        `, undefined, undefined, undefined, true);
+    });
 });