Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: scoping for named types #563

Merged
merged 11 commits into from
Sep 19, 2023
51 changes: 50 additions & 1 deletion src/language/scoping/safe-ds-scope-computation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,82 @@ import {
} from 'langium';
import {
isSdsClass,
isSdsDeclaration,
isSdsEnum,
isSdsEnumVariant,
isSdsFunction,
isSdsModule,
isSdsTypeParameter,
isSdsTypeParameterList,
SdsClass,
SdsEnum,
SdsTypeParameter,
} from '../generated/ast.js';

export class SafeDsScopeComputation extends DefaultScopeComputation {
override processNode(node: AstNode, document: LangiumDocument, scopes: PrecomputedScopes): void {
if (isSdsTypeParameter(node)) {
if (isSdsClass(node)) {
this.processSdsClass(node, document, scopes);
} else if (isSdsEnum(node)) {
this.processSdsEnum(node, document, scopes);
} else if (isSdsTypeParameter(node)) {
this.processSdsTypeParameter(node, document, scopes);
} else {
super.processNode(node, document, scopes);
}
}

private processSdsClass(node: SdsClass, document: LangiumDocument, scopes: PrecomputedScopes): void {
const name = this.nameProvider.getName(node);
if (!name) {
return;
}

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

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

const containingDeclaration = getContainerOfType(node.$container, isSdsDeclaration);
if (isSdsModule(containingDeclaration)) {
this.addToScopesIfKeyIsDefined(scopes, containingDeclaration, description);
}
}

private processSdsEnum(node: SdsEnum, document: LangiumDocument, scopes: PrecomputedScopes): void {
const name = this.nameProvider.getName(node);
if (!name) {
return;
}

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

this.addToScopesIfKeyIsDefined(scopes, node.body, description);

const containingDeclaration = getContainerOfType(node.$container, isSdsDeclaration);
if (isSdsModule(containingDeclaration)) {
this.addToScopesIfKeyIsDefined(scopes, containingDeclaration, description);
}
}

private processSdsTypeParameter(
node: SdsTypeParameter,
document: LangiumDocument,
scopes: PrecomputedScopes,
): void {
const containingDeclaration = getContainerOfType(node, isSdsTypeParameterList)?.$container;
if (!containingDeclaration) {
/* c8 ignore next 2 */
return;
}

const name = this.nameProvider.getName(node);
if (!name) {
/* c8 ignore next 2 */
return;
}

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

if (isSdsClass(containingDeclaration)) {
Expand Down
64 changes: 59 additions & 5 deletions src/language/scoping/safe-ds-scope-provider.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,72 @@
import { DefaultScopeProvider, EMPTY_SCOPE, getContainerOfType, ReferenceInfo, Scope } from 'langium';
import { isSdsSegment, isSdsYield } from '../generated/ast.js';
import {
isSdsClass,
isSdsEnum,
isSdsMemberType,
isSdsNamedType,
isSdsNamedTypeDeclaration,
isSdsSegment,
isSdsYield,
SdsMemberType,
SdsNamedTypeDeclaration,
SdsType,
SdsYield,
} from '../generated/ast.js';
import { resultsOrEmpty } from '../helpers/astShortcuts.js';

export class SafeDsScopeProvider extends DefaultScopeProvider {
override getScope(context: ReferenceInfo): Scope {
if (isSdsYield(context.container) && context.property === 'result') {
return this.getScopeForYieldResult(context);
if (isSdsNamedType(context.container) && context.property === 'declaration') {
const node = context.container;

if (isSdsMemberType(node.$container) && node.$containerProperty === 'member') {
return this.getScopeForMemberTypeMember(node.$container);
} else {
return super.getScope(context);
}
} else if (isSdsYield(context.container) && context.property === 'result') {
return this.getScopeForYieldResult(context.container);
} else {
return super.getScope(context);
}
}

getScopeForYieldResult(context: ReferenceInfo): Scope {
const containingSegment = getContainerOfType(context.container, isSdsSegment);
private getScopeForMemberTypeMember(node: SdsMemberType): Scope {
const declaration = this.getUniqueReferencedDeclarationForType(node.receiver);
if (!declaration) {
return EMPTY_SCOPE;
}

if (isSdsClass(declaration)) {
const members = declaration.body?.members ?? [];
return this.createScopeForNodes(members.filter(isSdsNamedTypeDeclaration));
} else if (isSdsEnum(declaration)) {
const variants = declaration.body?.variants ?? [];
return this.createScopeForNodes(variants);
} else {
return EMPTY_SCOPE;
}
}

/**
* Returns the unique declaration that is referenced by this type. If the type references none or multiple
* declarations, undefined is returned.
*
* @param type The type to get the referenced declaration for.
* @returns The referenced declaration or undefined.
*/
private getUniqueReferencedDeclarationForType(type: SdsType): SdsNamedTypeDeclaration | undefined {
if (isSdsNamedType(type)) {
return type.declaration.ref;
} else if (isSdsMemberType(type)) {
return type.member.declaration.ref;
} else {
return undefined;
}
}

private getScopeForYieldResult(node: SdsYield): Scope {
const containingSegment = getContainerOfType(node, isSdsSegment);
if (!containingSegment) {
return EMPTY_SCOPE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ annotation »Before«
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
annotation MyAnnotation

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ class MyClass {
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
attr myAttribute: Int
}

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ annotation »Before«
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
class MyClass

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ enum MyEnum {
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
MyEnumVariant
}

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ annotation »Before«
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
enum MyEnum

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ annotation »Before«
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
fun myFunction()

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// $TEST$ references annotation
@»MyAnnotation«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«

package test.scoping.annotationCalls.onModule

// $TEST$ target annotation
annotation »MyAnnotation«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ fun myFunction(
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
myParameter: Int
)

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ annotation »Before«
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
pipeline myPipeline {}

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ fun myFunction() -> (
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
myResult: Int
)

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ annotation »Before«
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
segment mySegment() {}

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ fun myFunction<
// $TEST$ references after
@»After«
// $TEST$ unresolved
@»NotAnAnnotation«
// $TEST$ unresolved
@»Unresolved«
MyTypeParameter
>()

// $TEST$ target after
annotation »After«

class NotAnAnnotation
Loading