Skip to content

Commit 1ed839a

Browse files
authored
Merge inferred array types (#1506)
1 parent ea58201 commit 1ed839a

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

packages/langium/src/grammar/type-system/type-collector/plain-types.ts

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -210,22 +210,36 @@ function plainToPropertyType(type: PlainPropertyType, union: UnionType | undefin
210210
}
211211

212212
export function mergePropertyTypes(first: PlainPropertyType, second: PlainPropertyType): PlainPropertyType {
213-
const flattenedFirst = flattenPlainType(first);
214-
const flattenedSecond = flattenPlainType(second);
215-
for (const second of flattenedSecond) {
216-
if (!includesType(flattenedFirst, second)) {
217-
flattenedFirst.push(second);
218-
}
213+
const { union: flattenedFirstUnion, array: flattenedFirstArray } = flattenPlainType(first);
214+
const { union: flattenedSecondUnion, array: flattenedSecondArray } = flattenPlainType(second);
215+
const flattenedUnion = mergeTypeUnion(flattenedFirstUnion, flattenedSecondUnion);
216+
const flattenedArray = mergeTypeUnion(flattenedFirstArray, flattenedSecondArray);
217+
if (flattenedArray.length > 0) {
218+
flattenedUnion.push({
219+
elementType: flattenedArray.length === 1 ? flattenedArray[0] : {
220+
types: flattenedArray
221+
}
222+
});
219223
}
220-
if (flattenedFirst.length === 1) {
221-
return flattenedFirst[0];
224+
if (flattenedUnion.length === 1) {
225+
return flattenedUnion[0];
222226
} else {
223227
return {
224-
types: flattenedFirst
228+
types: flattenedUnion
225229
};
226230
}
227231
}
228232

233+
function mergeTypeUnion(first: PlainPropertyType[], second: PlainPropertyType[]): PlainPropertyType[] {
234+
const result = [...first];
235+
for (const type of second) {
236+
if (!includesType(result, type)) {
237+
result.push(type);
238+
}
239+
}
240+
return result;
241+
}
242+
229243
function includesType(list: PlainPropertyType[], value: PlainPropertyType): boolean {
230244
return list.some(e => typeEquals(e, value));
231245
}
@@ -244,10 +258,22 @@ function typeEquals(first: PlainPropertyType, second: PlainPropertyType): boolea
244258
}
245259
}
246260

247-
export function flattenPlainType(type: PlainPropertyType): PlainPropertyType[] {
261+
export function flattenPlainType(type: PlainPropertyType): { union: PlainPropertyType[], array: PlainPropertyType[] } {
248262
if (isPlainPropertyUnion(type)) {
249-
return type.types.flatMap(e => flattenPlainType(e));
263+
const flattened = type.types.flatMap(e => flattenPlainType(e));
264+
return {
265+
union: flattened.map(e => e.union).flat(),
266+
array: flattened.map(e => e.array).flat()
267+
};
268+
} else if (isPlainArrayType(type)) {
269+
return {
270+
array: flattenPlainType(type.elementType).union,
271+
union: []
272+
};
250273
} else {
251-
return [type];
274+
return {
275+
array: [],
276+
union: [type]
277+
};
252278
}
253279
}

packages/langium/src/grammar/validation/validator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ export class LangiumGrammarValidator {
866866
const flattened = flattenPlainType(plainType);
867867
let hasRef = false;
868868
let hasNonRef = false;
869-
for (const flat of flattened) {
869+
for (const flat of flattened.union.concat(flattened.array)) {
870870
if (isPlainReferenceType(flat)) {
871871
hasRef = true;
872872
} else if (!isPlainReferenceType(flat)) {

packages/langium/test/grammar/type-system/inferred-types.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,30 @@ describe('types of `$container` and `$type` are correct', () => {
798798
}
799799
`);
800800
});
801+
802+
test('Should merge types of array properties', async () => {
803+
const expected = expandToString`
804+
export interface A extends AstNode {
805+
readonly $type: 'A';
806+
values: Array<number | string>;
807+
}
808+
`;
809+
await expectTypes(`
810+
A: values+=ID values+=NUMBER;
811+
terminal ID returns string: /string/;
812+
terminal NUMBER returns number: /number/;
813+
`, expected);
814+
await expectTypes(`
815+
A: (values+=ID | values+=NUMBER)+;
816+
terminal ID returns string: /string/;
817+
terminal NUMBER returns number: /number/;
818+
`, expected);
819+
await expectTypes(`
820+
A: values+=(ID | NUMBER)+;
821+
terminal ID returns string: /string/;
822+
terminal NUMBER returns number: /number/;
823+
`, expected);
824+
});
801825
});
802826

803827
// https://github.com/eclipse-langium/langium/issues/744

0 commit comments

Comments
 (0)