Skip to content

Commit

Permalink
feat: error if type parameter is set multiple times
Browse files Browse the repository at this point in the history
  • Loading branch information
lars-reimann committed Oct 11, 2023
1 parent 4e675f8 commit 0ccdeaa
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 23 deletions.
48 changes: 48 additions & 0 deletions src/language/validation/other/types/namedTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { SdsNamedType } from '../../../generated/ast.js';
import { ValidationAcceptor } from 'langium';
import { SafeDsServices } from '../../../safe-ds-module.js';
import { typeArgumentsOrEmpty } from '../../../helpers/nodeProperties.js';
import { duplicatesBy } from '../../../helpers/collectionUtils.js';

export const CODE_NAMED_TYPE_DUPLICATE_TYPE_PARAMETER = 'named-type/duplicate-type-parameter';
export const CODE_NAMED_TYPE_POSITIONAL_AFTER_NAMED = 'named-type/positional-after-named';

export const namedTypeMustNotSetTypeParameterMultipleTimes = (services: SafeDsServices) => {
const nodeMapper = services.helpers.NodeMapper;
const typeArgumentToTypeParameterOrUndefined = nodeMapper.typeArgumentToTypeParameterOrUndefined.bind(nodeMapper);

return (node: SdsNamedType, accept: ValidationAcceptor): void => {
const typeArguments = typeArgumentsOrEmpty(node.typeArgumentList);
const duplicates = duplicatesBy(typeArguments, typeArgumentToTypeParameterOrUndefined);

for (const duplicate of duplicates) {
const correspondingTypeParameter = typeArgumentToTypeParameterOrUndefined(duplicate)!;
accept('error', `The type parameter '${correspondingTypeParameter.name}' is already set.`, {
node: duplicate,
code: CODE_NAMED_TYPE_DUPLICATE_TYPE_PARAMETER,
});
}
};
};

export const namedTypeTypeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments = (
node: SdsNamedType,
accept: ValidationAcceptor,
): void => {
const typeArgumentList = node.typeArgumentList;
if (!typeArgumentList) {
return;
}

let foundNamed = false;
for (const typeArgument of typeArgumentList.typeArguments) {
if (typeArgument.typeParameter) {
foundNamed = true;
} else if (foundNamed) {
accept('error', 'After the first named type argument all type arguments must be named.', {
node: typeArgument,
code: CODE_NAMED_TYPE_POSITIONAL_AFTER_NAMED,
});
}
}
};
21 changes: 0 additions & 21 deletions src/language/validation/other/types/typeArgumentLists.ts

This file was deleted.

8 changes: 6 additions & 2 deletions src/language/validation/safe-ds-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import {
callableTypeMustNotHaveOptionalParameters,
callableTypeParameterMustNotHaveConstModifier,
} from './other/types/callableTypes.js';
import { typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/types/typeArgumentLists.js';
import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js';
import {
referenceMustNotBeFunctionPointer,
Expand Down Expand Up @@ -82,6 +81,10 @@ import {
import { memberAccessMustBeNullSafeIfReceiverIsNullable } from './other/expressions/memberAccesses.js';
import { importPackageMustExist, importPackageShouldNotBeEmpty } from './other/imports.js';
import { singleUseAnnotationsMustNotBeRepeated } from './builtins/repeatable.js';
import {
namedTypeMustNotSetTypeParameterMultipleTimes,
namedTypeTypeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments
} from "./other/types/namedTypes.js";

/**
* Register custom validation checks.
Expand Down Expand Up @@ -140,8 +143,10 @@ export const registerValidationChecks = function (services: SafeDsServices) {
SdsNamedType: [
namedTypeDeclarationShouldNotBeDeprecated(services),
namedTypeDeclarationShouldNotBeExperimental(services),
namedTypeMustNotSetTypeParameterMultipleTimes(services),
namedTypeMustSetAllTypeParameters(services),
namedTypeTypeArgumentListShouldBeNeeded,
namedTypeTypeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments,
],
SdsParameter: [
parameterMustHaveTypeHint,
Expand All @@ -165,7 +170,6 @@ export const registerValidationChecks = function (services: SafeDsServices) {
segmentResultListShouldNotBeEmpty,
],
SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts],
SdsTypeArgumentList: [typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments],
SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter],
SdsTypeParameterList: [typeParameterListShouldNotBeEmpty],
SdsUnionType: [unionTypeMustHaveTypeArguments, unionTypeShouldNotHaveASingularTypeArgument],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package tests.validation.other.types.typeArgumentLists.duplicateTypeParameters

class MyClass<A, B>

fun myFunction(
f: MyClass<
// $TEST$ no error r"The type parameter '\w+' is already set\."
»Int«,
// $TEST$ error "The type parameter 'A' is already set."
»A = Int«
>,
g: MyClass<
// $TEST$ no error r"The type parameter '\w+' is already set\."
»B = Int«,
// $TEST$ error "The type parameter 'B' is already set."
»B = Int«
>,
h: MyClass<
// $TEST$ no error r"The type parameter '\w+' is already set\."
»A = Int«,
// $TEST$ no error r"The type parameter '\w+' is already set\."
»B = Int«
>,
i: MyClass<
// $TEST$ no error r"The type parameter '\w+' is already set\."
»Int«,
// $TEST$ no error r"The type parameter '\w+' is already set\."
»Int«
>,
j: MyClass<
// $TEST$ no error r"The type parameter '\w+' is already set\."
»Unresolved = Int«,
// $TEST$ no error r"The type parameter '\w+' is already set\."
»Unresolved = Int«
>
)

0 comments on commit 0ccdeaa

Please sign in to comment.