Skip to content

Commit 7629a86

Browse files
authored
Assign invalid types as dynamic (#1389)
* Makes assignments of invalid act be typed as dynamic * Makes assignments of invalid act be typed as dynamic
1 parent f38c476 commit 7629a86

File tree

5 files changed

+129
-5
lines changed

5 files changed

+129
-5
lines changed

src/bscPlugin/validation/BrsFileValidator.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,58 @@ describe('BrsFileValidator', () => {
770770
expectZeroDiagnostics(program);
771771
});
772772
});
773+
774+
describe('types', () => {
775+
it('sets assignments of invalid as dynamic', () => {
776+
const file = program.setFile<BrsFile>('source/main.bs', `
777+
sub test()
778+
channel = invalid
779+
if true
780+
channel = {
781+
height: 123
782+
}
783+
end if
784+
785+
height = 0
786+
if channel <> invalid then
787+
height += channel.height
788+
end if
789+
end sub
790+
`);
791+
program.validate();
792+
expectZeroDiagnostics(program);
793+
const func = file.ast.statements[0].findChild<FunctionExpression>(isFunctionExpression, { walkMode: WalkMode.visitAllRecursive });
794+
const table = func.body.getSymbolTable();
795+
const data = {} as ExtraSymbolData;
796+
const channelType = table.getSymbolType('channel', { flags: SymbolTypeFlag.runtime, data: data });
797+
expectTypeToBe(channelType, DynamicType);
798+
});
799+
800+
it('sets default arg of invalid as dynamic', () => {
801+
const file = program.setFile<BrsFile>('source/main.bs', `
802+
sub test(channel = invalid)
803+
if true
804+
channel = {
805+
height: 123
806+
}
807+
end if
808+
809+
height = 0
810+
if channel <> invalid then
811+
height += channel.height
812+
end if
813+
end sub
814+
`);
815+
program.validate();
816+
expectZeroDiagnostics(program);
817+
const func = file.ast.statements[0].findChild<FunctionExpression>(isFunctionExpression, { walkMode: WalkMode.visitAllRecursive });
818+
const table = func.body.getSymbolTable();
819+
const data = {} as ExtraSymbolData;
820+
const channelType = table.getSymbolType('channel', { flags: SymbolTypeFlag.runtime, data: data });
821+
expectTypeToBe(channelType, DynamicType);
822+
});
823+
});
824+
773825
describe('instances of types', () => {
774826
it('sets assigned variables as instances', () => {
775827
const file = program.setFile<BrsFile>('source/main.bs', `

src/bscPlugin/validation/BrsFileValidator.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isAliasStatement, isArrayType, isBlock, isBody, isClassStatement, isConditionalCompileConstStatement, isConditionalCompileErrorStatement, isConditionalCompileStatement, isConstStatement, isDottedGetExpression, isDottedSetStatement, isEnumStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isImportStatement, isIndexedGetExpression, isIndexedSetStatement, isInterfaceStatement, isLibraryStatement, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTypecastExpression, isTypecastStatement, isUnaryExpression, isVariableExpression, isWhileStatement } from '../../astUtils/reflection';
1+
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';
22
import { createVisitor, WalkMode } from '../../astUtils/visitors';
33
import { DiagnosticMessages } from '../../DiagnosticMessages';
44
import type { BrsFile } from '../../files/BrsFile';
@@ -108,7 +108,10 @@ export class BrsFileValidator {
108108
AssignmentStatement: (node) => {
109109
const data: ExtraSymbolData = {};
110110
//register this variable
111-
const nodeType = node.getType({ flags: SymbolTypeFlag.runtime, data: data });
111+
let nodeType = node.getType({ flags: SymbolTypeFlag.runtime, data: data });
112+
if (isInvalidType(nodeType) || isVoidType(nodeType)) {
113+
nodeType = DynamicType.instance;
114+
}
112115
node.parent.getSymbolTable()?.addSymbol(node.tokens.name.text, { definingNode: node, isInstance: true, isFromDocComment: data.isFromDocComment }, nodeType, SymbolTypeFlag.runtime);
113116
},
114117
DottedSetStatement: (node) => {

src/bscPlugin/validation/ScopeValidator.spec.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,70 @@ describe('ScopeValidator', () => {
21892189
expectZeroDiagnostics(program);
21902190
});
21912191

2192+
it('allows access of properties of union with invalid', () => {
2193+
program.setFile<BrsFile>('source/main.bs', `
2194+
sub test()
2195+
channel = invalid
2196+
if true
2197+
channel = {
2198+
height: 123
2199+
}
2200+
end if
2201+
2202+
height = 0
2203+
if channel <> invalid then
2204+
height += channel.height
2205+
end if
2206+
end sub
2207+
`);
2208+
program.validate();
2209+
expectZeroDiagnostics(program);
2210+
2211+
});
2212+
2213+
it('sets default arg of invalid as dynamic', () => {
2214+
program.setFile<BrsFile>('source/main.bs', `
2215+
sub test(channel = invalid)
2216+
if true
2217+
channel = {
2218+
height: 123
2219+
}
2220+
end if
2221+
2222+
height = 0
2223+
if channel <> invalid then
2224+
height += channel.height
2225+
end if
2226+
end sub
2227+
`);
2228+
program.validate();
2229+
expectZeroDiagnostics(program);
2230+
2231+
});
2232+
2233+
it('sets assignment of function returning invalid as dynamic', () => {
2234+
program.setFile<BrsFile>('source/main.bs', `
2235+
sub test()
2236+
channel = noReturn()
2237+
if true
2238+
channel = {
2239+
height: 123
2240+
}
2241+
end if
2242+
2243+
height = 0
2244+
if channel <> invalid then
2245+
height += channel.height
2246+
end if
2247+
end sub
2248+
2249+
sub noReturn()
2250+
print "hello"
2251+
end sub
2252+
`);
2253+
program.validate();
2254+
expectZeroDiagnostics(program);
2255+
});
21922256
});
21932257

21942258
describe('itemCannotBeUsedAsVariable', () => {

src/types/VoidType.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { BscType } from './BscType';
33
import { BscTypeKind } from './BscTypeKind';
44
import { isUnionTypeCompatible } from './helpers';
55
import { BuiltInInterfaceAdder } from './BuiltInInterfaceAdder';
6-
import type { TypeCompatibilityData } from '../interfaces';
6+
import type { GetTypeOptions, TypeCompatibilityData } from '../interfaces';
7+
import { DynamicType } from './DynamicType';
78

89
export class VoidType extends BscType {
910
constructor(
@@ -36,6 +37,10 @@ export class VoidType extends BscType {
3637
public isEqual(targetType: BscType) {
3738
return isVoidType(targetType);
3839
}
40+
41+
getMemberType(memberName: string, options: GetTypeOptions) {
42+
return DynamicType.instance;
43+
}
3944
}
4045

4146
BuiltInInterfaceAdder.primitiveTypeInstanceCache.set('void', VoidType.instance);

src/types/helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { TypeCompatibilityData } from '../interfaces';
2-
import { isAnyReferenceType, isDynamicType, isEnumMemberType, isEnumType, isInheritableType, isInterfaceType, isReferenceType, isUnionType } from '../astUtils/reflection';
2+
import { isAnyReferenceType, isDynamicType, isEnumMemberType, isEnumType, isInheritableType, isInterfaceType, isReferenceType, isUnionType, isVoidType } from '../astUtils/reflection';
33
import type { BscType } from './BscType';
44
import type { UnionType } from './UnionType';
55

@@ -126,7 +126,7 @@ export function getUniqueType(types: BscType[], unionTypeFactory: (types: BscTyp
126126
if (!types || types.length === 0) {
127127
return undefined;
128128
}
129-
const dynType = types.find((x) => !isAnyReferenceType(x) && isDynamicType(x));
129+
const dynType = types.find((x) => !isAnyReferenceType(x) && (isDynamicType(x) || isVoidType(x)));
130130
if (dynType) {
131131
return dynType;
132132
}

0 commit comments

Comments
 (0)