From ca08f90000879f8a83730b80f7bbd3231711add0 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 30 Oct 2023 12:04:51 +0100 Subject: [PATCH] feat: scoping for inherited members --- .../scoping/safe-ds-scope-provider.ts | 30 ++++++++------- .../typing/safe-ds-class-hierarchy.ts | 37 +++++++------------ 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts index 3fed19dfd..3d10c608f 100644 --- a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts +++ b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts @@ -63,14 +63,16 @@ import { getTypeParameters, isStatic, } from '../helpers/nodeProperties.js'; +import { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js'; import { SafeDsServices } from '../safe-ds-module.js'; +import { ClassType, EnumVariantType } from '../typing/model.js'; +import type { SafeDsClassHierarchy } from '../typing/safe-ds-class-hierarchy.js'; import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js'; import { SafeDsPackageManager } from '../workspace/safe-ds-package-manager.js'; -import { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js'; -import { ClassType, EnumVariantType } from '../typing/model.js'; export class SafeDsScopeProvider extends DefaultScopeProvider { private readonly astReflection: AstReflection; + private readonly classHierarchy: SafeDsClassHierarchy; private readonly nodeMapper: SafeDsNodeMapper; private readonly packageManager: SafeDsPackageManager; private readonly typeComputer: SafeDsTypeComputer; @@ -79,6 +81,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { super(services); this.astReflection = services.shared.AstReflection; + this.classHierarchy = services.types.ClassHierarchy; this.nodeMapper = services.helpers.NodeMapper; this.packageManager = services.workspace.PackageManager; this.typeComputer = services.types.TypeComputer; @@ -178,12 +181,9 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { const declaration = this.getUniqueReferencedDeclarationForExpression(node.receiver); if (isSdsClass(declaration)) { const ownStaticMembers = getMatchingClassMembers(declaration, isStatic); - // val superTypeMembers = receiverDeclaration.superClassMembers() - // .filter { it.isStatic() } - // .toList() - // - // return Scopes.scopeFor(ownStaticMembers, Scopes.scopeFor(superTypeMembers)) - return this.createScopeForNodes(ownStaticMembers); + const superclassStaticMembers = this.classHierarchy.streamSuperclassMembers(declaration).filter(isStatic); + + return this.createScopeForNodes(ownStaticMembers, this.createScopeForNodes(superclassStaticMembers)); } else if (isSdsEnum(declaration)) { return this.createScopeForNodes(getEnumVariants(declaration)); } @@ -208,12 +208,14 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { if (receiverType instanceof ClassType) { const ownInstanceMembers = getMatchingClassMembers(receiverType.declaration, (it) => !isStatic(it)); - // val superTypeMembers = type.sdsClass.superClassMembers() - // .filter { !it.isStatic() } - // .toList() - // - // Scopes.scopeFor(members, Scopes.scopeFor(superTypeMembers, resultScope)) - return this.createScopeForNodes(ownInstanceMembers, resultScope); + const superclassInstanceMembers = this.classHierarchy + .streamSuperclassMembers(receiverType.declaration) + .filter((it) => !isStatic(it)); + + return this.createScopeForNodes( + ownInstanceMembers, + this.createScopeForNodes(superclassInstanceMembers, resultScope), + ); } else if (receiverType instanceof EnumVariantType) { const parameters = getParameters(receiverType.declaration); return this.createScopeForNodes(parameters, resultScope); diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts index 1bc6c7a87..9d9beebc1 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts @@ -1,10 +1,10 @@ -import { SafeDsServices } from '../safe-ds-module.js'; -import { SafeDsClasses } from '../builtins/safe-ds-classes.js'; -import { SdsClass } from '../generated/ast.js'; import { EMPTY_STREAM, stream, Stream } from 'langium'; -import { getParentTypes } from '../helpers/nodeProperties.js'; -import { SafeDsTypeComputer } from './safe-ds-type-computer.js'; +import { SafeDsClasses } from '../builtins/safe-ds-classes.js'; +import { SdsClass, type SdsClassMember } from '../generated/ast.js'; +import { getMatchingClassMembers, getParentTypes } from '../helpers/nodeProperties.js'; +import { SafeDsServices } from '../safe-ds-module.js'; import { ClassType } from './model.js'; +import { SafeDsTypeComputer } from './safe-ds-type-computer.js'; export class SafeDsClassHierarchy { private readonly builtinClasses: SafeDsClasses; @@ -60,6 +60,14 @@ export class SafeDsClassHierarchy { } } + streamSuperclassMembers(node: SdsClass | undefined): Stream { + if (!node) { + return EMPTY_STREAM; + } + + return this.streamSuperclasses(node).flatMap(getMatchingClassMembers); + } + /** * Returns the parent class of the given class, or undefined if there is no parent class. Only the first parent * type is considered, i.e. multiple inheritance is not supported. @@ -74,22 +82,3 @@ export class SafeDsClassHierarchy { return undefined; } } - -// fun SdsClass.superClassMembers() = -// this.superClasses().flatMap { it.classMembersOrEmpty().asSequence() } -// -// // TODO only static methods can be hidden -// fun SdsFunction.hiddenFunction(): SdsFunction? { -// val containingClassOrInterface = closestAncestorOrNull() ?: return null -// return containingClassOrInterface.superClassMembers() -// .filterIsInstance() -// .firstOrNull { it.name == name } -// } -// -// fun SdsClass?.inheritedNonStaticMembersOrEmpty(): Set { -// return this?.parentClassesOrEmpty() -// ?.flatMap { it.classMembersOrEmpty() } -// ?.filter { it is SdsAttribute && !it.isStatic || it is SdsFunction && !it.isStatic } -// ?.toSet() -// .orEmpty() -// }