Skip to content

Commit 3da8dd0

Browse files
feat: scoping for type arguments (#585)
Closes partially #540 ### Summary of Changes Implement scoping rules for the `typeParameter` property of a type argument. --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
1 parent ed0aa97 commit 3da8dd0

File tree

11 files changed

+180
-0
lines changed

11 files changed

+180
-0
lines changed

src/language/scoping/safe-ds-scope-computation.ts

+16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
isSdsTypeParameterList,
1818
SdsClass,
1919
SdsEnum,
20+
SdsEnumVariant,
2021
SdsTypeParameter,
2122
} from '../generated/ast.js';
2223

@@ -26,6 +27,8 @@ export class SafeDsScopeComputation extends DefaultScopeComputation {
2627
this.processSdsClass(node, document, scopes);
2728
} else if (isSdsEnum(node)) {
2829
this.processSdsEnum(node, document, scopes);
30+
} else if (isSdsEnumVariant(node)) {
31+
this.processSdsEnumVariant(node, document, scopes);
2932
} else if (isSdsTypeParameter(node)) {
3033
this.processSdsTypeParameter(node, document, scopes);
3134
} else {
@@ -68,6 +71,19 @@ export class SafeDsScopeComputation extends DefaultScopeComputation {
6871
}
6972
}
7073

74+
private processSdsEnumVariant(node: SdsEnumVariant, document: LangiumDocument, scopes: PrecomputedScopes): void {
75+
const name = this.nameProvider.getName(node);
76+
if (!name) {
77+
/* c8 ignore next 2 */
78+
return;
79+
}
80+
81+
const description = this.descriptions.createDescription(node, name, document);
82+
83+
this.addToScopesIfKeyIsDefined(scopes, node.parameterList, description);
84+
this.addToScopesIfKeyIsDefined(scopes, node.constraintList, description);
85+
}
86+
7187
private processSdsTypeParameter(
7288
node: SdsTypeParameter,
7389
document: LangiumDocument,

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

+25
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
isSdsCallable,
1414
isSdsClass,
1515
isSdsEnum,
16+
isSdsEnumVariant,
1617
isSdsLambda,
1718
isSdsMemberAccess,
1819
isSdsMemberType,
@@ -23,6 +24,7 @@ import {
2324
isSdsReference,
2425
isSdsSegment,
2526
isSdsStatement,
27+
isSdsTypeArgument,
2628
isSdsYield,
2729
SdsDeclaration,
2830
SdsExpression,
@@ -33,6 +35,7 @@ import {
3335
SdsReference,
3436
SdsStatement,
3537
SdsType,
38+
SdsTypeArgument,
3639
SdsYield,
3740
} from '../generated/ast.js';
3841
import {
@@ -42,6 +45,7 @@ import {
4245
parametersOrEmpty,
4346
resultsOrEmpty,
4447
statementsOrEmpty,
48+
typeParametersOrEmpty,
4549
} from '../helpers/shortcuts.js';
4650
import { isContainedIn } from '../helpers/ast.js';
4751
import { isStatic } from '../helpers/checks.js';
@@ -62,6 +66,8 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
6266
} else {
6367
return this.getScopeForDirectReferenceTarget(node);
6468
}
69+
} else if (isSdsTypeArgument(node) && context.property === 'typeParameter') {
70+
return this.getScopeForTypeArgumentTypeParameter(node);
6571
} else if (isSdsYield(node) && context.property === 'result') {
6672
return this.getScopeForYieldResult(node);
6773
} else {
@@ -291,6 +297,25 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
291297
}
292298
}
293299

