From 4518aee4f2aba1b6a738093a1bac0b70a620dcdd Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 30 Oct 2023 12:17:52 +0100 Subject: [PATCH] feat: scoping for inherited members (#706) Closes #540 ### Summary of Changes Implement reference resolution to inherited class members. --- .../scoping/safe-ds-scope-provider.ts | 30 +- .../safe-ds-lang/src/language/typing/model.ts | 22 +- .../typing/safe-ds-class-hierarchy.ts | 37 +- .../typing/safe-ds-class-hierarchy.test.ts | 73 ++- .../scoping/member accesses/skip-main.sdstest | 587 ------------------ .../to class members/hiding/main.sdstest | 43 ++ .../to class members/inheritance/main.sdstest | 84 +++ .../to class members/overriding/main.sdstest | 25 + 8 files changed, 263 insertions(+), 638 deletions(-) delete mode 100644 packages/safe-ds-lang/tests/resources/scoping/member accesses/skip-main.sdstest create mode 100644 packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/hiding/main.sdstest create mode 100644 packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/inheritance/main.sdstest create mode 100644 packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/overriding/main.sdstest 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/model.ts b/packages/safe-ds-lang/src/language/typing/model.ts index 496365451..1cd2f4148 100644 --- a/packages/safe-ds-lang/src/language/typing/model.ts +++ b/packages/safe-ds-lang/src/language/typing/model.ts @@ -137,17 +137,6 @@ export class NamedTupleType extends Type { return this.entries[index]?.type ?? UnknownType; } - /** - * If this only has one entry, returns its type. Otherwise, returns this. - */ - override unwrap(): Type { - if (this.entries.length === 1) { - return this.entries[0].type; - } - - return this; - } - override equals(other: Type): boolean { if (other === this) { return true; @@ -164,6 +153,17 @@ export class NamedTupleType extends Type { override toString(): string { return `(${this.entries.join(', ')})`; } + + /** + * If this only has one entry, returns its type. Otherwise, returns this. + */ + override unwrap(): Type { + if (this.entries.length === 1) { + return this.entries[0].type; + } + + return this; + } } export class NamedTupleEntry { 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() -// } diff --git a/packages/safe-ds-lang/tests/language/typing/safe-ds-class-hierarchy.test.ts b/packages/safe-ds-lang/tests/language/typing/safe-ds-class-hierarchy.test.ts index 1724cef6d..1f7271901 100644 --- a/packages/safe-ds-lang/tests/language/typing/safe-ds-class-hierarchy.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/safe-ds-class-hierarchy.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { createSafeDsServices } from '../../../src/language/safe-ds-module.js'; import { NodeFileSystem } from 'langium/node'; import { clearDocuments } from 'langium/test'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { isSdsClass, SdsClass } from '../../../src/language/generated/ast.js'; +import { createSafeDsServices } from '../../../src/language/index.js'; import { getNodeOfType } from '../../helpers/nodeFinder.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; @@ -131,4 +131,73 @@ describe('SafeDsClassHierarchy', async () => { expect(superclassNames(firstClass)).toStrictEqual(expected); }); }); + + describe('streamSuperclassMembers', () => { + const superclassMemberNames = (node: SdsClass | undefined) => + classHierarchy + .streamSuperclassMembers(node) + .map((member) => member.name) + .toArray(); + + it('should return an empty stream if passed undefined', () => { + expect(superclassMemberNames(undefined)).toStrictEqual([]); + }); + + const testCases = [ + { + testName: 'should return the members of the parent type', + code: ` + class A { + attr a: Int + fun f() + } + + class B sub A + `, + index: 1, + expected: ['a', 'f'], + }, + { + testName: 'should only consider members of the first parent type', + code: ` + class A { + attr a: Int + fun f() + } + + class B { + attr b: Int + fun g() + } + + class C sub A, B + `, + index: 2, + expected: ['a', 'f'], + }, + { + testName: 'should return members of all superclasses', + code: ` + class A { + attr a: Int + fun f() + } + + class B sub A { + attr b: Int + fun g() + } + + class C sub B + `, + index: 2, + expected: ['b', 'g', 'a', 'f'], + }, + ]; + + it.each(testCases)('$testName', async ({ code, index, expected }) => { + const firstClass = await getNodeOfType(services, code, isSdsClass, index); + expect(superclassMemberNames(firstClass)).toStrictEqual(expected); + }); + }); }); diff --git a/packages/safe-ds-lang/tests/resources/scoping/member accesses/skip-main.sdstest b/packages/safe-ds-lang/tests/resources/scoping/member accesses/skip-main.sdstest deleted file mode 100644 index af6791263..000000000 --- a/packages/safe-ds-lang/tests/resources/scoping/member accesses/skip-main.sdstest +++ /dev/null @@ -1,587 +0,0 @@ -package tests.scoping.references - -import tests.languageTests.scoping.reference2.AnnotationInOtherPackage1 -import tests.languageTests.scoping.reference2.ClassInOtherPackage1 -import tests.languageTests.scoping.reference2.EnumInOtherPackage1 -import tests.languageTests.scoping.reference2.globalFunctionInOtherPackage1 -import tests.languageTests.scoping.reference2.internalStepInOtherPackage -import tests.languageTests.scoping.reference2.privateStepInOtherPackage -import tests.languageTests.scoping.reference2.stepInOtherPackage1 -import tests.languageTests.scoping.reference2.pipelineInOtherPackage1 - -/* -inner class Reference { - - @Nested - inner class MemberAccess { - - @Test - fun `should resolve static class attribute accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classStaticAttributeInSameFile = - findUniqueDeclarationOrFail("classStaticAttributeInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classStaticAttributeInSameFile) - } - - @Test - fun `should resolve instance class attribute accessed from class instance`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classInstanceAttributeInSameFile = - findUniqueDeclarationOrFail("classInstanceAttributeInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInstanceAttributeInSameFile) - } - - @Test - fun `should resolve nested class accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classInClassInSameFile = - findUniqueDeclarationOrFail("ClassInClassInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInClassInSameFile) - } - - @Test - fun `should resolve nested enum accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val enumInClassInSameFile = - findUniqueDeclarationOrFail("EnumInClassInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumInClassInSameFile) - } - - @Test - fun `should resolve static class method accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classStaticMethodInSameFile = - findUniqueDeclarationOrFail("classStaticMethodInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[9].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classStaticMethodInSameFile) - } - - @Test - fun `should resolve instance class method accessed from class instance`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classInstanceMethodInSameFile = - findUniqueDeclarationOrFail("classInstanceMethodInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[11].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInstanceMethodInSameFile) - } - - @Test - fun `should resolve enum variants`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToEnumVariants") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val references = step.body.descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants from step annotation`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToEnumVariants") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val annotations = step.annotationCallsOrEmpty() - annotations.shouldHaveSize(1) - - val references = annotations[0].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants from parameter annotation`() = withResource(REFERENCE) { - val parameter = - findUniqueDeclarationOrFail("referenceToEnumVariantFromParameterAnnotation") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val annotations = parameter.annotationCallsOrEmpty() - annotations.shouldHaveSize(1) - - val references = annotations[0].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants of nested enum from class annotation`() = withResource(REFERENCE) { - val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameClass") - - val annotations = parameter.annotationCallsOrEmpty() - annotations.shouldHaveSize(2) - - val references = annotations[0].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants of global enum from class annotation`() = withResource(REFERENCE) { - val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") - val enumVariantInSameClass = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val annotations = parameter.annotationCallsOrEmpty() - annotations.shouldHaveSize(2) - - val references = annotations[1].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameClass) - } - - @Test - fun `should resolve parameters of enum variants`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToEnumVariantParameters") - val enumVariantParameterInSameFile = - findUniqueDeclarationOrFail("enumVariantParameterInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(3) - - val declaration = references[2].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantParameterInSameFile) - } - - @Test - fun `should resolve inherited static class attribute accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassStaticAttribute = - findUniqueDeclarationOrFail("superClassStaticAttribute") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassStaticAttribute) - } - - @Test - fun `should resolve inherited instance class attribute accessed from class instance`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassInstanceAttribute = - findUniqueDeclarationOrFail("superClassInstanceAttribute") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassInstanceAttribute) - } - - @Test - fun `should resolve inherited nested class accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val classInSuperClass = - findUniqueDeclarationOrFail("ClassInSuperClass") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInSuperClass) - } - - @Test - fun `should resolve inherited nested enum accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val enumInSuperClass = - findUniqueDeclarationOrFail("EnumInSuperClass") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumInSuperClass) - } - - @Test - fun `should resolve inherited static class method accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassStaticMethod = - findUniqueDeclarationOrFail("superClassStaticMethod") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[9].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassStaticMethod) - } - - @Test - fun `should resolve inherited instance class method accessed from class instance`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassInstanceMethod = - findUniqueDeclarationOrFail("superClassInstanceMethod") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[11].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassInstanceMethod) - } - - @Test - fun `should resolve overridden instance attribute`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") - val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") - val instanceAttributeForOverriding = - subClassForOverriding.findUniqueDeclarationOrFail("instanceAttributeForOverriding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(instanceAttributeForOverriding) - } - - @Test - fun `should resolve overridden instance method`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") - val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") - val instanceMethodForOverriding = - subClassForOverriding.findUniqueDeclarationOrFail("instanceMethodForOverriding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(instanceMethodForOverriding) - } - - @Test - fun `should resolve hidden static attribute`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val staticAttributeForHiding = - subClassForHiding.findUniqueDeclarationOrFail("staticAttributeForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(staticAttributeForHiding) - } - - @Test - fun `should resolve hidden nested class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val nestedClassForHiding = - subClassForHiding.findUniqueDeclarationOrFail("NestedClassForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(nestedClassForHiding) - } - - @Test - fun `should resolve hidden nested enum`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val nestedEnumForHiding = - subClassForHiding.findUniqueDeclarationOrFail("NestedEnumForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(nestedEnumForHiding) - } - - @Test - fun `should resolve hidden static method`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val staticMethodForHiding = - subClassForHiding.findUniqueDeclarationOrFail("staticMethodForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(staticMethodForHiding) - } - - @Test - fun `should not resolve static class members accessed from instance`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStaticClassMembersFromInstance") - val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") - - val references = step.descendants() - .filter { it.declaration != classInSameFile } - .toList() - references.shouldHaveSize(8) - references.forEachAsClue { - it.declaration.shouldNotBeResolved() - } - } - - @Test - fun `should not resolve instance class members accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInstanceClassMembersFromClass") - val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") - - val references = step.descendants() - .filter { it.declaration != classInSameFile } - .toList() - references.shouldHaveSize(4) - references.forEachAsClue { - it.declaration.shouldNotBeResolved() - } - } - - @Test - fun `should not resolve class members with unqualified access`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("unqualifiedReferencesToClassMembers") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - references.forEachAsClue { - it.declaration.shouldNotBeResolved() - } - } - - @Test - fun `should not resolve enum variants with unqualified access`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariants") - - val references = step.descendants().toList() - references.shouldHaveSize(1) - references[0].declaration.shouldNotBeResolved() - } - - @Test - fun `should not resolve parameters of enum variants with unqualified access`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariantParameters") - - val references = step.descendants().toList() - references.shouldHaveSize(1) - references[0].declaration.shouldNotBeResolved() - } - } - } -*/ - -annotation AnnotationInSameFile - -class SuperClass { - static attr superClassStaticAttribute: Int - attr superClassInstanceAttribute: Int - - class ClassInSuperClass - - enum EnumInSuperClass - - static fun superClassStaticMethod() - fun superClassInstanceMethod() -} -class ClassInSameFile() sub SuperClass { - static attr classStaticAttributeInSameFile: Int - attr classInstanceAttributeInSameFile: Int - - class ClassInClassInSameFile - - enum EnumInClassInSameFile - - static fun classStaticMethodInSameFile() -> classStaticMethodResultInSameFile: Int - fun classInstanceMethodInSameFile() -> classInstanceMethodResultInSameFile: Int -} - -enum EnumInSameFile { - EnumVariantInSameFile(enumVariantParameterInSameFile: Int) -} - -fun globalFunctionInSameFile(globalFunctionParameterInSameFile: Int) -> globalFunctionResultInSameFile: Int -fun globalFunctionWithOneResultWithIdenticalMember() -> result: ClassForResultMemberAccess -fun globalFunctionWithTwoResults() -> (result1: Int, result2: Int) - - -step stepInSameFile(stepParameterInSameFile: Int) -> stepResultInSameFile: Int { - val placeholderInSameFile = 1; -} -step stepWithOneResultWithIdenticalMember() -> result: ClassForResultMemberAccess {} -step stepWithTwoResults() -> (result1: Int, result2: Int) {} - -step internalStepInSameFile() {} -step privateStepInSameFile() {} - -pipeline pipelineInSameFile {} - -class SuperClassForOverriding() { - attr instanceAttributeForOverriding: Int - fun instanceMethodForOverriding() -} -class SubClassForOverriding() -sub SuperClassForOverriding { - attr instanceAttributeForOverriding: Int - fun instanceMethodForOverriding() -} - -class SuperClassForHiding { - static attr staticAttributeForHiding: Int - class NestedClassForHiding - enum NestedEnumForHiding - static fun staticMethodForHiding() -} -class SubClassForHiding sub SuperClassForHiding { - static attr staticAttributeForHiding: Int - class NestedClassForHiding - enum NestedEnumForHiding - static fun staticMethodForHiding() -} - - -// Access to own members ------------------------------------------------------- - -step referencesToClassMembers() { - ClassInSameFile.classStaticAttributeInSameFile; - ClassInSameFile<*>().classInstanceAttributeInSameFile; - ClassInSameFile.ClassInClassInSameFile; - ClassInSameFile.EnumInClassInSameFile; - ClassInSameFile.classStaticMethodInSameFile; - ClassInSameFile<*>().classInstanceMethodInSameFile(); -} - -@Target(EnumInSameFile.EnumVariantInSameFile) -step referencesToEnumVariants(@Target(EnumInSameFile.EnumVariantInSameFile) referenceToEnumVariantFromParameterAnnotation: Int) { - EnumInSameFile.EnumVariantInSameFile; -} - -class ReferencesToEnumVariants { - @Target(EnumInSameClass.EnumVariantInSameClass) - @Target(EnumInSameFile.EnumVariantInSameFile) - class ReferencesToEnumVariantsInnerClass - - enum EnumInSameClass { - EnumVariantInSameClass - } -} - -step referencesToEnumVariantParameters() { - EnumInSameFile.EnumVariantInSameFile.enumVariantParameterInSameFile; -} - - -// Access to inherited members ------------------------------------------------- - -step referencesToInheritedClassMembers() { - ClassInSameFile.superClassStaticAttribute; - ClassInSameFile<*>().superClassInstanceAttribute; - ClassInSameFile.ClassInSuperClass; - ClassInSameFile.EnumInSuperClass; - ClassInSameFile.superClassStaticMethod; - ClassInSameFile<*>().superClassInstanceMethod(); -} - - -// Overriding ------------------------------------------------------------------ -step referencesToOverriddenMembers() { - SuperClassForOverriding().instanceAttributeForOverriding; - SuperClassForOverriding().instanceMethodForOverriding(); - - SubClassForOverriding().instanceAttributeForOverriding; - SubClassForOverriding().instanceMethodForOverriding(); -} - -// Hiding ---------------------------------------------------------------------- -step referencesToHiddenMembers() { - SubClassForHiding.staticAttributeForHiding; - SubClassForHiding.NestedClassForHiding; - SubClassForHiding.NestedEnumForHiding; - SubClassForHiding.staticMethodForHiding; -} - -// Access to static members from instance -------------------------------------- - -step referencesToStaticClassMembersFromInstance() { - ClassInSameFile<*>().classStaticAttributeInSameFile; - ClassInSameFile<*>().ClassInClassInSameFile; - ClassInSameFile<*>().EnumInClassInSameFile; - ClassInSameFile<*>().classStaticMethodInSameFile; - - ClassInSameFile<*>().superClassStaticAttribute; - ClassInSameFile<*>().ClassInSuperClass; - ClassInSameFile<*>().EnumInSuperClass; - ClassInSameFile<*>().superClassStaticMethod; -} - - -// Access to instance members from class --------------------------------------- - -step referencesToInstanceClassMembersFromClass() { - ClassInSameFile.classInstanceAttributeInSameFile; - ClassInSameFile.classInstanceMethodInSameFile(); - - ClassInSameFile.superClassInstanceAttribute; - ClassInSameFile.superClassInstanceMethod(); -} diff --git a/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/hiding/main.sdstest b/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/hiding/main.sdstest new file mode 100644 index 000000000..8f4afe94a --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/hiding/main.sdstest @@ -0,0 +1,43 @@ +package tests.scoping.memberAccesses.toClassMembers.hiding + +class MySuperClass { + // $TEST$ target MySuperClass_myStaticAttribute + static attr »myStaticAttribute«: Int + + // $TEST$ target MySuperClass_myNestedClass + class »MyNestedClass« + + // $TEST$ target MySuperClass_myNestedEnum + enum »MyNestedEnum« + + // $TEST$ target MySuperClass_myStaticMethod + static fun »myStaticMethod1«() +} + +class MyClass sub MySuperClass { + // $TEST$ target MyClass_myStaticAttribute + static attr »myStaticAttribute«: Int + + // $TEST$ target MyClass_myNestedClass + class »MyNestedClass« + + // $TEST$ target MyClass_myNestedEnum + enum »MyNestedEnum« + + // $TEST$ target MyClass_myStaticMethod + static fun »myStaticMethod1«() +} + +pipeline myPipeline { + // $TEST$ references MyClass_myStaticAttribute + MyClass.»myStaticAttribute«; + + // $TEST$ references MyClass_myNestedClass + MyClass.»MyNestedClass«; + + // $TEST$ references MyClass_myNestedEnum + MyClass.»MyNestedEnum«; + + // $TEST$ references MyClass_myStaticMethod + MyClass.»myStaticMethod1«(); +} diff --git a/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/inheritance/main.sdstest b/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/inheritance/main.sdstest new file mode 100644 index 000000000..ae51019de --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/inheritance/main.sdstest @@ -0,0 +1,84 @@ +package tests.scoping.memberAccesses.toClassMembers.inheritance + +class MySuperClass1 { + // $TEST$ target MySuperClass1_myInstanceAttribute + attr »myInstanceAttribute1«: Int + + // $TEST$ target MySuperClass1_myInstanceMethod + fun »myInstanceMethod1«() + + + // $TEST$ target MySuperClass1_myStaticAttribute + static attr »myStaticAttribute1«: Int + + // $TEST$ target MySuperClass1_myNestedClass + class »MyNestedClass1« + + // $TEST$ target MySuperClass1_myNestedEnum + enum »MyNestedEnum1« + + // $TEST$ target MySuperClass1_myStaticMethod + static fun »myStaticMethod1«() +} + +class MySuperClass2 { + // $TEST$ target MySuperClass2_myInstanceAttribute + attr »myInstanceAttribute2«: Int + + // $TEST$ target MySuperClass2_myInstanceMethod + fun »myInstanceMethod2«() + + + // $TEST$ target MySuperClass2_myStaticAttribute + static attr »myStaticAttribute2«: Int + + // $TEST$ target MySuperClass2_myNestedClass + class »MyNestedClass2« + + // $TEST$ target MySuperClass2_myNestedEnum + enum »MyNestedEnum2« + + // $TEST$ target MySuperClass2_myStaticMethod + static fun »myStaticMethod2«() +} + +class MyClass() sub MySuperClass1, MySuperClass2 + +pipeline myPipeline { + // $TEST$ references MySuperClass1_myInstanceAttribute + MyClass().»myInstanceAttribute1«; + + // $TEST$ references MySuperClass1_myInstanceMethod + MyClass().»myInstanceMethod1«(); + + // $TEST$ references MySuperClass1_myStaticAttribute + MyClass.»myStaticAttribute1«; + + // $TEST$ references MySuperClass1_myNestedClass + MyClass.»MyNestedClass1«; + + // $TEST$ references MySuperClass1_myNestedEnum + MyClass.»MyNestedEnum1«; + + // $TEST$ references MySuperClass1_myStaticMethod + MyClass.»myStaticMethod1«(); + + + // $TEST$ unresolved + MyClass().»myInstanceAttribute2«; + + // $TEST$ unresolved + MyClass().»myInstanceMethod2«(); + + // $TEST$ unresolved + MyClass.»myStaticAttribute2«; + + // $TEST$ unresolved + MyClass.»MyNestedClass2«; + + // $TEST$ unresolved + MyClass.»MyNestedEnum2«; + + // $TEST$ unresolved + MyClass.»myStaticMethod2«(); +} diff --git a/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/overriding/main.sdstest b/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/overriding/main.sdstest new file mode 100644 index 000000000..670737321 --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/scoping/member accesses/to class members/overriding/main.sdstest @@ -0,0 +1,25 @@ +package tests.scoping.memberAccesses.toClassMembers.overriding + +class MySuperClass { + // $TEST$ target MySuperClass_myInstanceAttribute + attr »myInstanceAttribute«: Int + + // $TEST$ target MySuperClass_myInstanceMethod + fun »myInstanceMethod1«() +} + +class MyClass() sub MySuperClass { + // $TEST$ target MyClass_myInstanceAttribute + attr »myInstanceAttribute«: Int + + // $TEST$ target MyClass_myInstanceMethod + fun »myInstanceMethod1«() +} + +pipeline myPipeline { + // $TEST$ references MyClass_myInstanceAttribute + MyClass().»myInstanceAttribute«; + + // $TEST$ references MyClass_myInstanceMethod + MyClass().»myInstanceMethod1«(); +}