Skip to content

Commit

Permalink
feat: scoping for type arguments (#585)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
lars-reimann and megalinter-bot authored Sep 30, 2023
1 parent ed0aa97 commit 3da8dd0
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/language/scoping/safe-ds-scope-computation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
isSdsTypeParameterList,
SdsClass,
SdsEnum,
SdsEnumVariant,
SdsTypeParameter,
} from '../generated/ast.js';

Expand All @@ -26,6 +27,8 @@ export class SafeDsScopeComputation extends DefaultScopeComputation {
this.processSdsClass(node, document, scopes);
} else if (isSdsEnum(node)) {
this.processSdsEnum(node, document, scopes);
} else if (isSdsEnumVariant(node)) {
this.processSdsEnumVariant(node, document, scopes);
} else if (isSdsTypeParameter(node)) {
this.processSdsTypeParameter(node, document, scopes);
} else {
Expand Down Expand Up @@ -68,6 +71,19 @@ export class SafeDsScopeComputation extends DefaultScopeComputation {
}
}

private processSdsEnumVariant(node: SdsEnumVariant, document: LangiumDocument, scopes: PrecomputedScopes): void {
const name = this.nameProvider.getName(node);
if (!name) {
/* c8 ignore next 2 */
return;
}

const description = this.descriptions.createDescription(node, name, document);

this.addToScopesIfKeyIsDefined(scopes, node.parameterList, description);
this.addToScopesIfKeyIsDefined(scopes, node.constraintList, description);
}

private processSdsTypeParameter(
node: SdsTypeParameter,
document: LangiumDocument,
Expand Down
25 changes: 25 additions & 0 deletions src/language/scoping/safe-ds-scope-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
isSdsCallable,
isSdsClass,
isSdsEnum,
isSdsEnumVariant,
isSdsLambda,
isSdsMemberAccess,
isSdsMemberType,
Expand All @@ -23,6 +24,7 @@ import {
isSdsReference,
isSdsSegment,
isSdsStatement,
isSdsTypeArgument,
isSdsYield,
SdsDeclaration,
SdsExpression,
Expand All @@ -33,6 +35,7 @@ import {
SdsReference,
SdsStatement,
SdsType,
SdsTypeArgument,
SdsYield,
} from '../generated/ast.js';
import {
Expand All @@ -42,6 +45,7 @@ import {
parametersOrEmpty,
resultsOrEmpty,
statementsOrEmpty,
typeParametersOrEmpty,
} from '../helpers/shortcuts.js';
import { isContainedIn } from '../helpers/ast.js';
import { isStatic } from '../helpers/checks.js';
Expand All @@ -62,6 +66,8 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
} else {
return this.getScopeForDirectReferenceTarget(node);
}
} else if (isSdsTypeArgument(node) && context.property === 'typeParameter') {
return this.getScopeForTypeArgumentTypeParameter(node);
} else if (isSdsYield(node) && context.property === 'result') {
return this.getScopeForYieldResult(node);
} else {
Expand Down Expand Up @@ -291,6 +297,25 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
}
}

private getScopeForTypeArgumentTypeParameter(node: SdsTypeArgument): Scope {
const containingNamedType = getContainerOfType(node, isSdsNamedType);
if (!containingNamedType) {
/* c8 ignore next 2 */
return EMPTY_SCOPE;
}

const namedTypeDeclaration = containingNamedType.declaration.ref;
if (isSdsClass(namedTypeDeclaration)) {
const typeParameters = typeParametersOrEmpty(namedTypeDeclaration.typeParameterList);
return this.createScopeForNodes(typeParameters);
} else if (isSdsEnumVariant(namedTypeDeclaration)) {
const typeParameters = typeParametersOrEmpty(namedTypeDeclaration.typeParameterList);
return this.createScopeForNodes(typeParameters);
} else {
return EMPTY_SCOPE;
}
}

