Repository: rokucommunity/brighterscript
Pull request #876 (2023-08-30 13:38:05)

Adds Return type validation
- Validates actual return type vs. the declared return type
- Subs and void functions will have validation errors when an actual type is included
- works in Brs and Bs files


https://github.com/rokucommunity/brighterscript/assets/810290/d065c1ee-30f2-496f-ad4d-2e66182b166b




Patch:
diff --git a/package-lock.json b/package-lock.json
index f3ec0994..32c5dc88 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1898,9 +1898,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001442",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz",
-      "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==",
+      "version": "1.0.30001518",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz",
+      "integrity": "sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==",
       "dev": true,
       "funding": [
         {
@@ -1910,6 +1910,10 @@
         {
           "type": "tidelift",
           "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
         }
       ]
     },
@@ -9338,9 +9342,9 @@
       "dev": true
     },
     "caniuse-lite": {
-      "version": "1.0.30001442",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz",
-      "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==",
+      "version": "1.0.30001518",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz",
+      "integrity": "sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==",
       "dev": true
     },
     "caseless": {
diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts
index 6419fd34..1d8fbd7a 100644
--- a/src/DiagnosticMessages.ts
+++ b/src/DiagnosticMessages.ts
@@ -734,6 +734,11 @@ export let DiagnosticMessages = {
         message: `Argument of type '${actualTypeString}' is not compatible with parameter of type '${expectedTypeString}'`,
         code: 1141,
         severity: DiagnosticSeverity.Error
+    }),
+    returnTypeMismatch: (actualTypeString: string, expectedTypeString: string) => ({
+        message: `Type '${actualTypeString}' is not compatible with declared return type '${expectedTypeString}'`,
+        code: 1142,
+        severity: DiagnosticSeverity.Error
     })
 };
 
diff --git a/src/Program.spec.ts b/src/Program.spec.ts
index 1f24688f..f4ab9e0d 100644
--- a/src/Program.spec.ts
+++ b/src/Program.spec.ts
@@ -1142,8 +1142,38 @@ describe('Program', () => {
         });
     });
 