300+
private getScopeForTypeArgumentTypeParameter(node: SdsTypeArgument): Scope {
301+
const containingNamedType = getContainerOfType(node, isSdsNamedType);
302+
if (!containingNamedType) {
303+
/* c8 ignore next 2 */
304+
return EMPTY_SCOPE;
305+
}
306+
307+
const namedTypeDeclaration = containingNamedType.declaration.ref;
308+
if (isSdsClass(namedTypeDeclaration)) {
309+
const typeParameters = typeParametersOrEmpty(namedTypeDeclaration.typeParameterList);
310+
return this.createScopeForNodes(typeParameters);
311+
} else if (isSdsEnumVariant(namedTypeDeclaration)) {
312+
const typeParameters = typeParametersOrEmpty(namedTypeDeclaration.typeParameterList);
313+
return this.createScopeForNodes(typeParameters);
314+
} else {
315+
return EMPTY_SCOPE;
316+
}
317+
}
318+
294319
private getScopeForYieldResult(node: SdsYield): Scope {
295320
const containingSegment = getContainerOfType(node, isSdsSegment);
296321
if (!containingSegment) {

tests/resources/scoping/named types/to enum variant in global enum/main.sdstest

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ package test.scoping.namedTypes.toEnumVariantInGlobalEnum
33
enum BeforeEnum {
44
// $TEST$ target before
55
»BeforeVariant«
6+
7+
Variant1
8+
9+
// $TEST$ unresolved
10+
Variant2(a: »Variant1«)
611
}
712

813
class BeforeVariant

tests/resources/scoping/named types/to enum variant in nested enum/main.sdstest

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ class BeforeClass {
44
enum BeforeEnum {
55
// $TEST$ target before
66
»BeforeVariant«
7+
8+
Variant1
9+
10+
// $TEST$ unresolved
11+
Variant2(a: »Variant1«)
712
}
813
}
914

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package test.scoping.typeArguments.toSomethingOtherThanTypeParameter
2+
3+
// $TEST$ unresolved
4+
segment mySegment(p: MyClass<»notAtypeParameter« = Int>) {}
5+
6+
class MyClass<T>
7+
8+
fun notAtypeParameter()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package test.scoping.typeArguments.toTypeParameterInContainingNamedTypeDeclaration
2+
3+
// $TEST$ target outerClass
4+
class MyClass<»T«>(
5+
// $TEST$ references outerClass
6+
p: MyClass<»T« = Int>,
7+
8+
// $TEST$ references outerClass
9+
) sub MyClass<»T« = Int> where {
10+
11+
// $TEST$ references outerClass
12+
T sub MyClass<»T« = Int>,
13+
} {
14+
// $TEST$ references outerClass
15+
attr a: MyClass<»T« = Int>
16+
17+
fun f<T>(
18+
// $TEST$ references outerClass
19+
p: MyClass<»T« = Int>,
20+
) -> (
21+
// $TEST$ references outerClass
22+
r: MyClass<»T« = Int>,
23+
) where {
24+
// $TEST$ references outerClass
25+
T sub MyClass<»T« = Int>,
26+
}
27+
28+
enum MyEnum {
29+
// $TEST$ target variant
30+
MyEnumVariant<»T«>(
31+
// $TEST$ references outerClass
32+
p1: MyClass<»T« = Int>,
33+
34+
// $TEST$ references variant
35+
p3: MyEnumVariant<»T« = Int>,
36+
) where {
37+
// $TEST$ references outerClass
38+
T sub MyClass<»T« = Int>,
39+
40+
// $TEST$ references variant
41+
T sub MyEnumVariant<»T« = Int>,
42+
}
43+
}
44+
45+
// $TEST$ target innerClass
46+
class MyClass<»T«>(
47+
// $TEST$ references innerClass
48+
p: MyClass<»T« = Int>,
49+
50+
// $TEST$ references innerClass
51+
) sub MyClass<»T« = Int> {
52+
// $TEST$ references innerClass
53+
attr a: MyClass<»T« = Int>
54+
55+
fun f<T>(
56+
// $TEST$ references innerClass
57+
p: MyClass<»T« = Int>,
58+
) -> (
59+
// $TEST$ references innerClass
60+
r: MyClass<»T« = Int>,
61+
) where {
62+
// $TEST$ references innerClass
63+
T sub MyClass<»T« = Int>,
64+
}
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package test.scoping.typeArguments.toTypeParameterInEnumVariantInGlobalEnum
2+
3+
enum MyEnum {
4+
// $TEST$ target t
5+
MyEnumVariant<»T«>
6+
}
7+
8+
segment mySegment(
9+
// $TEST$ references t
10+
p: MyEnum.MyEnumVariant<»T« = Int>
11+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.scoping.typeArguments.toTypeParameterInEnumVariantInNestedEnum
2+
3+
class MyClass<T> {
4+
enum MyEnum {
5+
// $TEST$ target t
6+
MyEnumVariant<»T«>
7+
}
8+
}
9+
10+
segment mySegment(
11+
// $TEST$ references t
12+
p: MyClass.MyEnum.MyEnumVariant<»T« = Int>
13+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package test.scoping.typeArguments.toTypeParameterInGlobalClass
2+
3+
// $TEST$ target t
4+
class MyClass<»T«>
5+
6+
segment mySegment(
7+
// $TEST$ references t
8+
p: MyClass<»T« = Int>
9+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package test.scoping.typeArguments.toTypeParameterInNestedClass
2+
3+
class MyClass<T> {
4+
// $TEST$ target t
5+
class MyNestedClass<»T«>
6+
}
7+
8+
segment mySegment(
9+
// $TEST$ references t
10+
p: MyClass.MyNestedClass<»T« = Int>
11+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package test.scoping.typeArguments.toUnresolved
2+
3+
segment mySegment(
4+
// $TEST$ unresolved
5+
p1: MyClass<»unresolved« = Int>,
6+
7+
// $TEST$ unresolved
8+
p2: unresolved<»T« = Int>,
9+
) {}
10+
11+
class MyClass<T>

0 commit comments

Comments
 (0)