Skip to content

Commit 3762c36

Browse files
authored
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.
1 parent 182d64b commit 3762c36

File tree

2 files changed

+140
-14
lines changed

2 files changed

+140
-14
lines changed

packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts

+25-14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from 'langium';
1212
import {
1313
isSdsAbstractCall,
14+
isSdsAnnotationCall,
1415
isSdsArgument,
1516
isSdsAssignment,
1617
isSdsBlock,
@@ -54,6 +55,7 @@ import {
5455
import { isContainedIn } from '../helpers/astUtils.js';
5556
import {
5657
getAbstractResults,
58+
getAnnotationCallTarget,
5759
getAssignees,
5860
getEnumVariants,
5961
getImportedDeclarations,
@@ -251,25 +253,34 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
251253
// Declarations in this file
252254
currentScope = this.globalDeclarationsInSameFile(node, currentScope);
253255

254-
// // Declarations in containing classes
255-
// context.containingClassOrNull()?.let {
256-
// result = classMembers(it, result)
257-
// }
258-
//
256+
// Declarations in containing declarations
257+
currentScope = this.containingDeclarations(node, currentScope);
259258

260259
// Declarations in containing blocks
261260
return this.localDeclarations(node, currentScope);
262261
}
263262

264-
// private fun classMembers(context: SdsClass, parentScope: IScope): IScope {
265-
// return when (val containingClassOrNull = context.containingClassOrNull()) {
266-
// is SdsClass -> Scopes.scopeFor(
267-
// context.classMembersOrEmpty(),
268-
// classMembers(containingClassOrNull, parentScope),
269-
// )
270-
// else -> Scopes.scopeFor(context.classMembersOrEmpty(), parentScope)
271-
// }
272-
// }
263+
private containingDeclarations(node: AstNode, outerScope: Scope): Scope {
264+
const result = [];
265+
266+
// Cannot reference the target of an annotation call from inside the annotation call
267+
let start: AstNode | undefined;
268+
const containingAnnotationCall = getContainerOfType(node, isSdsAnnotationCall);
269+
if (containingAnnotationCall) {
270+
start = getAnnotationCallTarget(containingAnnotationCall)?.$container;
271+
} else {
272+
start = node.$container;
273+
}
274+
275+
// Only containing classes, enums, and enum variants can be referenced
276+
let current = getContainerOfType(start, isSdsNamedTypeDeclaration);
277+
while (current) {
278+
result.push(current);
279+
current = getContainerOfType(current.$container, isSdsNamedTypeDeclaration);
280+
}
281+
282+
return this.createScopeForNodes(result, outerScope);
283+
}
273284

274285
private globalDeclarationsInSameFile(node: AstNode, outerScope: Scope): Scope {
275286
const module = getContainerOfType(node, isSdsModule);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package tests.scoping.references.inSameFile.toContainingDeclarations
2+
3+
@Repeatable
4+
annotation MyAnnotation(p: Any?)
5+
6+
// $TEST$ target outerClass
7+
class »MyClass«(
8+
@MyAnnotation(
9+
// $TEST$ references outerClass
10+
p = »MyClass«()
11+
)
12+
// $TEST$ references outerClass
13+
p: Any? = »MyClass«()
14+
) {
15+
16+
@MyAnnotation(
17+
// $TEST$ references outerClass
18+
p = »MyClass«()
19+
)
20+
@MyAnnotation(
21+
// $TEST$ unresolved
22+
p = »MyEnum«
23+
)
24+
fun f(
25+
@MyAnnotation(
26+
// $TEST$ references outerClass
27+
p = »MyClass«()
28+
)
29+
// $TEST$ references outerClass
30+
p1: Any? = »MyClass«(),
31+
32+
@MyAnnotation(
33+
// $TEST$ unresolved
34+
p = »MyEnum«
35+
)
36+
// $TEST$ unresolved
37+
p2: Any? = »MyEnum«
38+
)
39+
40+
@MyAnnotation(
41+
// $TEST$ references outerClass
42+
p = »MyClass«()
43+
)
44+
@MyAnnotation(
45+
// $TEST$ unresolved
46+
p = »MyEnum«
47+
)
48+
// $TEST$ target enum
49+
enum »MyEnum« {
50+
// $TEST$ target variant
51+
»MyEnumVariant«(
52+
@MyAnnotation(
53+
// $TEST$ references outerClass
54+
p = »MyClass«()
55+
)
56+
// $TEST$ references outerClass
57+
p1: Any? = »MyClass«(),
58+
59+
@MyAnnotation(
60+
// $TEST$ references enum
61+
p = »MyEnum«
62+
)
63+
// $TEST$ references enum
64+
p2: Any? = »MyEnum«,
65+
66+
@MyAnnotation(
67+
// $TEST$ references variant
68+
p = »MyEnumVariant«
69+
)
70+
// $TEST$ references variant
71+
p3: Any? = »MyEnumVariant«,
72+
)
73+
}
74+
75+
@MyAnnotation(
76+
// $TEST$ references outerClass
77+
p = »MyClass«()
78+
)
79+
@MyAnnotation(
80+
// $TEST$ unresolved
81+
p = »MyEnum«
82+
)
83+
// $TEST$ target innerClass
84+
class »MyClass«(
85+
@MyAnnotation(
86+
// $TEST$ references innerClass
87+
p = »MyClass«()
88+
)
89+
// $TEST$ references innerClass
90+
p1: Any? = »MyClass«(),
91+
92+
@MyAnnotation(
93+
// $TEST$ unresolved
94+
p = »MyEnum«
95+
)
96+
// $TEST$ unresolved
97+
p2: Any? = »MyEnum«
98+
) {
99+
fun f(
100+
@MyAnnotation(
101+
// $TEST$ references innerClass
102+
p = »MyClass«()
103+
)
104+
// $TEST$ references innerClass
105+
p1: Any? = »MyClass«(),
106+
107+
@MyAnnotation(
108+
// $TEST$ unresolved
109+
p = »MyEnum«
110+
)
111+
// $TEST$ unresolved
112+
p2: Any? = »MyEnum«
113+
)
114+
}
115+
}

0 commit comments

Comments
 (0)