Skip to content

Commit a877f4c

Browse files
feat: scoping for named types (#563)
Closes partially #540 ### Summary of Changes * Implement scoping for named types --------- Co-authored-by: megalinter-bot <[email protected]>
1 parent a510f2b commit a877f4c

File tree

37 files changed

+873
-8
lines changed

37 files changed

+873
-8
lines changed

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

+50-1
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,82 @@ import {
88
} from 'langium';
99
import {
1010
isSdsClass,
11+
isSdsDeclaration,
12+
isSdsEnum,
1113
isSdsEnumVariant,
1214
isSdsFunction,
15+
isSdsModule,
1316
isSdsTypeParameter,
1417
isSdsTypeParameterList,
18+
SdsClass,
19+
SdsEnum,
1520
SdsTypeParameter,
1621
} from '../generated/ast.js';
1722

1823
export class SafeDsScopeComputation extends DefaultScopeComputation {
1924
override processNode(node: AstNode, document: LangiumDocument, scopes: PrecomputedScopes): void {
20-
if (isSdsTypeParameter(node)) {
25+
if (isSdsClass(node)) {
26+
this.processSdsClass(node, document, scopes);
27+
} else if (isSdsEnum(node)) {
28+
this.processSdsEnum(node, document, scopes);
29+
} else if (isSdsTypeParameter(node)) {
2130
this.processSdsTypeParameter(node, document, scopes);
2231
} else {
2332
super.processNode(node, document, scopes);
2433
}
2534
}
2635

36+
private processSdsClass(node: SdsClass, document: LangiumDocument, scopes: PrecomputedScopes): void {
37+
const name = this.nameProvider.getName(node);
38+
if (!name) {
39+
return;
40+
}
41+
42+
const description = this.descriptions.createDescription(node, name, document);
43+
44+
this.addToScopesIfKeyIsDefined(scopes, node.parameterList, description);
45+
this.addToScopesIfKeyIsDefined(scopes, node.constraintList, description);
46+
this.addToScopesIfKeyIsDefined(scopes, node.body, description);
47+
48+
const containingDeclaration = getContainerOfType(node.$container, isSdsDeclaration);
49+
if (isSdsModule(containingDeclaration)) {
50+
this.addToScopesIfKeyIsDefined(scopes, containingDeclaration, description);
51+
}
52+
}
53+
54+
private processSdsEnum(node: SdsEnum, document: LangiumDocument, scopes: PrecomputedScopes): void {
55+
const name = this.nameProvider.getName(node);
56+
if (!name) {
57+
return;
58+
}
59+
60+
const description = this.descriptions.createDescription(node, name, document);
61+
62+
this.addToScopesIfKeyIsDefined(scopes, node.body, description);
63+
64+
const containingDeclaration = getContainerOfType(node.$container, isSdsDeclaration);
65+
if (isSdsModule(containingDeclaration)) {
66+
this.addToScopesIfKeyIsDefined(scopes, containingDeclaration, description);
67+
}
68+
}
69+
2770
private processSdsTypeParameter(
2871
node: SdsTypeParameter,
2972
document: LangiumDocument,
3073
scopes: PrecomputedScopes,
3174
): void {
3275
const containingDeclaration = getContainerOfType(node, isSdsTypeParameterList)?.$container;
3376
if (!containingDeclaration) {
77+
/* c8 ignore next 2 */
3478
return;
3579
}
3680

3781
const name = this.nameProvider.getName(node);
82+
if (!name) {
83+
/* c8 ignore next 2 */
84+
return;
85+
}
86+
3887
const description = this.descriptions.createDescription(node, name, document);
3988

4089
if (isSdsClass(containingDeclaration)) {

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

+59-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,72 @@
11
import { DefaultScopeProvider, EMPTY_SCOPE, getContainerOfType, ReferenceInfo, Scope } from 'langium';
2-
import { isSdsSegment, isSdsYield } from '../generated/ast.js';
2+
import {
3+
isSdsClass,
4+
isSdsEnum,
5+
isSdsMemberType,
6+
isSdsNamedType,
7+
isSdsNamedTypeDeclaration,
8+
isSdsSegment,
9+
isSdsYield,
10+
SdsMemberType,
11+
SdsNamedTypeDeclaration,
12+
SdsType,
13+
SdsYield,
14+
} from '../generated/ast.js';
315
import { resultsOrEmpty } from '../helpers/astShortcuts.js';
416

517
export class SafeDsScopeProvider extends DefaultScopeProvider {
618
override getScope(context: ReferenceInfo): Scope {
7-
if (isSdsYield(context.container) && context.property === 'result') {
8-
return this.getScopeForYieldResult(context);
19+
if (isSdsNamedType(context.container) && context.property === 'declaration') {
20+
const node = context.container;
21+
22+
if (isSdsMemberType(node.$container) && node.$containerProperty === 'member') {
23+
return this.getScopeForMemberTypeMember(node.$container);
24+
} else {
25+
return super.getScope(context);
26+
}
27+
} else if (isSdsYield(context.container) && context.property === 'result') {
28+
return this.getScopeForYieldResult(context.container);
929
} else {
1030
return super.getScope(context);
1131
}
1232
}
1333

14-
getScopeForYieldResult(context: ReferenceInfo): Scope {
15-
const containingSegment = getContainerOfType(context.container, isSdsSegment);
34+
private getScopeForMemberTypeMember(node: SdsMemberType): Scope {
35+
const declaration = this.getUniqueReferencedDeclarationForType(node.receiver);
36+
if (!declaration) {
37+
return EMPTY_SCOPE;
38+
}
39+
40+
if (isSdsClass(declaration)) {
41+
const members = declaration.body?.members ?? [];
42+
return this.createScopeForNodes(members.filter(isSdsNamedTypeDeclaration));
43+
} else if (isSdsEnum(declaration)) {
44+
const variants = declaration.body?.variants ?? [];
45+
return this.createScopeForNodes(variants);
46+
} else {
47+
return EMPTY_SCOPE;
48+
}
49+
}
50+
51+
/**
52+
* Returns the unique declaration that is referenced by this type. If the type references none or multiple
53+
* declarations, undefined is returned.
54+
*
55+
* @param type The type to get the referenced declaration for.
56+
* @returns The referenced declaration or undefined.
57+
*/
58+
private getUniqueReferencedDeclarationForType(type: SdsType): SdsNamedTypeDeclaration | undefined {
59+
if (isSdsNamedType(type)) {
60+
return type.declaration.ref;
61+
} else if (isSdsMemberType(type)) {
62+
return type.member.declaration.ref;
63+
} else {
64+
return undefined;
65+
}
66+
}
67+
68+
private getScopeForYieldResult(node: SdsYield): Scope {
69+
const containingSegment = getContainerOfType(node, isSdsSegment);
1670
if (!containingSegment) {
1771
return EMPTY_SCOPE;
1872
}

tests/resources/scoping/annotation calls/on annotation/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ annotation »Before«
88
// $TEST$ references after
99
@»After«
1010
// $TEST$ unresolved
11+
@»NotAnAnnotation«
12+
// $TEST$ unresolved
1113
@»Unresolved«
1214
annotation MyAnnotation
1315

1416
// $TEST$ target after
1517
annotation »After«
18+
19+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on attribute/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ class MyClass {
99
// $TEST$ references after
1010
@»After«
1111
// $TEST$ unresolved
12+
@»NotAnAnnotation«
13+
// $TEST$ unresolved
1214
@»Unresolved«
1315
attr myAttribute: Int
1416
}
1517

1618
// $TEST$ target after
1719
annotation »After«
20+
21+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on class/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ annotation »Before«
88
// $TEST$ references after
99
@»After«
1010
// $TEST$ unresolved
11+
@»NotAnAnnotation«
12+
// $TEST$ unresolved
1113
@»Unresolved«
1214
class MyClass
1315

1416
// $TEST$ target after
1517
annotation »After«
18+
19+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on enum variant/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ enum MyEnum {
99
// $TEST$ references after
1010
@»After«
1111
// $TEST$ unresolved
12+
@»NotAnAnnotation«
13+
// $TEST$ unresolved
1214
@»Unresolved«
1315
MyEnumVariant
1416
}
1517

1618
// $TEST$ target after
1719
annotation »After«
20+
21+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on enum/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ annotation »Before«
88
// $TEST$ references after
99
@»After«
1010
// $TEST$ unresolved
11+
@»NotAnAnnotation«
12+
// $TEST$ unresolved
1113
@»Unresolved«
1214
enum MyEnum
1315

1416
// $TEST$ target after
1517
annotation »After«
18+
19+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on function/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ annotation »Before«
88
// $TEST$ references after
99
@»After«
1010
// $TEST$ unresolved
11+
@»NotAnAnnotation«
12+
// $TEST$ unresolved
1113
@»Unresolved«
1214
fun myFunction()
1315

1416
// $TEST$ target after
1517
annotation »After«
18+
19+
class NotAnAnnotation
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// $TEST$ references annotation
22
@»MyAnnotation«
33
// $TEST$ unresolved
4+
@»NotAnAnnotation«
5+
// $TEST$ unresolved
46
@»Unresolved«
57

68
package test.scoping.annotationCalls.onModule
79

810
// $TEST$ target annotation
911
annotation »MyAnnotation«
12+
13+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on parameter/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ fun myFunction(
99
// $TEST$ references after
1010
@»After«
1111
// $TEST$ unresolved
12+
@»NotAnAnnotation«
13+
// $TEST$ unresolved
1214
@»Unresolved«
1315
myParameter: Int
1416
)
1517

1618
// $TEST$ target after
1719
annotation »After«
20+
21+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on pipeline/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ annotation »Before«
88
// $TEST$ references after
99
@»After«
1010
// $TEST$ unresolved
11+
@»NotAnAnnotation«
12+
// $TEST$ unresolved
1113
@»Unresolved«
1214
pipeline myPipeline {}
1315

1416
// $TEST$ target after
1517
annotation »After«
18+
19+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on result/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ fun myFunction() -> (
99
// $TEST$ references after
1010
@»After«
1111
// $TEST$ unresolved
12+
@»NotAnAnnotation«
13+
// $TEST$ unresolved
1214
@»Unresolved«
1315
myResult: Int
1416
)
1517

1618
// $TEST$ target after
1719
annotation »After«
20+
21+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on segment/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ annotation »Before«
88
// $TEST$ references after
99
@»After«
1010
// $TEST$ unresolved
11+
@»NotAnAnnotation«
12+
// $TEST$ unresolved
1113
@»Unresolved«
1214
segment mySegment() {}
1315

1416
// $TEST$ target after
1517
annotation »After«
18+
19+
class NotAnAnnotation

tests/resources/scoping/annotation calls/on type parameter/main.sdstest

+4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ fun myFunction<
99
// $TEST$ references after
1010
@»After«
1111
// $TEST$ unresolved
12+
@»NotAnAnnotation«
13+
// $TEST$ unresolved
1214
@»Unresolved«
1315
MyTypeParameter
1416
>()
1517

1618
// $TEST$ target after
1719
annotation »After«
20+
21+
class NotAnAnnotation

0 commit comments

Comments
 (0)