private getScopeForYieldResult(node: SdsYield): Scope {
const containingSegment = getContainerOfType(node, isSdsSegment);
if (!containingSegment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ package test.scoping.namedTypes.toEnumVariantInGlobalEnum
enum BeforeEnum {
// $TEST$ target before
»BeforeVariant«

Variant1

// $TEST$ unresolved
Variant2(a: »Variant1«)
}

class BeforeVariant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ class BeforeClass {
enum BeforeEnum {
// $TEST$ target before
»BeforeVariant«

Variant1

// $TEST$ unresolved
Variant2(a: »Variant1«)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package test.scoping.typeArguments.toSomethingOtherThanTypeParameter

// $TEST$ unresolved
segment mySegment(p: MyClass<»notAtypeParameter« = Int>) {}

class MyClass<T>

fun notAtypeParameter()
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package test.scoping.typeArguments.toTypeParameterInContainingNamedTypeDeclaration

// $TEST$ target outerClass
class MyClass<»T«>(
// $TEST$ references outerClass
p: MyClass<»T« = Int>,

// $TEST$ references outerClass
) sub MyClass<»T« = Int> where {

// $TEST$ references outerClass
T sub MyClass<»T« = Int>,
} {
// $TEST$ references outerClass
attr a: MyClass<»T« = Int>

fun f<T>(
// $TEST$ references outerClass
p: MyClass<»T« = Int>,
) -> (
// $TEST$ references outerClass
r: MyClass<»T« = Int>,
) where {
// $TEST$ references outerClass
T sub MyClass<»T« = Int>,
}

enum MyEnum {
// $TEST$ target variant
MyEnumVariant<»T«>(
// $TEST$ references outerClass
p1: MyClass<»T« = Int>,

// $TEST$ references variant
p3: MyEnumVariant<»T« = Int>,
) where {
// $TEST$ references outerClass
T sub MyClass<»T« = Int>,

// $TEST$ references variant
T sub MyEnumVariant<»T« = Int>,
}
}

// $TEST$ target innerClass
class MyClass<»T«>(
// $TEST$ references innerClass
p: MyClass<»T« = Int>,

// $TEST$ references innerClass
) sub MyClass<»T« = Int> {
// $TEST$ references innerClass
attr a: MyClass<»T« = Int>

fun f<T>(
// $TEST$ references innerClass
p: MyClass<»T« = Int>,
) -> (
// $TEST$ references innerClass
r: MyClass<»T« = Int>,
) where {
// $TEST$ references innerClass
T sub MyClass<»T« = Int>,
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package test.scoping.typeArguments.toTypeParameterInEnumVariantInGlobalEnum

enum MyEnum {
// $TEST$ target t
MyEnumVariant<»T«>
}

segment mySegment(
// $TEST$ references t
p: MyEnum.MyEnumVariant<»T« = Int>
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package test.scoping.typeArguments.toTypeParameterInEnumVariantInNestedEnum

class MyClass<T> {
enum MyEnum {
// $TEST$ target t
MyEnumVariant<»T«>
}
}

segment mySegment(
// $TEST$ references t
p: MyClass.MyEnum.MyEnumVariant<»T« = Int>
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package test.scoping.typeArguments.toTypeParameterInGlobalClass

// $TEST$ target t
class MyClass<»T«>

segment mySegment(
// $TEST$ references t
p: MyClass<»T« = Int>
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package test.scoping.typeArguments.toTypeParameterInNestedClass

class MyClass<T> {
// $TEST$ target t
class MyNestedClass<»T«>
}

segment mySegment(
// $TEST$ references t
p: MyClass.MyNestedClass<»T« = Int>
) {}
11 changes: 11 additions & 0 deletions tests/resources/scoping/type arguments/to unresolved/main.sdstest
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package test.scoping.typeArguments.toUnresolved

segment mySegment(
// $TEST$ unresolved
p1: MyClass<»unresolved« = Int>,

// $TEST$ unresolved
p2: unresolved<»T« = Int>,
) {}

class MyClass<T>

0 comments on commit 3da8dd0

Please sign in to comment.