Skip to content

Commit

Permalink
feat: scoping for references to containing declarations (#708)
Browse files Browse the repository at this point in the history
Closes #540

### Summary of Changes

Implement the same scoping rules for references to containing
declarations as for named types.
  • Loading branch information
lars-reimann authored Oct 30, 2023
1 parent 182d64b commit 3762c36
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from 'langium';
import {
isSdsAbstractCall,
isSdsAnnotationCall,
isSdsArgument,
isSdsAssignment,
isSdsBlock,
Expand Down Expand Up @@ -54,6 +55,7 @@ import {
import { isContainedIn } from '../helpers/astUtils.js';
import {
getAbstractResults,
getAnnotationCallTarget,
getAssignees,
getEnumVariants,
getImportedDeclarations,
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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«
)
}
}

0 comments on commit 3762c36

Please sign in to comment.