From 3762c36c310dd7a7cf7176c19f346ace686f6968 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 30 Oct 2023 14:19:18 +0100 Subject: [PATCH] feat: scoping for references to containing declarations (#708) Closes #540 ### Summary of Changes Implement the same scoping rules for references to containing declarations as for named types. --- .../scoping/safe-ds-scope-provider.ts | 39 +++--- .../to containing declarations/main.sdstest | 115 ++++++++++++++++++ 2 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 packages/safe-ds-lang/tests/resources/scoping/references/in same file/to containing declarations/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 0e6021c85..7fa1a54d5 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 @@ -11,6 +11,7 @@ import { } from 'langium'; import { isSdsAbstractCall, + isSdsAnnotationCall, isSdsArgument, isSdsAssignment, isSdsBlock, @@ -54,6 +55,7 @@ import { import { isContainedIn } from '../helpers/astUtils.js'; import { getAbstractResults, + getAnnotationCallTarget, getAssignees, getEnumVariants, getImportedDeclarations, @@ -251,25 +253,34 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { // Declarations in this file currentScope = this.globalDeclarationsInSameFile(node, currentScope); - // // Declarations in containing classes - // context.containingClassOrNull()?.let { - // result = classMembers(it, result) - // } - // + // Declarations in containing declarations + currentScope = this.containingDeclarations(node, currentScope); // Declarations in containing blocks return this.localDeclarations(node, currentScope); } - // private fun classMembers(context: SdsClass, parentScope: IScope): IScope { - // return when (val containingClassOrNull = context.containingClassOrNull()) { - // is SdsClass -> Scopes.scopeFor( - // context.classMembersOrEmpty(), - // classMembers(containingClassOrNull, parentScope), - // ) - // else -> Scopes.scopeFor(context.classMembersOrEmpty(), parentScope) - // } - // } + private containingDeclarations(node: AstNode, outerScope: Scope): Scope { + const result = []; + + // Cannot reference the target of an annotation call from inside the annotation call + let start: AstNode | undefined; + const containingAnnotationCall = getContainerOfType(node, isSdsAnnotationCall); + if (containingAnnotationCall) { + start = getAnnotationCallTarget(containingAnnotationCall)?.$container; + } else { + start = node.$container; + } + + // Only containing classes, enums, and enum variants can be referenced + let current = getContainerOfType(start, isSdsNamedTypeDeclaration); + while (current) { + result.push(current); + current = getContainerOfType(current.$container, isSdsNamedTypeDeclaration); + } + + return this.createScopeForNodes(result, outerScope); + } private globalDeclarationsInSameFile(node: AstNode, outerScope: Scope): Scope { const module = getContainerOfType(node, isSdsModule); diff --git a/packages/safe-ds-lang/tests/resources/scoping/references/in same file/to containing declarations/main.sdstest b/packages/safe-ds-lang/tests/resources/scoping/references/in same file/to containing declarations/main.sdstest new file mode 100644 index 000000000..daee4dd20 --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/scoping/references/in same file/to containing declarations/main.sdstest @@ -0,0 +1,115 @@ +package tests.scoping.references.inSameFile.toContainingDeclarations + +@Repeatable +annotation MyAnnotation(p: Any?) + +// $TEST$ target outerClass +class »MyClass«( + @MyAnnotation( + // $TEST$ references outerClass + p = »MyClass«() + ) + // $TEST$ references outerClass + p: Any? = »MyClass«() +) { + + @MyAnnotation( + // $TEST$ references outerClass + p = »MyClass«() + ) + @MyAnnotation( + // $TEST$ unresolved + p = »MyEnum« + ) + fun f( + @MyAnnotation( + // $TEST$ references outerClass + p = »MyClass«() + ) + // $TEST$ references outerClass + p1: Any? = »MyClass«(), + + @MyAnnotation( + // $TEST$ unresolved + p = »MyEnum« + ) + // $TEST$ unresolved + p2: Any? = »MyEnum« + ) + + @MyAnnotation( + // $TEST$ references outerClass + p = »MyClass«() + ) + @MyAnnotation( + // $TEST$ unresolved + p = »MyEnum« + ) + // $TEST$ target enum + enum »MyEnum« { + // $TEST$ target variant + »MyEnumVariant«( + @MyAnnotation( + // $TEST$ references outerClass + p = »MyClass«() + ) + // $TEST$ references outerClass + p1: Any? = »MyClass«(), + + @MyAnnotation( + // $TEST$ references enum + p = »MyEnum« + ) + // $TEST$ references enum + p2: Any? = »MyEnum«, + + @MyAnnotation( + // $TEST$ references variant + p = »MyEnumVariant« + ) + // $TEST$ references variant + p3: Any? = »MyEnumVariant«, + ) + } + + @MyAnnotation( + // $TEST$ references outerClass + p = »MyClass«() + ) + @MyAnnotation( + // $TEST$ unresolved + p = »MyEnum« + ) + // $TEST$ target innerClass + class »MyClass«( + @MyAnnotation( + // $TEST$ references innerClass + p = »MyClass«() + ) + // $TEST$ references innerClass + p1: Any? = »MyClass«(), + + @MyAnnotation( + // $TEST$ unresolved + p = »MyEnum« + ) + // $TEST$ unresolved + p2: Any? = »MyEnum« + ) { + fun f( + @MyAnnotation( + // $TEST$ references innerClass + p = »MyClass«() + ) + // $TEST$ references innerClass + p1: Any? = »MyClass«(), + + @MyAnnotation( + // $TEST$ unresolved + p = »MyEnum« + ) + // $TEST$ unresolved + p2: Any? = »MyEnum« + ) + } +}