From 52374aa2c0b168ff6b81b53a2d745ebd6303f1b9 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 10 Nov 2023 10:57:46 +0100 Subject: [PATCH] feat: error if type parameter is used in nested named type declaration (#750) Closes #748 ### Summary of Changes Show an error if a type parameter of a class is used inside a nested named type declaration. --- docs/language/common/types.md | 2 +- .../other/declarations/typeParameters.ts | 31 ++++++++++++++++++- .../language/validation/safe-ds-validator.ts | 10 ++++-- .../main.sdstest | 2 +- .../type parameters/usage/main.sdstest | 29 +++++++++++++++++ 5 files changed, 69 insertions(+), 5 deletions(-) rename packages/safe-ds-lang/tests/resources/validation/other/declarations/{ => constraints}/type parameter constraints/left operand must be own type parameter/main.sdstest (96%) create mode 100644 packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage/main.sdstest diff --git a/docs/language/common/types.md b/docs/language/common/types.md index eda797e82..fa6bd2e26 100644 --- a/docs/language/common/types.md +++ b/docs/language/common/types.md @@ -40,7 +40,7 @@ A declaration with an _enum type_ must be one of the [variants][variants] of the ```sds enum SomeEnum { - SomeEnumVariant, + SomeEnumVariant SomeOtherEnumVariant(count: Int) } ``` diff --git a/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts b/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts index 26a738147..71fcb80f8 100644 --- a/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts +++ b/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts @@ -1,13 +1,16 @@ +import { findLocalReferences, getContainerOfType, hasContainerOfType, ValidationAcceptor } from 'langium'; import { isSdsCallable, isSdsClass, + isSdsDeclaration, + isSdsNamedTypeDeclaration, isSdsParameterList, isSdsUnionType, SdsTypeParameter, } from '../../../generated/ast.js'; -import { findLocalReferences, getContainerOfType, hasContainerOfType, ValidationAcceptor } from 'langium'; export const CODE_TYPE_PARAMETER_INSUFFICIENT_CONTEXT = 'type-parameter/insufficient-context'; +export const CODE_TYPE_PARAMETER_USAGE = 'type-parameter/usage'; export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, accept: ValidationAcceptor) => { const containingCallable = getContainerOfType(node, isSdsCallable); @@ -45,3 +48,29 @@ export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, a }); } }; + +export const typeParameterMustNotBeUsedInNestedNamedTypeDeclarations = ( + node: SdsTypeParameter, + accept: ValidationAcceptor, +) => { + // Only classes can have nested named type declarations + const declarationWithTypeParameter = getContainerOfType(node.$container, isSdsDeclaration); + if (!isSdsClass(declarationWithTypeParameter)) { + return; + } + + findLocalReferences(node).forEach((it) => { + const reference = it.$refNode?.astNode; + const containingNamedTypeDeclaration = getContainerOfType(reference, isSdsNamedTypeDeclaration); + if ( + reference && + containingNamedTypeDeclaration && + containingNamedTypeDeclaration !== declarationWithTypeParameter + ) { + accept('error', 'Type parameters cannot be used in nested named type declarations.', { + node: reference, + code: CODE_TYPE_PARAMETER_USAGE, + }); + } + }); +}; diff --git a/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts b/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts index f078cb7b0..765892e52 100644 --- a/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts +++ b/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts @@ -72,7 +72,10 @@ import { segmentShouldBeUsed, } from './other/declarations/segments.js'; import { typeParameterConstraintLeftOperandMustBeOwnTypeParameter } from './other/declarations/typeParameterConstraints.js'; -import { typeParameterMustHaveSufficientContext } from './other/declarations/typeParameters.js'; +import { + typeParameterMustHaveSufficientContext, + typeParameterMustNotBeUsedInNestedNamedTypeDeclarations, +} from './other/declarations/typeParameters.js'; import { callArgumentsMustBeConstantIfParameterIsConstant } from './other/expressions/calls.js'; import { divisionDivisorMustNotBeZero } from './other/expressions/infixOperations.js'; import { @@ -309,7 +312,10 @@ export const registerValidationChecks = function (services: SafeDsServices) { segmentShouldBeUsed(services), ], SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts], - SdsTypeParameter: [typeParameterMustHaveSufficientContext], + SdsTypeParameter: [ + typeParameterMustHaveSufficientContext, + typeParameterMustNotBeUsedInNestedNamedTypeDeclarations, + ], SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter], SdsTypeParameterList: [typeParameterListShouldNotBeEmpty], SdsUnionType: [ diff --git a/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameter constraints/left operand must be own type parameter/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/other/declarations/constraints/type parameter constraints/left operand must be own type parameter/main.sdstest similarity index 96% rename from packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameter constraints/left operand must be own type parameter/main.sdstest rename to packages/safe-ds-lang/tests/resources/validation/other/declarations/constraints/type parameter constraints/left operand must be own type parameter/main.sdstest index d2fc27800..1cfaad7c1 100644 --- a/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameter constraints/left operand must be own type parameter/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/validation/other/declarations/constraints/type parameter constraints/left operand must be own type parameter/main.sdstest @@ -1,4 +1,4 @@ -package tests.validation.other.declarations.typeParameterConstraints.typeParameterOnContainer +package tests.validation.other.declarations.constraints.typeParameterConstraints.typeParameterOnContainer annotation MyAnnotation where { // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." diff --git a/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage/main.sdstest new file mode 100644 index 000000000..f5593451e --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage/main.sdstest @@ -0,0 +1,29 @@ +package tests.validation.other.declarations.typeParameters.usage + +// $TEST$ no error "Type parameters cannot be used in nested named type declarations." +class MyClass(p: »T«) { + // $TEST$ no error "Type parameters cannot be used in nested named type declarations." + attr a: »T« + + // $TEST$ no error "Type parameters cannot be used in nested named type declarations." + // $TEST$ no error "Type parameters cannot be used in nested named type declarations." + // $TEST$ no error "Type parameters cannot be used in nested named type declarations." + // $TEST$ no error "Type parameters cannot be used in nested named type declarations." + fun f(p1: »T«, p2: »S«) -> (r1: »T«, r2: »S«) + + // $TEST$ error "Type parameters cannot be used in nested named type declarations." + // $TEST$ no error "Type parameters cannot be used in nested named type declarations." + class MyInnerClass(p1: »T«, p2: »S«) { + // $TEST$ error "Type parameters cannot be used in nested named type declarations." + attr a: »T« + + // $TEST$ error "Type parameters cannot be used in nested named type declarations." + fun f(p: »T«) + } + + enum MyInnerEnum { + // $TEST$ error "Type parameters cannot be used in nested named type declarations." + // $TEST$ no error "Type parameters cannot be used in nested named type declarations." + MyEnumVariant(p1: »T«, p2: »S«) + } +}