Skip to content

Commit

Permalink
feat: scoping of arguments (#601)
Browse files Browse the repository at this point in the history
Closes partially #540.

### Summary of Changes

Named arguments now correctly point to their corresponding parameter.
  • Loading branch information
lars-reimann committed Oct 5, 2023
1 parent 8d68a42 commit 6b486cd
Show file tree
Hide file tree
Showing 16 changed files with 332 additions and 7 deletions.
39 changes: 38 additions & 1 deletion src/language/scoping/safe-ds-scope-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import {
Scope,
} from 'langium';
import {
isSdsAbstractCall,
isSdsAnnotationCall,
isSdsArgument,
isSdsAssignment,
isSdsBlock,
isSdsCall,
isSdsCallable,
isSdsClass,
isSdsEnum,
Expand All @@ -31,6 +35,7 @@ import {
isSdsTypeArgument,
isSdsWildcardImport,
isSdsYield,
SdsArgument,
SdsDeclaration,
SdsExpression,
SdsImportedDeclaration,
Expand Down Expand Up @@ -61,6 +66,7 @@ import { isStatic } from '../helpers/checks.js';
import { SafeDsServices } from '../safe-ds-module.js';
import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js';
import { SafeDsPackageManager } from '../workspace/safe-ds-package-manager.js';
import { CallableType, StaticType } from '../typing/model.js';

export class SafeDsScopeProvider extends DefaultScopeProvider {
private readonly astReflection: AstReflection;
Expand All @@ -78,7 +84,9 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
override getScope(context: ReferenceInfo): Scope {
const node = context.container;

if (isSdsImportedDeclaration(node) && context.property === 'declaration') {
if (isSdsArgument(node) && context.property === 'parameter') {
return this.getScopeForArgumentParameter(node);
} else if (isSdsImportedDeclaration(node) && context.property === 'declaration') {
return this.getScopeForImportedDeclarationDeclaration(node);
} else if (isSdsNamedType(node) && context.property === 'declaration') {
if (isSdsMemberType(node.$container) && node.$containerProperty === 'member') {
Expand All @@ -101,6 +109,35 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
}
}

private getScopeForArgumentParameter(node: SdsArgument): Scope {
const containingAbstractCall = getContainerOfType(node, isSdsAbstractCall);
if (isSdsAnnotationCall(containingAbstractCall)) {
const annotation = containingAbstractCall.annotation?.ref;
if (!annotation) {
return EMPTY_SCOPE;
}

const parameters = parametersOrEmpty(annotation.parameterList);
return this.createScopeForNodes(parameters);
} else if (isSdsCall(containingAbstractCall)) {
const receiverType = this.typeComputer.computeType(containingAbstractCall.receiver);
if (receiverType instanceof CallableType) {
const parameters = parametersOrEmpty(receiverType.sdsCallable.parameterList);
return this.createScopeForNodes(parameters);
} else if (receiverType instanceof StaticType) {
const declaration = receiverType.instanceType.sdsDeclaration;
if (isSdsCallable(declaration)) {
const parameters = parametersOrEmpty(declaration.parameterList);
return this.createScopeForNodes(parameters);
}
}

return EMPTY_SCOPE;
} /* c8 ignore start */ else {
return EMPTY_SCOPE;
} /* c8 ignore stop */
}

private getScopeForImportedDeclarationDeclaration(node: SdsImportedDeclaration): Scope {
const ownPackageName = packageNameOrNull(node);

Expand Down
4 changes: 2 additions & 2 deletions src/language/typing/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class CallableType extends Type {
override isNullable: boolean = false;

constructor(
readonly callable: SdsCallable,
readonly sdsCallable: SdsCallable,
readonly inputType: NamedTupleType,
readonly outputType: NamedTupleType,
) {
Expand All @@ -48,7 +48,7 @@ export class CallableType extends Type {
}

return (
other.callable === this.callable &&
other.sdsCallable === this.sdsCallable &&
other.inputType.equals(this.inputType) &&
other.outputType.equals(this.outputType)
);
Expand Down
6 changes: 2 additions & 4 deletions src/language/typing/safe-ds-type-computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,14 +357,12 @@ export class SafeDsTypeComputer {
const receiverType = this.computeType(node.receiver);

if (receiverType instanceof CallableType) {
if (!isSdsAnnotation(receiverType.callable)) {
if (!isSdsAnnotation(receiverType.sdsCallable)) {
return receiverType.outputType;
}
} else if (receiverType instanceof StaticType) {
const instanceType = receiverType.instanceType;
const declaration = instanceType.sdsDeclaration;

if (isSdsClass(declaration) || isSdsEnumVariant(declaration)) {
if (isSdsCallable(instanceType.sdsDeclaration)) {
return instanceType;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package tests.scoping.arguments.ofAnnotationCalls.toParameter

annotation MyAnnotation(
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
)

@MyAnnotation(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
)
pipeline myPipeline {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tests.scoping.arguments.ofAnnotationCalls.toSomethingOtherThanParameter

annotation MyAnnotation(a: Int)

@MyAnnotation(
// $TEST$ unresolved
»MyAnnotation« = 0,
)
pipeline myPipeline {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package tests.scoping.arguments.ofAnnotationCalls.unresolved

annotation MyAnnotation(a: Int)

@MyAnnotation(
// $TEST$ unresolved
»unresolved« = 0,
)
@Unresolved(
// $TEST$ unresolved
»a« = 0
)
pipeline myPipeline {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package tests.scoping.arguments.ofCalls.toParameterOfAnnotation

annotation MyAnnotation(
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
)

pipeline myPipeline {
MyAnnotation(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);

val alias = MyAnnotation;
alias(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package tests.scoping.arguments.ofCalls.toParameterOfBlockLambda

pipeline myPipeline {
val myBlockLambda = (
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
) {};

myBlockLambda(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package tests.scoping.arguments.ofCalls.toParameterOfCallableType

segment mySegment(myCallableType: (
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
) -> ()) {
myCallableType(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);

val alias = myCallableType;
alias(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package tests.scoping.arguments.ofCalls.toParameterOfClass

class MyClass(
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
) {}

pipeline myPipeline {
MyClass(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);

val alias = MyClass;
alias(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package tests.scoping.arguments.ofCalls.toParameterOfEnumVariant

enum MyEnum {
MyEnumVariant(
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
)
}


pipeline myPipeline {
MyEnum.MyEnumVariant(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);

val alias = MyEnum.MyEnumVariant;
alias(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package tests.scoping.arguments.ofCalls.toParameterOfExpressionLambda

pipeline myPipeline {
val myExpressionLambda = (
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
) -> 1;

myExpressionLambda(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package tests.scoping.arguments.ofCalls.toParameterOfFunction

fun myFunction(
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
)

pipeline myPipeline {
myFunction(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);

val alias = myFunction;
alias(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package tests.scoping.arguments.ofCalls.toParameterOfSegment

segment mySegment(
// $TEST$ target a
»a«: Int,
// $TEST$ target b
»b«: Int = 0,
// $TEST$ target c
vararg »c«: Int
) {}

pipeline myPipeline {
mySegment(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);

val alias = mySegment;
alias(
// $TEST$ references c
»c« = 0,
// $TEST$ references a
»a« = 0,
// $TEST$ references b
»b« = 0
);
}
Loading

0 comments on commit 6b486cd

Please sign in to comment.