From 72847dddb5db99ea87c09b07c6cb0c97eeea4161 Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Fri, 3 Jan 2025 16:59:15 -0400 Subject: [PATCH] Makes assignments of invalid act be typed as dynamic --- src/bscPlugin/validation/BrsFileValidator.ts | 4 +- .../validation/ScopeValidator.spec.ts | 64 +++++++++++++++++++ src/types/VoidType.ts | 7 +- src/types/helpers.ts | 4 +- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/bscPlugin/validation/BrsFileValidator.ts b/src/bscPlugin/validation/BrsFileValidator.ts index e3966d22f..1f050c7dc 100644 --- a/src/bscPlugin/validation/BrsFileValidator.ts +++ b/src/bscPlugin/validation/BrsFileValidator.ts @@ -1,4 +1,4 @@ -import { isAliasStatement, isArrayType, isBlock, isBody, isClassStatement, isConditionalCompileConstStatement, isConditionalCompileErrorStatement, isConditionalCompileStatement, isConstStatement, isDottedGetExpression, isDottedSetStatement, isEnumStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isImportStatement, isIndexedGetExpression, isIndexedSetStatement, isInterfaceStatement, isInvalidType, isLibraryStatement, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTypecastExpression, isTypecastStatement, isUnaryExpression, isVariableExpression, isWhileStatement } from '../../astUtils/reflection'; +import { isAliasStatement, isArrayType, isBlock, isBody, isClassStatement, isConditionalCompileConstStatement, isConditionalCompileErrorStatement, isConditionalCompileStatement, isConstStatement, isDottedGetExpression, isDottedSetStatement, isEnumStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isImportStatement, isIndexedGetExpression, isIndexedSetStatement, isInterfaceStatement, isInvalidType, isLibraryStatement, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTypecastExpression, isTypecastStatement, isUnaryExpression, isVariableExpression, isVoidType, isWhileStatement } from '../../astUtils/reflection'; import { createVisitor, WalkMode } from '../../astUtils/visitors'; import { DiagnosticMessages } from '../../DiagnosticMessages'; import type { BrsFile } from '../../files/BrsFile'; @@ -109,7 +109,7 @@ export class BrsFileValidator { const data: ExtraSymbolData = {}; //register this variable let nodeType = node.getType({ flags: SymbolTypeFlag.runtime, data: data }); - if (isInvalidType(nodeType)) { + if (isInvalidType(nodeType) || isVoidType(nodeType)) { nodeType = DynamicType.instance; } node.parent.getSymbolTable()?.addSymbol(node.tokens.name.text, { definingNode: node, isInstance: true, isFromDocComment: data.isFromDocComment }, nodeType, SymbolTypeFlag.runtime); diff --git a/src/bscPlugin/validation/ScopeValidator.spec.ts b/src/bscPlugin/validation/ScopeValidator.spec.ts index c70b97275..69ad1601c 100644 --- a/src/bscPlugin/validation/ScopeValidator.spec.ts +++ b/src/bscPlugin/validation/ScopeValidator.spec.ts @@ -2189,6 +2189,70 @@ describe('ScopeValidator', () => { expectZeroDiagnostics(program); }); + it('allows access of properties of union with invalid', () => { + program.setFile('source/main.bs', ` + sub test() + channel = invalid + if true + channel = { + height: 123 + } + end if + + height = 0 + if channel <> invalid then + height += channel.height + end if + end sub + `); + program.validate(); + expectZeroDiagnostics(program); + + }); + + it('sets default arg of invalid as dynamic', () => { + program.setFile('source/main.bs', ` + sub test(channel = invalid) + if true + channel = { + height: 123 + } + end if + + height = 0 + if channel <> invalid then + height += channel.height + end if + end sub + `); + program.validate(); + expectZeroDiagnostics(program); + + }); + + it('sets assignment of function returning invalid as dynamic', () => { + program.setFile('source/main.bs', ` + sub test() + channel = noReturn() + if true + channel = { + height: 123 + } + end if + + height = 0 + if channel <> invalid then + height += channel.height + end if + end sub + + sub noReturn() + print "hello" + end sub + `); + program.validate(); + expectZeroDiagnostics(program); + }); }); describe('itemCannotBeUsedAsVariable', () => { diff --git a/src/types/VoidType.ts b/src/types/VoidType.ts index 8d9288c86..c1bcd2e13 100644 --- a/src/types/VoidType.ts +++ b/src/types/VoidType.ts @@ -3,7 +3,8 @@ import { BscType } from './BscType'; import { BscTypeKind } from './BscTypeKind'; import { isUnionTypeCompatible } from './helpers'; import { BuiltInInterfaceAdder } from './BuiltInInterfaceAdder'; -import type { TypeCompatibilityData } from '../interfaces'; +import type { GetTypeOptions, TypeCompatibilityData } from '../interfaces'; +import { DynamicType } from './DynamicType'; export class VoidType extends BscType { constructor( @@ -36,6 +37,10 @@ export class VoidType extends BscType { public isEqual(targetType: BscType) { return isVoidType(targetType); } + + getMemberType(memberName: string, options: GetTypeOptions) { + return DynamicType.instance; + } } BuiltInInterfaceAdder.primitiveTypeInstanceCache.set('void', VoidType.instance); diff --git a/src/types/helpers.ts b/src/types/helpers.ts index e2710969f..531e7dadd 100644 --- a/src/types/helpers.ts +++ b/src/types/helpers.ts @@ -1,5 +1,5 @@ import type { TypeCompatibilityData } from '../interfaces'; -import { isAnyReferenceType, isDynamicType, isEnumMemberType, isEnumType, isInheritableType, isInterfaceType, isReferenceType, isUnionType } from '../astUtils/reflection'; +import { isAnyReferenceType, isDynamicType, isEnumMemberType, isEnumType, isInheritableType, isInterfaceType, isReferenceType, isUnionType, isVoidType } from '../astUtils/reflection'; import type { BscType } from './BscType'; import type { UnionType } from './UnionType'; @@ -126,7 +126,7 @@ export function getUniqueType(types: BscType[], unionTypeFactory: (types: BscTyp if (!types || types.length === 0) { return undefined; } - const dynType = types.find((x) => !isAnyReferenceType(x) && isDynamicType(x)); + const dynType = types.find((x) => !isAnyReferenceType(x) && (isDynamicType(x) || isVoidType(x))); if (dynType) { return dynType; }