From 2b377de373622d8b8c1d958e97321695a9ebb0d8 Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Fri, 13 Dec 2024 14:35:36 -0400 Subject: [PATCH] Changes expectedReturnStatement to allow coercable types --- src/DiagnosticMessages.ts | 4 +- .../validation/ScopeValidator.spec.ts | 66 +++++++++++++------ src/bscPlugin/validation/ScopeValidator.ts | 4 +- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts index a02d2ac50..43d73de14 100644 --- a/src/DiagnosticMessages.ts +++ b/src/DiagnosticMessages.ts @@ -818,8 +818,8 @@ export let DiagnosticMessages = { }, severity: DiagnosticSeverity.Error }), - expectedReturnStatement: () => ({ - message: `Expected return statement in function`, + returnTypeCoercionMismatch: (returnType = 'string') => ({ + message: `Function has no return statement and will return 'invalid': '${returnType}' cannot be coerced into 'invalid'`, code: 1155, severity: DiagnosticSeverity.Error }) diff --git a/src/bscPlugin/validation/ScopeValidator.spec.ts b/src/bscPlugin/validation/ScopeValidator.spec.ts index ad39d04fe..6ed75bde7 100644 --- a/src/bscPlugin/validation/ScopeValidator.spec.ts +++ b/src/bscPlugin/validation/ScopeValidator.spec.ts @@ -2432,7 +2432,7 @@ describe('ScopeValidator', () => { }); }); - describe('expectedReturnStatement', () => { + describe('returnTypeCoercionMismatch', () => { it('allows functions, subs, and "function as void/dynamic" to not have return statements', () => { program.setFile('source/util.bs', ` function noTypeSpecified() @@ -2451,35 +2451,61 @@ describe('ScopeValidator', () => { expectZeroDiagnostics(program); }); - it('detects when a function does not have a return statement', () => { program.setFile('source/util.bs', ` - function doSomething() as integer + function doSomething() as string end function `); program.validate(); expectDiagnostics(program, [ - DiagnosticMessages.expectedReturnStatement().message + DiagnosticMessages.returnTypeCoercionMismatch().message ]); }); + it('allows when a function does not have a return statement, but type coercsion is okay', () => { + program.setFile('source/util.bs', ` + interface Whatever + name as string + end interface + + function doSomething() as Whatever + end function + + function doSomething2() as object + end function + + function doSomething3() as integer + end function + + function doSomething4() as float + end function + + function doSomething5() as boolean + end function + + `); + program.validate(); + // all these are ok + expectZeroDiagnostics(program); + }); + it('detects when a namespaced function does not have a return statement', () => { program.setFile('source/util.bs', ` namespace alpha - function doSomething() as integer + function doSomething() as string end function end namespace `); program.validate(); expectDiagnostics(program, [ - DiagnosticMessages.expectedReturnStatement().message + DiagnosticMessages.returnTypeCoercionMismatch().message ]); }); it('detects when an inline function does not have a return statement', () => { program.setFile('source/util.bs', ` - function outer() as integer - inner = function() as integer + function outer() as string + inner = function () as string print "no return!" end function return inner() @@ -2487,32 +2513,32 @@ describe('ScopeValidator', () => { `); program.validate(); expectDiagnostics(program, [ - DiagnosticMessages.expectedReturnStatement().message + DiagnosticMessages.returnTypeCoercionMismatch().message ]); }); it('detects when an outer function does not have a return statement', () => { program.setFile('source/util.bs', ` - function outer() as integer - inner = function() as integer - return 1 + function outer() as string + inner = function() as string + return "abc" end function print inner() end function `); program.validate(); expectDiagnostics(program, [ - DiagnosticMessages.expectedReturnStatement().message + DiagnosticMessages.returnTypeCoercionMismatch().message ]); }); it('detects when a outer function has a return statement in a branch', () => { program.setFile('source/util.bs', ` - function hasBranch(x) as integer + function hasBranch(x) as string if x = 1 - return 1 + return "1" else - return 2 + return "2" end if end function `); @@ -2522,20 +2548,20 @@ describe('ScopeValidator', () => { it('works for sub with return types with missing return', () => { program.setFile('source/util.bs', ` - sub doSomething() as integer + sub doSomething() as string end sub `); program.validate(); expectDiagnostics(program, [ - DiagnosticMessages.expectedReturnStatement().message + DiagnosticMessages.returnTypeCoercionMismatch().message ]); }); it('works for sub with return types', () => { program.setFile('source/util.bs', ` - sub doSomething() as integer - return 1 + sub doSomething() as string + return "1" end sub `); program.validate(); diff --git a/src/bscPlugin/validation/ScopeValidator.ts b/src/bscPlugin/validation/ScopeValidator.ts index 59940a741..5fb1164c1 100644 --- a/src/bscPlugin/validation/ScopeValidator.ts +++ b/src/bscPlugin/validation/ScopeValidator.ts @@ -896,9 +896,9 @@ export class ScopeValidator { return; } const returns = func.body?.findChild(isReturnStatement, { walkMode: WalkMode.visitAll }); - if (!returns) { + if (!returns && isStringType(returnType)) { this.addMultiScopeDiagnostic({ - ...DiagnosticMessages.expectedReturnStatement(), + ...DiagnosticMessages.returnTypeCoercionMismatch(returnType.toString()), location: func.location }); }