-    describe('transpile', () => {
+    it('beforeProgramTranspile sends entries in alphabetical order', () => {
+        program.setFile('source/main.bs', trim`
+            sub main()
+                print "hello world"
+            end sub
+        `);
 
+        program.setFile('source/common.bs', trim`
+            sub getString()
+                return "test"
+            end sub
+        `);
+
+        //send the files out of order
+        const result = program['beforeProgramTranspile']([{
+            src: s`${rootDir}/source/main.bs`,
+            dest: 'source/main.bs'
+        }, {
+            src: s`${rootDir}/source/main.bs`,
+            dest: 'source/main.bs'
+        }], program.options.stagingDir);
+
+        //entries should now be in alphabetic order
+        expect(
+            result.entries.map(x => x.outputPath)
+        ).to.eql([
+            s`${stagingDir}/source/common.brs`,
+            s`${stagingDir}/source/main.brs`
+        ]);
+    });
+
+    describe('transpile', () => {
         it('detects and transpiles files added between beforeProgramTranspile and afterProgramTranspile', async () => {
             program.setFile('source/main.bs', trim`
                 sub main()
diff --git a/src/Program.ts b/src/Program.ts
index 8d510a45..0f7d4085 100644
--- a/src/Program.ts
+++ b/src/Program.ts
@@ -1124,6 +1124,9 @@ export class Program {
                 file: file,
                 outputPath: getOutputPath(file)
             };
+            //sort the entries to make transpiling more deterministic
+        }).sort((a, b) => {
+            return a.file.srcPath < b.file.srcPath ? -1 : 1;
         });
 
         const astEditor = new AstEditor();
diff --git a/src/ProgramBuilder.ts b/src/ProgramBuilder.ts
index 953fc1ee..2e22b776 100644
--- a/src/ProgramBuilder.ts
+++ b/src/ProgramBuilder.ts
@@ -13,6 +13,7 @@ import * as diagnosticUtils from './diagnosticUtils';
 import * as fsExtra from 'fs-extra';
 import * as requireRelative from 'require-relative';
 import { Throttler } from './Throttler';
+import { URI } from 'vscode-uri';
 
 /**
  * A runner class that handles
@@ -322,8 +323,19 @@ export class ProgramBuilder {
             for (let diagnostic of sortedDiagnostics) {
                 //default the severity to error if undefined
                 let severity = typeof diagnostic.severity === 'number' ? diagnostic.severity : DiagnosticSeverity.Error;
+                let relatedInformation = (diagnostic.relatedInformation ?? []).map(x => {
+                    let relatedInfoFilePath = URI.parse(x.location.uri).fsPath;
+                    if (!emitFullPaths) {
+                        relatedInfoFilePath = path.relative(cwd, relatedInfoFilePath);
+                    }
+                    return {
+                        filePath: relatedInfoFilePath,
+                        range: x.location.range,
+                        message: x.message
+                    };
+                });
                 //format output
-                diagnosticUtils.printDiagnostic(options, severity, filePath, lines, diagnostic);
+                diagnosticUtils.printDiagnostic(options, severity, filePath, lines, diagnostic, relatedInformation);
             }
         }
     }
diff --git a/src/Scope.spec.ts b/src/Scope.spec.ts
index c224ea18..3b522599 100644
--- a/src/Scope.spec.ts
+++ b/src/Scope.spec.ts
@@ -1204,7 +1204,7 @@ describe('Scope', () => {
                     end function
                 `);
                 program.validate();
-                expectDiagnostics(program, [
+                expectDiagnosticsIncludes(program, [
                     DiagnosticMessages.cannotFindName('unknownType').message,
                     DiagnosticMessages.cannotFindName('unknownType').message
                 ]);
diff --git a/src/bscPlugin/CallExpressionInfo.ts b/src/bscPlugin/CallExpressionInfo.ts
index e200ae74..7d1bd644 100644
--- a/src/bscPlugin/CallExpressionInfo.ts
+++ b/src/bscPlugin/CallExpressionInfo.ts
@@ -24,7 +24,7 @@ export class CallExpressionInfo {
     expression?: Expression;
 
     //the contextually relevant callExpression, which relates to it
-    callExpression?: CallExpression;
+    callExpression?: CallExpression | CallfuncExpression;
     type: CallExpressionType;
 
     file: BrsFile;
@@ -66,7 +66,7 @@ export class CallExpressionInfo {
             this.newExpression = callExpression.parent;
         }
         if (isCallfuncExpression(callExpression)) {
-            this.name = (callExpression as CallfuncExpression).methodName.text;
+            this.name = callExpression.methodName.text;
         } else if (isVariableExpression(callExpression.callee)) {
             this.name = callExpression.callee.name.text;
         } else if (isVariableExpression(callExpression)) {
@@ -94,7 +94,7 @@ export class CallExpressionInfo {
         return util.rangeContains(boundingRange, this.position);
     }
 
-    ascertainCallExpression(): CallExpression {
+    ascertainCallExpression(): CallExpression | CallfuncExpression {
         let expression = this.expression;
         function isCallFuncOrCallExpression(expression: Expression) {
             return isCallfuncExpression(expression) || isCallExpression(expression);
@@ -116,7 +116,7 @@ export class CallExpressionInfo {
             }
         }
 
-        return callExpression as CallExpression;
+        return callExpression;
     }
 
     ascertainType(): CallExpressionType {
diff --git a/src/bscPlugin/validation/ScopeValidator.spec.ts b/src/bscPlugin/validation/ScopeValidator.spec.ts
index ee35a420..bdaa0ec1 100644
--- a/src/bscPlugin/validation/ScopeValidator.spec.ts
+++ b/src/bscPlugin/validation/ScopeValidator.spec.ts
@@ -962,4 +962,99 @@ describe('ScopeValidator', () => {
             ]);
         });
     });
+
+    describe('returnTypeMismatch', () => {
+        it('finds when a function returns a type that is not what was declared', () => {
+            program.setFile('source/util.bs', `
+                function getPi() as float
+                    return "apple" ' get it?
+                end function
+            `);
+            program.validate();
+            //should have error - return value should be a float, not a string
+            expectDiagnostics(program, [
+                DiagnosticMessages.returnTypeMismatch('string', 'float').message
+            ]);
+        });
+
+        it('finds all return statements that do not match', () => {
+            program.setFile('source/util.bs', `
+                function getPi(kind as integer) as float
+                    if kind = 1
+                        return "apple"
+                    else if kind = 2
+                        return false
+                    else if kind = 3
+                        return new Pie("lemon")
+                    end if
+                    return 3.14
+                end function
+
+                class Pie
+                   kind as string
+                   sub new(kind as string)
+                       m.kind = kind
+                   end sub
+                end class
+            `);
+            program.validate();
+            //should have error - return value should be a float, not whatever else
+            expectDiagnostics(program, [
+                DiagnosticMessages.returnTypeMismatch('string', 'float').message,
+                DiagnosticMessages.returnTypeMismatch('boolean', 'float').message,
+                DiagnosticMessages.returnTypeMismatch('Pie', 'float').message
+            ]);
+        });
+
+
+        it('allows returning compatible types', () => {
+            program.setFile('source/util.bs', `
+                function getPi() as float
+                    return 3 ' integers are compatible with floats
+                end function
+
+                function getPie() as Pie
+                    return new Tart("lemon") ' Tart extends Pie
+                end function
+
+                class Pie
+                    kind as string
+                    sub new(kind as string)
+                        m.kind = kind
+                    end sub
+                end class
+
+                class Tart extends Pie
+                    size = "small"
+                end class
+            `);
+            program.validate();
+            //should have no errors
+            expectZeroDiagnostics(program);
+        });
+
+        it('detects return types on void functions (subs)', () => {
+            program.setFile('source/util.bs', `
+                sub sayHello(name as string)
+                    return "hello " + name ' return should be void in subs
+                end sub
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.returnTypeMismatch('string', 'void').message
+            ]);
+        });
+
+        it('detects return types on void functions', () => {
+            program.setFile('source/util.bs', `
+                function sayHello(name as string) as void
+                    return "hello " + name ' return should be void in subs
+                end function
+            `);
+            program.validate();
+            expectDiagnostics(program, [
+                DiagnosticMessages.returnTypeMismatch('string', 'void').message
+            ]);
+        });
+    });
 });
diff --git a/src/bscPlugin/validation/ScopeValidator.ts b/src/bscPlugin/validation/ScopeValidator.ts
index d8770dbd..124a0e36 100644
--- a/src/bscPlugin/validation/ScopeValidator.ts
+++ b/src/bscPlugin/validation/ScopeValidator.ts
@@ -1,11 +1,11 @@
 import { URI } from 'vscode-uri';
-import { isAssignmentStatement, isBinaryExpression, isBrsFile, isClassType, isLiteralExpression, isNamespaceStatement, isTypeExpression, isTypedFunctionType, isUnionType, isVariableExpression, isXmlScope } from '../../astUtils/reflection';
+import { isAssignmentStatement, isBinaryExpression, isBrsFile, isClassType, isFunctionExpression, isLiteralExpression, isNamespaceStatement, isTypeExpression, isTypedFunctionType, isUnionType, isVariableExpression, isXmlScope } from '../../astUtils/reflection';
 import { Cache } from '../../Cache';
 import { DiagnosticMessages } from '../../DiagnosticMessages';
 import type { BrsFile } from '../../files/BrsFile';
 import type { BscFile, BsDiagnostic, OnScopeValidateEvent } from '../../interfaces';
 import { SymbolTypeFlag } from '../../SymbolTable';
-import type { AssignmentStatement, EnumStatement, NamespaceStatement } from '../../parser/Statement';
+import type { AssignmentStatement, EnumStatement, NamespaceStatement, ReturnStatement } from '../../parser/Statement';
 import util from '../../util';
 import { nodes, components } from '../../roku-types';
 import type { BRSComponentData } from '../../roku-types';
@@ -58,9 +58,13 @@ export class ScopeValidator {
                 file.ast.walk(createVisitor({
                     CallExpression: (functionCall) => {
                         this.validateFunctionCall(file, functionCall);
+                    },
+                    ReturnStatement: (returnStatement) => {
+                        this.validateReturnStatement(file, returnStatement);
                     }
+
                 }), {
-                    walkMode: WalkMode.visitExpressionsRecursive
+                    walkMode: WalkMode.visitAllRecursive
                 });
             }
         });
@@ -459,6 +463,28 @@ export class ScopeValidator {
         this.event.scope.addDiagnostics(diagnostics);
     }
 
+
+    /**
+     * Detect return statements with incompatible types vs. declared return type
+     */
+    private validateReturnStatement(file: BrsFile, returnStmt: ReturnStatement) {
+        const diagnostics: BsDiagnostic[] = [];
+        const getTypeOptions = { flags: SymbolTypeFlag.runtime };
+        let funcType = returnStmt.findAncestor(isFunctionExpression).getType({ flags: SymbolTypeFlag.typetime });
+        if (isTypedFunctionType(funcType)) {
+            const actualReturnType = returnStmt.value?.getType(getTypeOptions);
+            if (actualReturnType && !funcType.returnType.isTypeCompatible(actualReturnType)) {
+                this.addMultiScopeDiagnostic({
+                    ...DiagnosticMessages.returnTypeMismatch(actualReturnType.toString(), funcType.returnType.toString()),
+                    range: returnStmt.value.range,
+                    file: file
+                });
+
+            }
+        }
+        this.event.scope.addDiagnostics(diagnostics);
+    }
+
     /**
      * Adds a diagnostic to the first scope for this key. Prevents duplicate diagnostics
      * for diagnostics where scope isn't important. (i.e. CreateObject validations)
@@ -492,7 +518,7 @@ export class ScopeValidator {
         if (isXmlScope(this.event.scope) && this.event.scope.xmlFile?.srcPath) {
             info.location = util.createLocation(
                 URI.file(this.event.scope.xmlFile.srcPath).toString(),
-                util.createRange(0, 0, 0, 10)
+                this.event.scope?.xmlFile?.ast?.componentElement?.getAttribute('name')?.range ?? util.createRange(0, 0, 0, 10)
             );
         } else {
             info.location = util.createLocation(
diff --git a/src/diagnosticUtils.spec.ts b/src/diagnosticUtils.spec.ts
index 05a075c4..f88491da 100644
--- a/src/diagnosticUtils.spec.ts
+++ b/src/diagnosticUtils.spec.ts
@@ -245,7 +245,7 @@ describe('diagnosticUtils', () => {
                 diagnosticUtils.getDiagnosticLine({ 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, ' ')
+                chalk.bgWhite(' ' + chalk.white(' '.repeat((range.start.line + 1).toString().length)) + ' ') + ' ' + squigglyText.padEnd(lineLength, ' ')
             ].join('\n'));
         }
 
diff --git a/src/diagnosticUtils.ts b/src/diagnosticUtils.ts
index af621463..bf88de75 100644
--- a/src/diagnosticUtils.ts
+++ b/src/diagnosticUtils.ts
@@ -3,6 +3,7 @@ import chalk from 'chalk';
 import type { BsConfig } from './BsConfig';
 import { DiagnosticSeverity } from 'vscode-languageserver';
 import type { BsDiagnostic } from '.';
+import type { Range } from 'vscode-languageserver';
 
 /**
  * Prepare print diagnostic formatting options
@@ -57,7 +58,8 @@ export function printDiagnostic(
     severity: DiagnosticSeverity,
     filePath: string,
     lines: string[],
-    diagnostic: BsDiagnostic
+    diagnostic: BsDiagnostic,
+    relatedInformation?: Array<{ range: Range; filePath: string; message: string }>
 ) {
     let { includeDiagnostic, severityTextMap, typeColor } = options;
 
@@ -91,6 +93,31 @@ export function printDiagnostic(
     console.log(
         getDiagnosticLine(diagnostic, diagnosticLine, typeColor[severity])
     );
+
+    //print related information if present (only first few rows)
+    const relatedInfoList = relatedInformation ?? [];
+    let indent = '    ';
+    for (let i = 0; i < relatedInfoList.length; i++) {
+        let relatedInfo = relatedInfoList[i];
+        //only show the first 5 relatedInfo links
+        if (i < 5) {
+            console.log('');
+            console.log(
+                indent,
+                chalk.cyan(relatedInfo.filePath ?? '<unknown file>') +
+                ':' +
+                chalk.yellow(
+                    relatedInfo.range
+                        ? (relatedInfo.range.start.line + 1) + ':' + (relatedInfo.range.start.character + 1)
+                        : 'line?:col?'
+                )
+            );
+            console.log(indent, relatedInfo.message);
+        } else {
+            console.log('\n', indent, `...and ${relatedInfoList.length - i + 1} more`);
+            break;
+        }
+    }
     console.log('');
 }
 
@@ -100,7 +127,7 @@ export function getDiagnosticLine(diagnostic: BsDiagnostic, diagnosticLine: stri
     //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)) + ' ') + ' ';
+        const blankLineNumberText = chalk.bgWhite(' ' + chalk.white(' '.repeat((diagnostic.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);
diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts
index f843da26..7c0dd814 100644
--- a/src/files/BrsFile.spec.ts
+++ b/src/files/BrsFile.spec.ts
@@ -1625,7 +1625,7 @@ describe('BrsFile', () => {
                     else if true
                     else if true then
                         if true then
-                            return true
+                            return
                         end if
                     end if
                 end sub
diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts
index 266d4afb..d198e91b 100644
--- a/src/parser/Statement.ts
+++ b/src/parser/Statement.ts
@@ -1,7 +1,7 @@
 /* eslint-disable no-bitwise */
 import type { Token, Identifier } from '../lexer/Token';
 import { CompoundAssignmentOperators, TokenKind } from '../lexer/TokenKind';
-import type { BinaryExpression, FunctionExpression, FunctionParameterExpression, LiteralExpression, TypeExpression } from './Expression';
+import type { BinaryExpression, DottedGetExpression, FunctionExpression, FunctionParameterExpression, LiteralExpression, TypeExpression } from './Expression';
 import { CallExpression, VariableExpression } from './Expression';
 import { util } from '../util';
 import type { Range } from 'vscode-languageserver';
@@ -1192,8 +1192,7 @@ export class LibraryStatement extends Statement implements TypedefProvider {
 export class NamespaceStatement extends Statement implements TypedefProvider {
     constructor(
         public keyword: Token,
-        // this should technically only be a VariableExpression or DottedGetExpression, but that can be enforced elsewhere
-        public nameExpression: Expression,
+        public nameExpression: VariableExpression | DottedGetExpression,
         public body: Body,
         public endKeyword: Token
     ) {
diff --git a/src/util.spec.ts b/src/util.spec.ts
index 9d04e64c..da8b2aa7 100644
--- a/src/util.spec.ts
+++ b/src/util.spec.ts
@@ -460,7 +460,16 @@ describe('util', () => {
         });
     });
 
-    describe('compareRangeToPosition', () => {
+    describe('comparePositionToRange', () => {
+        it('does not crash on undefined props', () => {
+            expect(
+                util.comparePositionToRange(null, util.createRange(0, 0, 0, 0))
+            ).to.eql(0);
+            expect(
+                util.comparePositionToRange(util.createPosition(1, 1), null)
+            ).to.eql(0);
+        });
+
         it('correctly compares positions to ranges with one line range line', () => {
             let range = Range.create(1, 10, 1, 15);
             expect(util.comparePositionToRange(Position.create(0, 13), range)).to.equal(-1);
@@ -579,6 +588,15 @@ describe('util', () => {
     });
 
     describe('rangesIntersect', () => {
+        it('does not crash on undefined range', () => {
+            expect(
+                util.rangesIntersect(null, util.createRange(0, 0, 0, 0))
+            ).to.be.false;
+            expect(
+                util.rangesIntersect(util.createRange(0, 0, 0, 0), null)
+            ).to.be.false;
+        });
+
         it('does not match when ranges do not touch (a < b)', () => {
             // AA BB
             expect(util.rangesIntersectOrTouch(
@@ -669,6 +687,15 @@ describe('util', () => {
     });
 
     describe('rangesIntersectOrTouch', () => {
+        it('does not crash on undefined range', () => {
+            expect(
+                util.rangesIntersectOrTouch(null, util.createRange(0, 0, 0, 0))
+            ).to.be.false;
+            expect(
+                util.rangesIntersectOrTouch(util.createRange(0, 0, 0, 0), null)
+            ).to.be.false;
+        });
+
         it('does not match when ranges do not touch (a < b)', () => {
             // AA BB
             expect(util.rangesIntersectOrTouch(
diff --git a/src/util.ts b/src/util.ts
index 4889ac8f..f13c3b50 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -505,6 +505,11 @@ export class Util {
      * ```
      */
     public rangesIntersect(a: Range, b: Range) {
+        //stop if the either range is misisng
+        if (!a || !b) {
+            return false;
+        }
+
         // Check if `a` is before `b`
         if (a.end.line < b.start.line || (a.end.line === b.start.line && a.end.character <= b.start.character)) {
             return false;
@@ -529,6 +534,10 @@ export class Util {
      * ```
      */
     public rangesIntersectOrTouch(a: Range, b: Range) {
+        //stop if the either range is misisng
+        if (!a || !b) {
+            return false;
+        }
         // Check if `a` is before `b`
         if (a.end.line < b.start.line || (a.end.line === b.start.line && a.end.character < b.start.character)) {
             return false;
@@ -552,6 +561,11 @@ export class Util {
     }
 
     public comparePositionToRange(position: Position, range: Range) {
+        //stop if the either range is misisng
+        if (!position || !range) {
+            return 0;
+        }
+
         if (position.line < range.start.line || (position.line === range.start.line && position.character < range.start.character)) {
             return -1;
         }


Test patch:
diff --git a/src/parser/tests/statement/Enum.spec.ts b/src/parser/tests/statement/Enum.spec.ts
index 80299ae7..d91e6761 100644
--- a/src/parser/tests/statement/Enum.spec.ts
+++ b/src/parser/tests/statement/Enum.spec.ts
@@ -610,7 +610,7 @@ describe('EnumStatement', () => {
                         val2
                     end enum
 
-                    function foo() as integer
+                    function foo() as MyEnum
                         return MyEnum.val1
                     end function
                 end namespace