Skip to content

Commit 1556715

Browse files
authored
Fixes issue with forEach iterating over a union of iterables (#1629)
1 parent 9e8e06b commit 1556715

File tree

5 files changed

+80
-1
lines changed

5 files changed

+80
-1
lines changed

src/Scope.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3807,6 +3807,25 @@ describe('Scope', () => {
38073807
expectTypeToBe(symbolTable.getSymbolType('key', opts), StringType);
38083808
});
38093809

3810+
it('should set the type of the for loop item whn the target is a union of typed arrays', () => {
3811+
let mainFile = program.setFile<BrsFile>('source/main.bs', `
3812+
sub process(data as integer[] or string[])
3813+
for each item in data
3814+
print item
3815+
end for
3816+
end sub
3817+
`);
3818+
program.validate();
3819+
expectZeroDiagnostics(program);
3820+
const forEachStmt = mainFile.ast.findChild<ForEachStatement>(isForEachStatement);
3821+
const symbolTable = forEachStmt.body.getSymbolTable();
3822+
const opts = { flags: SymbolTypeFlag.runtime };
3823+
const itemType = symbolTable.getSymbolType('item', opts) as UnionType;
3824+
expectTypeToBe(itemType, UnionType);
3825+
expect(itemType.types).to.include(IntegerType.instance);
3826+
expect(itemType.types).to.include(StringType.instance);
3827+
});
3828+
38103829
it('should use dynamic type for loop item when type cannot be inferred', () => {
38113830
let mainFile = program.setFile<BrsFile>('source/main.bs', `
38123831
sub process(data as roList)

src/astUtils/reflection.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,9 @@ export function isIterableType(value: any): boolean {
583583
if (isArrayTypeLike(value) || isAssociativeArrayTypeLike(value)) {
584584
return true;
585585
}
586+
if (isCompoundTypeOf(value, isIterableType)) {
587+
return true;
588+
}
586589
if (isBuiltInType(value, 'roByteArray') || isBuiltInType(value, 'roList') || isBuiltInType(value, 'roXMLList') || isBuiltInType(value, 'roMessagePort')) {
587590
return true;
588591
}

src/bscPlugin/validation/BrsFileValidator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export class BrsFileValidator {
134134
this.validateNoOptionalChainingInVarSet(node, [node.obj]);
135135
},
136136
ForEachStatement: (node) => {
137-
//register the for loop variable
137+
//registering the for loop variable happens in the visitor for Block, since the loop variable is scoped to the loop body
138138
},
139139
NamespaceStatement: (node) => {
140140
if (!node?.nameExpression) {

src/bscPlugin/validation/ScopeValidator.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6651,5 +6651,59 @@ describe('ScopeValidator', () => {
66516651
DiagnosticMessages.notIterable('integer').message
66526652
]);
66536653
});
6654+
6655+
it('allows a union of iterable types to be iterated over', () => {
6656+
program.setFile('source/test.bs', `
6657+
sub doStuff(items as roList or roArray)
6658+
for each item in items
6659+
print item
6660+
end for
6661+
end sub
6662+
`);
6663+
program.validate();
6664+
expectZeroDiagnostics(program);
6665+
});
6666+
6667+
it('allows a union of typed arrays to be iterated over', () => {
6668+
program.setFile('source/test.bs', `
6669+
sub setAllText(text as string, labels as roSGNodeLabel[] or roSGNodeMultiStyleLabel[])
6670+
for each label in labels
6671+
label.text = text
6672+
end for
6673+
end sub
6674+
`);
6675+
program.validate();
6676+
expectZeroDiagnostics(program);
6677+
});
6678+
6679+
it('allows a type statement of a typed array to be iterated over', () => {
6680+
program.setFile('source/test.bs', `
6681+
6682+
type StringArray = string[]
6683+
6684+
sub printAllText(text as StringArray)
6685+
for each t in text
6686+
print t
6687+
end for
6688+
end sub
6689+
`);
6690+
program.validate();
6691+
expectZeroDiagnostics(program);
6692+
});
6693+
6694+
it('allows a type statement of a typed array to be iterated over', () => {
6695+
program.setFile('source/test.bs', `
6696+
6697+
type StringArray = string[]
6698+
6699+
sub printAllText(text as StringArray)
6700+
for each t in text
6701+
print t
6702+
end for
6703+
end sub
6704+
`);
6705+
program.validate();
6706+
expectZeroDiagnostics(program);
6707+
});
66546708
});
66556709
});

src/util.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2759,6 +2759,9 @@ export class Util {
27592759
return StringType.instance;
27602760
} else if (isBuiltInType(iteratorType, 'roByteArray')) {
27612761
return IntegerType.instance;
2762+
} else if (isUnionType(iteratorType)) {
2763+
const iteratorDefaultTypes = iteratorType.types.map(t => this.getIteratorDefaultType(t));
2764+
return getUniqueType(iteratorDefaultTypes, unionTypeFactory);
27622765
}
27632766
return DynamicType.instance;
27642767
}

0 commit comments

Comments
 (0)