From cba3abf75b1c9e0a4a845fea6f03c0390f0dfeff Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Thu, 21 Sep 2023 11:49:39 +0200 Subject: [PATCH] feat: error if left operand of type parameter constraint does not belong to declaration with constraint (#571) Closes #562 ### Summary of Changes Show an error if the left operand of a type parameter constraint refers to a type parameter that does not belong to the declaration with the constraint (but a declaration that contains it). --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> --- .../declarations/typeParameterConstraints.ts | 25 +++++++ src/language/validation/safe-ds-validator.ts | 2 + .../main.sdstest | 67 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 src/language/validation/other/declarations/typeParameterConstraints.ts create mode 100644 tests/resources/validation/other/declarations/type parameter constraints/left operand must be own type parameter/main.sdstest diff --git a/src/language/validation/other/declarations/typeParameterConstraints.ts b/src/language/validation/other/declarations/typeParameterConstraints.ts new file mode 100644 index 000000000..a6a837f1c --- /dev/null +++ b/src/language/validation/other/declarations/typeParameterConstraints.ts @@ -0,0 +1,25 @@ +import { isSdsDeclaration, SdsTypeParameterConstraint } from '../../../generated/ast.js'; +import { getContainerOfType, ValidationAcceptor } from 'langium'; + +export const CODE_TYPE_PARAMETER_CONSTRAINT_LEFT_OPERAND = 'type-parameter-constraint/left-operand'; + +export const typeParameterConstraintLeftOperandMustBeOwnTypeParameter = ( + node: SdsTypeParameterConstraint, + accept: ValidationAcceptor, +) => { + const typeParameter = node.leftOperand.ref; + if (!typeParameter) { + return; + } + + const declarationWithConstraint = getContainerOfType(node.$container, isSdsDeclaration); + const declarationWithTypeParameters = getContainerOfType(typeParameter.$container, isSdsDeclaration); + + if (declarationWithConstraint !== declarationWithTypeParameters) { + accept('error', 'The left operand must refer to a type parameter of the declaration with the constraint.', { + node, + property: 'leftOperand', + code: CODE_TYPE_PARAMETER_CONSTRAINT_LEFT_OPERAND, + }); + } +}; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index e4afd988b..c38d2d943 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -19,6 +19,7 @@ import { templateStringMustHaveExpressionBetweenTwoStringParts } from './other/e import { yieldMustNotBeUsedInPipeline } from './other/statements/assignments.js'; import { attributeMustHaveTypeHint, parameterMustHaveTypeHint, resultMustHaveTypeHint } from './types.js'; import { moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage } from './other/modules.js'; +import { typeParameterConstraintLeftOperandMustBeOwnTypeParameter } from './other/declarations/typeParameterConstraints.js'; /** * Register custom validation checks. @@ -40,6 +41,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsResult: [resultMustHaveTypeHint], SdsSegment: [segmentResultListShouldNotBeEmpty], SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts], + SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter], SdsUnionType: [unionTypeShouldNotHaveASingularTypeArgument], SdsYield: [yieldMustNotBeUsedInPipeline], }; diff --git a/tests/resources/validation/other/declarations/type parameter constraints/left operand must be own type parameter/main.sdstest b/tests/resources/validation/other/declarations/type parameter constraints/left operand must be own type parameter/main.sdstest new file mode 100644 index 000000000..d2fc27800 --- /dev/null +++ b/tests/resources/validation/other/declarations/type parameter constraints/left operand must be own type parameter/main.sdstest @@ -0,0 +1,67 @@ +package tests.validation.other.declarations.typeParameterConstraints.typeParameterOnContainer + +annotation MyAnnotation where { + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »Unresolved« sub MyGlobalClass, +} + +class MyGlobalClass where { + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »T1« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »Unresolved« sub MyGlobalClass +} { + class MyNestedClass where { + // $TEST$ error "The left operand must refer to a type parameter of the declaration with the constraint." + »T1« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »T2« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »Unresolved« sub MyGlobalClass, + } + + enum MyNestedEnum { + MyEnumVariant where { + // $TEST$ error "The left operand must refer to a type parameter of the declaration with the constraint." + »T1« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »T2« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »Unresolved« sub MyGlobalClass, + } + } + + fun myMethod() where { + // $TEST$ error "The left operand must refer to a type parameter of the declaration with the constraint." + »T1« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »T2« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »Unresolved« sub MyGlobalClass, + } +} + +enum MyGlobalEnum { + MyEnumVariant where { + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »T1« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »Unresolved« sub MyGlobalClass, + } +} + +fun myGlobalFunction() where { + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »T1« sub MyGlobalClass, + + // $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint." + »Unresolved« sub MyGlobalClass, +}