Skip to content

Commit

Permalink
test: move tests for lowestCommonSupertype to data-driven type comp…
Browse files Browse the repository at this point in the history
…uter tests

This makes them considerably more readable and easier to extend. We can also make `lowestCommonSupertype` private now without ignoring TypeScript errors.
  • Loading branch information
lars-reimann committed Feb 9, 2024
1 parent 814d1e0 commit 92f6747
Show file tree
Hide file tree
Showing 14 changed files with 384 additions and 463 deletions.
128 changes: 64 additions & 64 deletions packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,11 +737,74 @@ export class SafeDsTypeComputer {
} /* c8 ignore stop */
}

// -----------------------------------------------------------------------------------------------------------------
// Supertypes
// -----------------------------------------------------------------------------------------------------------------

/**
* Returns the supertype of the given `type` that is declared by the given `clazz`. If no such supertype exists,
* `undefined` is returned. Type parameters on parent types get substituted.
*/
computeSupertypeOfClass(type: ClassType | undefined, clazz: SdsClass | undefined): ClassType | undefined {
if (!type || !clazz) {
return undefined;
}

return stream([type], this.streamSupertypes(type)).find((it) => it.declaration === clazz);
}

/**
* Returns a stream of all declared supertypes of the given type. Direct ancestors are returned first, followed by
* their ancestors and so on. The given type is never included in the stream.
*
* Compared to `ClassHierarchy.streamSuperTypes`, this method cannot be used to detect cycles in the inheritance
* hierarchy. However, it can handle type parameters on parent types and substitute them accordingly.
*/
streamSupertypes(type: ClassType | undefined): Stream<ClassType> {
if (!type) {
return EMPTY_STREAM;
}

return stream(this.supertypesGenerator(type));
}

private *supertypesGenerator(type: ClassType): Generator<ClassType, void> {
// Compared to `ClassHierarchy.superclassesGenerator`, we already include the given type in the list of visited
// elements, since this method here is not used to detect cycles.
const visited = new Set<SdsClass>([type.declaration]);
let current = this.parentClassType(type);
while (current && !visited.has(current.declaration)) {
yield current;
visited.add(current.declaration);
current = this.parentClassType(current);
}

const Any = this.coreTypes.Any;
if (Any instanceof ClassType && !visited.has(Any.declaration)) {
yield Any;
}
}

/**
* Tries to evaluate the first parent type of the class to a `ClassType` and returns it if successful. Type
* parameters get substituted. If there is no parent type or the parent type is not a class, `undefined` is
* returned.
*/
private parentClassType(type: ClassType | undefined): ClassType | undefined {
const [firstParentTypeNode] = getParentTypes(type?.declaration);
const firstParentType = this.computeType(firstParentTypeNode, type?.substitutions);

if (firstParentType instanceof ClassType) {
return firstParentType;
}
return undefined;
}

// -----------------------------------------------------------------------------------------------------------------
// Lowest common supertype
// -----------------------------------------------------------------------------------------------------------------

lowestCommonSupertype(...types: Type[]): Type {
private lowestCommonSupertype(...types: Type[]): Type {
// Simplify types
const simplifiedTypes = this.simplifyTypes(types);

Expand Down Expand Up @@ -902,69 +965,6 @@ export class SafeDsTypeComputer {
private Any(isNullable: boolean): Type {
return isNullable ? this.coreTypes.AnyOrNull : this.coreTypes.Any;
}

// -----------------------------------------------------------------------------------------------------------------
// Supertypes
// -----------------------------------------------------------------------------------------------------------------

/**
* Returns the supertype of the given `type` that is declared by the given `clazz`. If no such supertype exists,
* `undefined` is returned. Type parameters on parent types get substituted.
*/
computeSupertypeOfClass(type: ClassType | undefined, clazz: SdsClass | undefined): ClassType | undefined {
if (!type || !clazz) {
return undefined;
}

return stream([type], this.streamSupertypes(type)).find((it) => it.declaration === clazz);
}

/**
* Returns a stream of all declared supertypes of the given type. Direct ancestors are returned first, followed by
* their ancestors and so on. The given type is never included in the stream.
*
* Compared to `ClassHierarchy.streamSuperTypes`, this method cannot be used to detect cycles in the inheritance
* hierarchy. However, it can handle type parameters on parent types and substitute them accordingly.
*/
streamSupertypes(type: ClassType | undefined): Stream<ClassType> {
if (!type) {
return EMPTY_STREAM;
}

return stream(this.supertypesGenerator(type));
}

private *supertypesGenerator(type: ClassType): Generator<ClassType, void> {
// Compared to `ClassHierarchy.superclassesGenerator`, we already include the given type in the list of visited
// elements, since this method here is not used to detect cycles.
const visited = new Set<SdsClass>([type.declaration]);
let current = this.parentClassType(type);
while (current && !visited.has(current.declaration)) {
yield current;
visited.add(current.declaration);
current = this.parentClassType(current);
}

const Any = this.coreTypes.Any;
if (Any instanceof ClassType && !visited.has(Any.declaration)) {
yield Any;
}
}

/**
* Tries to evaluate the first parent type of the class to a `ClassType` and returns it if successful. Type
* parameters get substituted. If there is no parent type or the parent type is not a class, `undefined` is
* returned.
*/
private parentClassType(type: ClassType | undefined): ClassType | undefined {
const [firstParentTypeNode] = getParentTypes(type?.declaration);
const firstParentType = this.computeType(firstParentTypeNode, type?.substitutions);

if (firstParentType instanceof ClassType) {
return firstParentType;
}
return undefined;
}
}

interface GroupTypesResult {
Expand Down
Loading

0 comments on commit 92f6747

Please sign in to comment.