diff --git a/src/language/scoping/safe-ds-scope-provider.ts b/src/language/scoping/safe-ds-scope-provider.ts index 34a44f6d7..afc1f4399 100644 --- a/src/language/scoping/safe-ds-scope-provider.ts +++ b/src/language/scoping/safe-ds-scope-provider.ts @@ -10,8 +10,12 @@ import { Scope, } from 'langium'; import { + isSdsAbstractCall, + isSdsAnnotationCall, + isSdsArgument, isSdsAssignment, isSdsBlock, + isSdsCall, isSdsCallable, isSdsClass, isSdsEnum, @@ -31,6 +35,7 @@ import { isSdsTypeArgument, isSdsWildcardImport, isSdsYield, + SdsArgument, SdsDeclaration, SdsExpression, SdsImportedDeclaration, @@ -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; @@ -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') { @@ -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); diff --git a/src/language/typing/model.ts b/src/language/typing/model.ts index d791bdba7..22c10882c 100644 --- a/src/language/typing/model.ts +++ b/src/language/typing/model.ts @@ -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, ) { @@ -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) ); diff --git a/src/language/typing/safe-ds-type-computer.ts b/src/language/typing/safe-ds-type-computer.ts index 142708c3f..f11429f5f 100644 --- a/src/language/typing/safe-ds-type-computer.ts +++ b/src/language/typing/safe-ds-type-computer.ts @@ -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; } } diff --git a/tests/resources/scoping/arguments/of annotation calls/to parameter/main.sdstest b/tests/resources/scoping/arguments/of annotation calls/to parameter/main.sdstest new file mode 100644 index 000000000..3fa6c7e51 --- /dev/null +++ b/tests/resources/scoping/arguments/of annotation calls/to parameter/main.sdstest @@ -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 {} diff --git a/tests/resources/scoping/arguments/of annotation calls/to something other than parameter/main.sdstest b/tests/resources/scoping/arguments/of annotation calls/to something other than parameter/main.sdstest new file mode 100644 index 000000000..a0edd6f00 --- /dev/null +++ b/tests/resources/scoping/arguments/of annotation calls/to something other than parameter/main.sdstest @@ -0,0 +1,9 @@ +package tests.scoping.arguments.ofAnnotationCalls.toSomethingOtherThanParameter + +annotation MyAnnotation(a: Int) + +@MyAnnotation( + // $TEST$ unresolved + »MyAnnotation« = 0, +) +pipeline myPipeline {} diff --git a/tests/resources/scoping/arguments/of annotation calls/unresolved/main.sdstest b/tests/resources/scoping/arguments/of annotation calls/unresolved/main.sdstest new file mode 100644 index 000000000..d391846e8 --- /dev/null +++ b/tests/resources/scoping/arguments/of annotation calls/unresolved/main.sdstest @@ -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 {} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of annotation/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of annotation/main.sdstest new file mode 100644 index 000000000..6bafc607a --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of annotation/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of block lambda/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of block lambda/main.sdstest new file mode 100644 index 000000000..30d60c9a7 --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of block lambda/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of callable type/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of callable type/main.sdstest new file mode 100644 index 000000000..d01a3622b --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of callable type/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of class/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of class/main.sdstest new file mode 100644 index 000000000..9d2493ae4 --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of class/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of enum variant/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of enum variant/main.sdstest new file mode 100644 index 000000000..5bb626e9a --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of enum variant/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of expression lambda/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of expression lambda/main.sdstest new file mode 100644 index 000000000..0a62f23e1 --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of expression lambda/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of function/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of function/main.sdstest new file mode 100644 index 000000000..9f9f4f632 --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of function/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to parameter of segment/main.sdstest b/tests/resources/scoping/arguments/of calls/to parameter of segment/main.sdstest new file mode 100644 index 000000000..c31352a55 --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to parameter of segment/main.sdstest @@ -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 + ); +} diff --git a/tests/resources/scoping/arguments/of calls/to something other than parameter/main.sdstest b/tests/resources/scoping/arguments/of calls/to something other than parameter/main.sdstest new file mode 100644 index 000000000..9df97d670 --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/to something other than parameter/main.sdstest @@ -0,0 +1,8 @@ +package tests.scoping.arguments.ofCalls.toSomethingOtherThanParameter + +fun f(a: Int) + +pipeline myPipeline { + // $TEST$ unresolved + f(»f« = 1); +} diff --git a/tests/resources/scoping/arguments/of calls/unresolved/main.sdstest b/tests/resources/scoping/arguments/of calls/unresolved/main.sdstest new file mode 100644 index 000000000..2c2292467 --- /dev/null +++ b/tests/resources/scoping/arguments/of calls/unresolved/main.sdstest @@ -0,0 +1,11 @@ +package tests.scoping.arguments.ofCalls.unresolved + +fun f(a: Int) + +pipeline myPipeline { + // $TEST$ unresolved + f(»unresolved« = 1); + + // $TEST$ unresolved + unresolved(»a« = 1); +}