From 47ce782857f8f8c0cd53e933cb9d826163476fba Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Thu, 21 Sep 2023 18:02:14 +0200 Subject: [PATCH] feat: unique names withing declarations (#575) Closes partially #543 ### Summary of Changes Ensure that names are unique within declarations. I'll port the checks for uniqueness on the file level in a future PR. --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> --- src/language/ast/checks.ts | 6 + src/language/ast/shortcuts.ts | 96 ++++++++++ src/language/formatting/safe-ds-formatter.ts | 14 +- src/language/grammar/safe-ds.langium | 12 +- src/language/helpers/astShortcuts.ts | 38 ---- .../scoping/safe-ds-scope-provider.ts | 2 +- src/language/validation/imports.ts | 2 +- src/language/validation/names.ts | 178 +++++++++++++++++- src/language/validation/safe-ds-validator.ts | 29 ++- .../duplicates/in annotation/main.sdstest | 10 + .../duplicates/in block lambda/main.sdstest | 52 +++++ .../duplicates/in callable type/main.sdstest | 23 +++ .../names/duplicates/in class/main.sdstest | 126 +++++++++++++ .../duplicates/in enum variant/main.sdstest | 23 +++ .../names/duplicates/in enum/main.sdstest | 10 + .../in expression lambda/main.sdstest | 12 ++ .../names/duplicates/in function/main.sdstest | 36 ++++ .../names/duplicates/in pipeline/main.sdstest | 10 + .../names/duplicates/in segment/main.sdstest | 45 +++++ 19 files changed, 670 insertions(+), 54 deletions(-) create mode 100644 src/language/ast/checks.ts create mode 100644 src/language/ast/shortcuts.ts delete mode 100644 src/language/helpers/astShortcuts.ts create mode 100644 tests/resources/validation/names/duplicates/in annotation/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in block lambda/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in callable type/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in class/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in enum variant/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in enum/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in expression lambda/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in function/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in pipeline/main.sdstest create mode 100644 tests/resources/validation/names/duplicates/in segment/main.sdstest diff --git a/src/language/ast/checks.ts b/src/language/ast/checks.ts new file mode 100644 index 000000000..53aeb3e79 --- /dev/null +++ b/src/language/ast/checks.ts @@ -0,0 +1,6 @@ +import { SdsImport } from '../generated/ast.js'; + +export const isWildcardImport = function (node: SdsImport): boolean { + const importedNamespace = node.importedNamespace ?? ''; + return importedNamespace.endsWith('*'); +}; diff --git a/src/language/ast/shortcuts.ts b/src/language/ast/shortcuts.ts new file mode 100644 index 000000000..840ac6fcc --- /dev/null +++ b/src/language/ast/shortcuts.ts @@ -0,0 +1,96 @@ +import { + isSdsAssignment, + isSdsBlockLambdaResult, + isSdsDeclaration, + isSdsPlaceholder, + SdsAnnotatedObject, + SdsAnnotationCall, + SdsAssignee, + SdsAssignment, + SdsBlock, + SdsBlockLambda, + SdsBlockLambdaResult, + SdsClass, + SdsClassMember, + SdsEnum, + SdsEnumVariant, + SdsLiteral, + SdsLiteralType, + SdsParameter, + SdsParameterList, + SdsPlaceholder, + SdsResult, + SdsResultList, + SdsStatement, + SdsTypeArgument, + SdsTypeArgumentList, + SdsTypeParameter, + SdsTypeParameterList, +} from '../generated/ast.js'; +import { stream } from 'langium'; + +export const annotationCallsOrEmpty = function (node: SdsAnnotatedObject | undefined): SdsAnnotationCall[] { + if (!node) { + /* c8 ignore next 2 */ + return []; + } + + if (isSdsDeclaration(node)) { + return node?.annotationCallList?.annotationCalls ?? node?.annotationCalls ?? []; + } else { + /* c8 ignore next 2 */ + return node?.annotationCalls ?? []; + } +}; + +export const assigneesOrEmpty = function (node: SdsAssignment | undefined): SdsAssignee[] { + return node?.assigneeList?.assignees ?? []; +}; + +export const blockLambdaResultsOrEmpty = function (node: SdsBlockLambda | undefined): SdsBlockLambdaResult[] { + return stream(statementsOrEmpty(node?.body)) + .filter(isSdsAssignment) + .flatMap(assigneesOrEmpty) + .filter(isSdsBlockLambdaResult) + .toArray(); +}; + +export const literalsOrEmpty = function (node: SdsLiteralType | undefined): SdsLiteral[] { + return node?.literalList?.literals ?? []; +}; + +export const classMembersOrEmpty = function (node: SdsClass | undefined): SdsClassMember[] { + return node?.body?.members ?? []; +}; + +export const parametersOrEmpty = function (node: SdsParameterList | undefined): SdsParameter[] { + return node?.parameters ?? []; +}; + +export const placeholdersOrEmpty = function (node: SdsBlock | undefined): SdsPlaceholder[] { + return stream(statementsOrEmpty(node)) + .filter(isSdsAssignment) + .flatMap(assigneesOrEmpty) + .filter(isSdsPlaceholder) + .toArray(); +}; + +export const resultsOrEmpty = function (node: SdsResultList | undefined): SdsResult[] { + return node?.results ?? []; +}; + +export const statementsOrEmpty = function (node: SdsBlock | undefined): SdsStatement[] { + return node?.statements ?? []; +}; + +export const typeArgumentsOrEmpty = function (node: SdsTypeArgumentList | undefined): SdsTypeArgument[] { + return node?.typeArguments ?? []; +}; + +export const typeParametersOrEmpty = function (node: SdsTypeParameterList | undefined): SdsTypeParameter[] { + return node?.typeParameters ?? []; +}; + +export const variantsOrEmpty = function (node: SdsEnum | undefined): SdsEnumVariant[] { + return node?.body?.variants ?? []; +}; diff --git a/src/language/formatting/safe-ds-formatter.ts b/src/language/formatting/safe-ds-formatter.ts index 424c91ef7..15400bcdf 100644 --- a/src/language/formatting/safe-ds-formatter.ts +++ b/src/language/formatting/safe-ds-formatter.ts @@ -1,7 +1,16 @@ -import { AbstractFormatter, AstNode, CstNode, findCommentNode, Formatting, isAstNode, FormattingAction, FormattingActionOptions } from 'langium'; +import { + AbstractFormatter, + AstNode, + CstNode, + findCommentNode, + Formatting, + isAstNode, + FormattingAction, + FormattingActionOptions, +} from 'langium'; import * as ast from '../generated/ast.js'; import { SdsImport, SdsImportAlias, SdsModule } from '../generated/ast.js'; -import { annotationCallsOrEmpty, literalsOrEmpty, typeArgumentsOrEmpty } from '../helpers/astShortcuts.js'; +import { annotationCallsOrEmpty, literalsOrEmpty, typeArgumentsOrEmpty } from '../ast/shortcuts.js'; import noSpace = Formatting.noSpace; import newLine = Formatting.newLine; import newLines = Formatting.newLines; @@ -876,6 +885,7 @@ export class SafeDsFormatter extends AbstractFormatter { } else if (ast.isSdsUnionType(node)) { return typeArgumentsOrEmpty(node.typeArgumentList).length > 0; } else { + /* c8 ignore next 2 */ return false; } } diff --git a/src/language/grammar/safe-ds.langium b/src/language/grammar/safe-ds.langium index 46fa0dc52..400522119 100644 --- a/src/language/grammar/safe-ds.langium +++ b/src/language/grammar/safe-ds.langium @@ -81,7 +81,7 @@ QualifiedName returns string: interface SdsModuleMember extends SdsDeclaration {} -SdsAnnotatedModuleMember returns SdsAnnotatedObject: +SdsAnnotatedModuleMember returns SdsModuleMember: {SdsAnnotationCallList} annotationCalls+=SdsAnnotationCall+ ( @@ -108,7 +108,7 @@ SdsAnnotatedModuleMember returns SdsAnnotatedObject: ) ; -SdsUnannotatedModuleMember returns SdsAnnotatedObject: +SdsUnannotatedModuleMember returns SdsModuleMember: {SdsAnnotation} SdsAnnotationFragment @@ -170,7 +170,7 @@ SdsParentTypeList returns SdsParentTypeList: ; interface SdsClassBody extends SdsObject { - members: SdsAnnotatedObject[] + members: SdsClassMember[] } SdsClassBody returns SdsClassBody: @@ -179,12 +179,12 @@ SdsClassBody returns SdsClassBody: interface SdsClassMember extends SdsDeclaration {} -SdsClassMember returns SdsAnnotatedObject: +SdsClassMember returns SdsClassMember: SdsAnnotatedClassMember | SdsUnannotatedClassMember ; -SdsAnnotatedClassMember returns SdsAnnotatedObject: +SdsAnnotatedClassMember returns SdsClassMember: {SdsAnnotationCallList} annotationCalls+=SdsAnnotationCall+ ( @@ -203,7 +203,7 @@ SdsAnnotatedClassMember returns SdsAnnotatedObject: ) ; -SdsUnannotatedClassMember returns SdsAnnotatedObject: +SdsUnannotatedClassMember returns SdsClassMember: {SdsAttribute} SdsAttributeFragment diff --git a/src/language/helpers/astShortcuts.ts b/src/language/helpers/astShortcuts.ts deleted file mode 100644 index fe119d3c4..000000000 --- a/src/language/helpers/astShortcuts.ts +++ /dev/null @@ -1,38 +0,0 @@ -// SdsAbstractDeclaration -------------------------------------------------------------------------- -import { - isSdsDeclaration, - SdsAnnotatedObject, - SdsAnnotationCall, - SdsImport, - SdsLiteral, - SdsLiteralType, - SdsResult, - SdsResultList, - SdsTypeArgument, - SdsTypeArgumentList, -} from '../generated/ast.js'; - -export const annotationCallsOrEmpty = function (node: SdsAnnotatedObject): SdsAnnotationCall[] { - if (isSdsDeclaration(node)) { - return node?.annotationCallList?.annotationCalls ?? node?.annotationCalls ?? []; - } else { - return node?.annotationCalls ?? []; - } -}; - -export const literalsOrEmpty = function (node: SdsLiteralType | undefined): SdsLiteral[] { - return node?.literalList?.literals ?? []; -}; - -export const resultsOrEmpty = function (node: SdsResultList | undefined): SdsResult[] { - return node?.results ?? []; -}; - -export const typeArgumentsOrEmpty = function (node: SdsTypeArgumentList | undefined): SdsTypeArgument[] { - return node?.typeArguments ?? []; -}; - -export const isWildcardImport = function (node: SdsImport): boolean { - const importedNamespace = node.importedNamespace ?? ''; - return importedNamespace.endsWith('*'); -}; diff --git a/src/language/scoping/safe-ds-scope-provider.ts b/src/language/scoping/safe-ds-scope-provider.ts index 96a99c6ea..418c95895 100644 --- a/src/language/scoping/safe-ds-scope-provider.ts +++ b/src/language/scoping/safe-ds-scope-provider.ts @@ -12,7 +12,7 @@ import { SdsType, SdsYield, } from '../generated/ast.js'; -import { resultsOrEmpty } from '../helpers/astShortcuts.js'; +import { resultsOrEmpty } from '../ast/shortcuts.js'; export class SafeDsScopeProvider extends DefaultScopeProvider { override getScope(context: ReferenceInfo): Scope { diff --git a/src/language/validation/imports.ts b/src/language/validation/imports.ts index f96ab2792..9cf781d81 100644 --- a/src/language/validation/imports.ts +++ b/src/language/validation/imports.ts @@ -1,6 +1,6 @@ import { ValidationAcceptor } from 'langium'; import { SdsImportAlias } from '../generated/ast.js'; -import { isWildcardImport } from '../helpers/astShortcuts.js'; +import { isWildcardImport } from '../ast/checks.js'; export const CODE_IMPORT_WILDCARD_IMPORT_WITH_ALIAS = 'import/wildcard-import-with-alias'; diff --git a/src/language/validation/names.ts b/src/language/validation/names.ts index 0084a3fff..1ac0f4205 100644 --- a/src/language/validation/names.ts +++ b/src/language/validation/names.ts @@ -1,8 +1,34 @@ -import { SdsDeclaration } from '../generated/ast.js'; +import { + SdsAnnotation, + SdsBlockLambda, + SdsCallableType, + SdsClass, + SdsDeclaration, + SdsEnum, + SdsEnumVariant, + SdsExpressionLambda, + SdsFunction, + SdsPipeline, + SdsSegment, +} from '../generated/ast.js'; import { ValidationAcceptor } from 'langium'; +import { + blockLambdaResultsOrEmpty, + classMembersOrEmpty, + parametersOrEmpty, + placeholdersOrEmpty, + resultsOrEmpty, + typeParametersOrEmpty, + variantsOrEmpty, +} from '../ast/shortcuts.js'; export const CODE_NAME_BLOCK_LAMBDA_PREFIX = 'name/block-lambda-prefix'; export const CODE_NAME_CASING = 'name/casing'; +export const CODE_NAME_DUPLICATE = 'name/duplicate'; + +// ----------------------------------------------------------------------------- +// Block lambda prefix +// ----------------------------------------------------------------------------- export const nameMustNotStartWithBlockLambdaPrefix = (node: SdsDeclaration, accept: ValidationAcceptor) => { const name = node.name ?? ''; @@ -20,6 +46,10 @@ export const nameMustNotStartWithBlockLambdaPrefix = (node: SdsDeclaration, acce } }; +// ----------------------------------------------------------------------------- +// Casing +// ----------------------------------------------------------------------------- + export const nameShouldHaveCorrectCasing = (node: SdsDeclaration, accept: ValidationAcceptor): void => { switch (node.$type) { case 'SdsAnnotation': @@ -62,6 +92,7 @@ export const nameShouldHaveCorrectCasing = (node: SdsDeclaration, accept: Valida case 'SdsTypeParameter': return nameShouldBeUpperCamelCase(node, 'type parameters', accept); } + /* c8 ignore next */ }; const nameShouldBeLowerCamelCase = (node: SdsDeclaration, nodeName: string, accept: ValidationAcceptor): void => { @@ -98,3 +129,148 @@ const acceptCasingWarning = ( code: CODE_NAME_CASING, }); }; + +// ----------------------------------------------------------------------------- +// Uniqueness +// ----------------------------------------------------------------------------- + +export const annotationMustContainUniqueNames = (node: SdsAnnotation, accept: ValidationAcceptor): void => { + namesMustBeUnique( + parametersOrEmpty(node.parameterList), + (name) => `A parameter with name '${name}' exists already.`, + accept, + ); +}; + +export const blockLambdaMustContainUniqueNames = (node: SdsBlockLambda, accept: ValidationAcceptor): void => { + const parametersAndPlaceholders = [...parametersOrEmpty(node.parameterList), ...placeholdersOrEmpty(node.body)]; + namesMustBeUnique( + parametersAndPlaceholders, + (name) => `A parameter or placeholder with name '${name}' exists already.`, + accept, + ); + + namesMustBeUnique( + blockLambdaResultsOrEmpty(node), + (name) => `A result with name '${name}' exists already.`, + accept, + ); +}; + +export const callableTypeMustContainUniqueNames = (node: SdsCallableType, accept: ValidationAcceptor): void => { + namesMustBeUnique( + parametersOrEmpty(node.parameterList), + (name) => `A parameter with name '${name}' exists already.`, + accept, + ); + namesMustBeUnique( + resultsOrEmpty(node.resultList), + (name) => `A result with name '${name}' exists already.`, + accept, + ); +}; + +export const classMustContainUniqueNames = (node: SdsClass, accept: ValidationAcceptor): void => { + const typeParametersAndParameters = [ + ...typeParametersOrEmpty(node.typeParameterList), + ...parametersOrEmpty(node.parameterList), + ]; + namesMustBeUnique( + typeParametersAndParameters, + (name) => `A type parameter or parameter with name '${name}' exists already.`, + accept, + ); + + namesMustBeUnique(classMembersOrEmpty(node), (name) => `A member with name '${name}' exists already.`, accept); +}; + +export const enumMustContainUniqueNames = (node: SdsEnum, accept: ValidationAcceptor): void => { + namesMustBeUnique(variantsOrEmpty(node), (name) => `A variant with name '${name}' exists already.`, accept); +}; + +export const enumVariantMustContainUniqueNames = (node: SdsEnumVariant, accept: ValidationAcceptor): void => { + const typeParametersAndParameters = [ + ...typeParametersOrEmpty(node.typeParameterList), + ...parametersOrEmpty(node.parameterList), + ]; + namesMustBeUnique( + typeParametersAndParameters, + (name) => `A type parameter or parameter with name '${name}' exists already.`, + accept, + ); +}; + +export const expressionLambdaMustContainUniqueNames = (node: SdsExpressionLambda, accept: ValidationAcceptor): void => { + namesMustBeUnique( + parametersOrEmpty(node.parameterList), + (name) => `A parameter with name '${name}' exists already.`, + accept, + ); +}; + +export const functionMustContainUniqueNames = (node: SdsFunction, accept: ValidationAcceptor): void => { + const typeParametersAndParameters = [ + ...typeParametersOrEmpty(node.typeParameterList), + ...parametersOrEmpty(node.parameterList), + ]; + namesMustBeUnique( + typeParametersAndParameters, + (name) => `A type parameter or parameter with name '${name}' exists already.`, + accept, + ); + + namesMustBeUnique( + resultsOrEmpty(node.resultList), + (name) => `A result with name '${name}' exists already.`, + accept, + ); +}; + +export const pipelineMustContainUniqueNames = (node: SdsPipeline, accept: ValidationAcceptor): void => { + namesMustBeUnique( + placeholdersOrEmpty(node.body), + (name) => `A placeholder with name '${name}' exists already.`, + accept, + ); +}; + +export const segmentMustContainUniqueNames = (node: SdsSegment, accept: ValidationAcceptor): void => { + const parametersAndPlaceholder = [...parametersOrEmpty(node.parameterList), ...placeholdersOrEmpty(node.body)]; + namesMustBeUnique( + parametersAndPlaceholder, + (name) => `A parameter or placeholder with name '${name}' exists already.`, + accept, + ); + + namesMustBeUnique( + resultsOrEmpty(node.resultList), + (name) => `A result with name '${name}' exists already.`, + accept, + ); +}; + +const namesMustBeUnique = ( + nodes: Iterable, + createMessage: (name: string) => string, + accept: ValidationAcceptor, +): void => { + const knownNames = new Set(); + + for (const node of nodes) { + const name = node.name; + if (!name) { + /* c8 ignore next 2 */ + continue; + } + + if (knownNames.has(name)) { + accept('error', createMessage(name), { + node, + property: 'name', + code: CODE_NAME_DUPLICATE, + }); + } else { + knownNames.add(name); + } + } +}; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index ce7d9016c..80e417303 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -1,7 +1,20 @@ import { ValidationChecks } from 'langium'; import { SafeDsAstType } from '../generated/ast.js'; import type { SafeDsServices } from '../safe-ds-module.js'; -import { nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing } from './names.js'; +import { + annotationMustContainUniqueNames, + blockLambdaMustContainUniqueNames, + callableTypeMustContainUniqueNames, + classMustContainUniqueNames, + enumMustContainUniqueNames, + enumVariantMustContainUniqueNames, + expressionLambdaMustContainUniqueNames, + functionMustContainUniqueNames, + nameMustNotStartWithBlockLambdaPrefix, + nameShouldHaveCorrectCasing, + pipelineMustContainUniqueNames, + segmentMustContainUniqueNames, +} from './names.js'; import { annotationParameterListShouldNotBeEmpty, assignmentShouldHaveMoreThanWildcardsAsAssignees, @@ -34,14 +47,19 @@ export const registerValidationChecks = function (services: SafeDsServices) { const validator = services.validation.SafeDsValidator; const checks: ValidationChecks = { SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees], - SdsAnnotation: [annotationParameterListShouldNotBeEmpty], + SdsAnnotation: [annotationMustContainUniqueNames, annotationParameterListShouldNotBeEmpty], SdsAttribute: [attributeMustHaveTypeHint], + SdsBlockLambda: [blockLambdaMustContainUniqueNames], + SdsCallableType: [callableTypeMustContainUniqueNames], + SdsClass: [classMustContainUniqueNames], SdsClassBody: [classBodyShouldNotBeEmpty], SdsConstraintList: [constraintListShouldNotBeEmpty], SdsDeclaration: [nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing], + SdsEnum: [enumMustContainUniqueNames], SdsEnumBody: [enumBodyShouldNotBeEmpty], - SdsEnumVariant: [enumVariantParameterListShouldNotBeEmpty], - SdsFunction: [functionResultListShouldNotBeEmpty], + SdsEnumVariant: [enumVariantMustContainUniqueNames, enumVariantParameterListShouldNotBeEmpty], + SdsExpressionLambda: [expressionLambdaMustContainUniqueNames], + SdsFunction: [functionMustContainUniqueNames, functionResultListShouldNotBeEmpty], SdsImportAlias: [importAliasMustNotBeUsedForWildcardImports], SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage], SdsParameter: [parameterMustHaveTypeHint], @@ -50,8 +68,9 @@ export const registerValidationChecks = function (services: SafeDsServices) { parameterListMustNotHaveRequiredParametersAfterOptionalParameters, parameterListVariadicParameterMustBeLast, ], + SdsPipeline: [pipelineMustContainUniqueNames], SdsResult: [resultMustHaveTypeHint], - SdsSegment: [segmentResultListShouldNotBeEmpty], + SdsSegment: [segmentMustContainUniqueNames, segmentResultListShouldNotBeEmpty], SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts], SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter], SdsTypeParameterList: [typeParameterListShouldNotBeEmpty], diff --git a/tests/resources/validation/names/duplicates/in annotation/main.sdstest b/tests/resources/validation/names/duplicates/in annotation/main.sdstest new file mode 100644 index 000000000..425a5f85c --- /dev/null +++ b/tests/resources/validation/names/duplicates/in annotation/main.sdstest @@ -0,0 +1,10 @@ +package tests.validation.names.duplicates.inAnnotation + +annotation A( + // $TEST$ no error r"A parameter with name '\w*' exists already\." + »duplicateParameter«: Int, + // $TEST$ error "A parameter with name 'duplicateParameter' exists already." + »duplicateParameter«: Int, + // $TEST$ no error r"A parameter with name '\w*' exists already\." + »uniqueParameter«: Int +) diff --git a/tests/resources/validation/names/duplicates/in block lambda/main.sdstest b/tests/resources/validation/names/duplicates/in block lambda/main.sdstest new file mode 100644 index 000000000..5a2cbd98e --- /dev/null +++ b/tests/resources/validation/names/duplicates/in block lambda/main.sdstest @@ -0,0 +1,52 @@ +package tests.validation.names.duplicates.inBlockLambda + +pipeline p { + ( + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »duplicateParameter«, + // $TEST$ error "A parameter or placeholder with name 'duplicateParameter' exists already." + »duplicateParameter«, + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »uniqueParameter«, + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »parameterAndPlaceholder«, + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »parameterAndResult« + ) { + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »duplicatePlaceholder« = 1; + // $TEST$ error "A parameter or placeholder with name 'duplicatePlaceholder' exists already." + val »duplicatePlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »uniquePlaceholder« = 1; + // $TEST$ error "A parameter or placeholder with name 'parameterAndPlaceholder' exists already." + val »parameterAndPlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »placeholderAndResult« = 1; + + // $TEST$ no error r"A result with name '\w*' exists already\." + yield »duplicateResult« = 0; + // $TEST$ error "A result with name 'duplicateResult' exists already." + yield »duplicateResult« = 0; + // $TEST$ no error r"A result with name '\w*' exists already\." + yield »uniqueResult« = 0; + // $TEST$ no error r"A result with name '\w*' exists already\." + yield »parameterAndResult« = 0; + //$TEST$ no error r"A result with name '\w*' exists already\." + yield »placeholderAndResult« = 0; + //$TEST$ no error r"A result with name '\w*' exists already\." + yield »resultAndPlaceholder« = 0; + + // $TEST$ no error "A parameter or placeholder with name '\w*' exists already\." + val »resultAndPlaceholder« = 1; + + () { + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »duplicatePlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »parameterAndPlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »placeholderAndResult« = 1; + }; + }; +} diff --git a/tests/resources/validation/names/duplicates/in callable type/main.sdstest b/tests/resources/validation/names/duplicates/in callable type/main.sdstest new file mode 100644 index 000000000..9320563e7 --- /dev/null +++ b/tests/resources/validation/names/duplicates/in callable type/main.sdstest @@ -0,0 +1,23 @@ +package tests.validation.names.duplicates.inCallableType + +segment s( + f: ( + // $TEST$ no error r"A parameter with name '\w*' exists already\." + »duplicateParameter«: Int, + // $TEST$ error "A parameter with name 'duplicateParameter' exists already." + »duplicateParameter«: Int, + // $TEST$ no error r"A parameter with name '\w*' exists already\." + »uniqueParameter«: Int, + // $TEST$ no error r"A parameter with name '\w*' exists already\." + »parameterAndResult«: Int + ) -> ( + // $TEST$ no error r"A result with name '\w*' exists already\." + »duplicateResult«: Int, + // $TEST$ error "A result with name 'duplicateResult' exists already." + »duplicateResult«: Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »uniqueResult«: Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »parameterAndResult«: Int + ) +) {} diff --git a/tests/resources/validation/names/duplicates/in class/main.sdstest b/tests/resources/validation/names/duplicates/in class/main.sdstest new file mode 100644 index 000000000..ba178f8db --- /dev/null +++ b/tests/resources/validation/names/duplicates/in class/main.sdstest @@ -0,0 +1,126 @@ +package tests.validation.names.duplicates.inClass + +class MyClass1< + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »DuplicateTypeParameter«, + // $TEST$ error "A type parameter or parameter with name 'DuplicateTypeParameter' exists already." + »DuplicateTypeParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »UniqueTypeParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndMember«, +>( + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »duplicateParameter«: Int, + // $TEST$ error "A type parameter or parameter with name 'duplicateParameter' exists already." + »duplicateParameter«: Int, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »uniqueParameter«: Int, + // $TEST$ error "A type parameter or parameter with name 'TypeParameterAndParameter' exists already." + »TypeParameterAndParameter«: Int, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »parameterAndMember«: Int, +) { + // $TEST$ no error r"A member with name '\w*' exists already\." + attr »duplicateAttribute«: Int + // $TEST$ error "A member with name 'duplicateAttribute' exists already." + attr »duplicateAttribute«: Int + // $TEST$ no error r"A member with name '\w*' exists already\." + attr »uniqueAttribute«: Int + + // $TEST$ no error r"A member with name '\w*' exists already\." + class »DuplicateClass« + // $TEST$ error "A member with name 'DuplicateClass' exists already." + class »DuplicateClass« + // $TEST$ no error r"A member with name '\w*' exists already\." + class »UniqueClass« + + // $TEST$ no error r"A member with name '\w*' exists already\." + enum »DuplicateEnum« + // $TEST$ error "A member with name 'DuplicateEnum' exists already." + enum »DuplicateEnum« + // $TEST$ no error r"A member with name '\w*' exists already\." + enum »UniqueEnum« + + // $TEST$ no error r"A member with name '\w*' exists already\." + fun »duplicateFun«() + // $TEST$ error "A member with name 'duplicateFun' exists already." + fun »duplicateFun«() + // $TEST$ no error r"A member with name '\w*' exists already\." + fun »uniqueFun«() + + // $TEST$ no error r"A member with name '\w*' exists already\." + attr »duplicateMember«: Int + // $TEST$ error "A member with name 'duplicateMember' exists already." + class »duplicateMember« + // $TEST$ error "A member with name 'duplicateMember' exists already." + enum »duplicateMember« + // $TEST$ error "A member with name 'duplicateMember' exists already." + fun »duplicateMember«() +} + +class MyClass2< + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndMember«, +> { + // $TEST$ no error r"A member with name '\w*' exists already\." + attr »TypeParameterAndMember«: Int +} + +class MyClass3< + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndMember«, +> { + // $TEST$ no error r"A member with name '\w*' exists already\." + class »TypeParameterAndMember« +} + +class MyClass4< + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndMember«, +> { + // $TEST$ no error r"A member with name '\w*' exists already\." + enum »TypeParameterAndMember« +} + +class MyClass5< + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndMember«, +> { + // $TEST$ no error r"A member with name '\w*' exists already\." + fun »TypeParameterAndMember«() +} + +class MyClass6( + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »parameterAndMember«: Int, +) { + // $TEST$ no error r"A member with name '\w*' exists already\." + attr »parameterAndMember«: Int +} + +class MyClass7( + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »parameterAndMember«: Int, +) { + // $TEST$ no error r"A member with name '\w*' exists already\." + class »parameterAndMember« +} + +class MyClass8( + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »parameterAndMember«: Int, +) { + // $TEST$ no error r"A member with name '\w*' exists already\." + enum »parameterAndMember« +} + +class MyClass9( + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »parameterAndMember«: Int, +) { + // $TEST$ no error r"A member with name '\w*' exists already\." + fun »parameterAndMember«() +} diff --git a/tests/resources/validation/names/duplicates/in enum variant/main.sdstest b/tests/resources/validation/names/duplicates/in enum variant/main.sdstest new file mode 100644 index 000000000..75e306484 --- /dev/null +++ b/tests/resources/validation/names/duplicates/in enum variant/main.sdstest @@ -0,0 +1,23 @@ +package tests.validation.names.duplicates.inEnumVariant + +enum MyEnum { + MyEnumVariant< + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »DuplicateTypeParameter«, + // $TEST$ error "A type parameter or parameter with name 'DuplicateTypeParameter' exists already." + »DuplicateTypeParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »UniqueTypeParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndParameter«, + >( + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »duplicateParameter«: Int, + // $TEST$ error "A type parameter or parameter with name 'duplicateParameter' exists already." + »duplicateParameter«: Int, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »uniqueParameter«: Int, + // $TEST$ error "A type parameter or parameter with name 'TypeParameterAndParameter' exists already." + »TypeParameterAndParameter«: Int, + ) +} diff --git a/tests/resources/validation/names/duplicates/in enum/main.sdstest b/tests/resources/validation/names/duplicates/in enum/main.sdstest new file mode 100644 index 000000000..08903cba2 --- /dev/null +++ b/tests/resources/validation/names/duplicates/in enum/main.sdstest @@ -0,0 +1,10 @@ +package tests.validation.names.duplicates.inEnum + +enum MyEnum { + // $TEST$ no error r"A variant with name '\w*' exists already\." + »MyVariant1« + // $TEST$ error "A variant with name 'MyVariant1' exists already." + »MyVariant1« + // $TEST$ no error r"A variant with name '\w*' exists already\." + »MyVariant2« +} diff --git a/tests/resources/validation/names/duplicates/in expression lambda/main.sdstest b/tests/resources/validation/names/duplicates/in expression lambda/main.sdstest new file mode 100644 index 000000000..71f4bb006 --- /dev/null +++ b/tests/resources/validation/names/duplicates/in expression lambda/main.sdstest @@ -0,0 +1,12 @@ +package tests.validation.names.duplicates.inExpressionLambda + +pipeline p { + ( + // $TEST$ no error r"A parameter with name '\w*' exists already\." + »duplicateParameter«, + // $TEST$ error "A parameter with name 'duplicateParameter' exists already." + »duplicateParameter«, + // $TEST$ no error r"A parameter with name '\w*' exists already\." + »uniqueParameter«, + ) -> 1; +} diff --git a/tests/resources/validation/names/duplicates/in function/main.sdstest b/tests/resources/validation/names/duplicates/in function/main.sdstest new file mode 100644 index 000000000..802d3412d --- /dev/null +++ b/tests/resources/validation/names/duplicates/in function/main.sdstest @@ -0,0 +1,36 @@ +package tests.validation.names.duplicates.inFunction + +fun myFunction< + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »DuplicateTypeParameter«, + // $TEST$ error "A type parameter or parameter with name 'DuplicateTypeParameter' exists already." + »DuplicateTypeParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »UniqueTypeParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndParameter«, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »TypeParameterAndResult«, +>( + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »duplicateParameter«: Int, + // $TEST$ error "A type parameter or parameter with name 'duplicateParameter' exists already." + »duplicateParameter«: Int, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »uniqueParameter«: Int, + // $TEST$ error "A type parameter or parameter with name 'TypeParameterAndParameter' exists already." + »TypeParameterAndParameter«: Int, + // $TEST$ no error r"A type parameter or parameter with name '\w*' exists already\." + »parameterAndResult«: Int, +) -> ( + // $TEST$ no error r"A result with name '\w*' exists already\." + »duplicateResult« : Int, + // $TEST$ error "A result with name 'duplicateResult' exists already." + »duplicateResult« : Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »uniqueResult« : Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »parameterAndResult« : Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »TypeParameterAndResult« : Int, +) diff --git a/tests/resources/validation/names/duplicates/in pipeline/main.sdstest b/tests/resources/validation/names/duplicates/in pipeline/main.sdstest new file mode 100644 index 000000000..f53765c2b --- /dev/null +++ b/tests/resources/validation/names/duplicates/in pipeline/main.sdstest @@ -0,0 +1,10 @@ +package tests.validation.names.duplicates.inPipeline + +pipeline p { + // $TEST$ no error r"A placeholder with name '\w*' exists already\." + val »duplicatePlaceholder« = 1; + // $TEST$ error "A placeholder with name 'duplicatePlaceholder' exists already." + val »duplicatePlaceholder« = 1; + // $TEST$ no error r"A placeholder with name '\w*' exists already\." + val »uniquePlaceholder« = 1; +} diff --git a/tests/resources/validation/names/duplicates/in segment/main.sdstest b/tests/resources/validation/names/duplicates/in segment/main.sdstest new file mode 100644 index 000000000..a46b224c5 --- /dev/null +++ b/tests/resources/validation/names/duplicates/in segment/main.sdstest @@ -0,0 +1,45 @@ +package tests.validation.names.duplicates.inSegment + +segment mySegment( + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »duplicateParameter«: Int, + // $TEST$ error "A parameter or placeholder with name 'duplicateParameter' exists already." + »duplicateParameter«: Int, + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »uniqueParameter«: Int, + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »parameterAndPlaceholder«: Int, + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + »parameterAndResult«: Int, +) -> ( + // $TEST$ no error r"A result with name '\w*' exists already\." + »duplicateResult« : Int, + // $TEST$ error "A result with name 'duplicateResult' exists already." + »duplicateResult« : Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »uniqueResult« : Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »parameterAndResult« : Int, + // $TEST$ no error r"A result with name '\w*' exists already\." + »placeholderAndResult« : Int, +) { + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »duplicatePlaceholder« = 1; + // $TEST$ error "A parameter or placeholder with name 'duplicatePlaceholder' exists already." + val »duplicatePlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »uniquePlaceholder« = 1; + // $TEST$ error "A parameter or placeholder with name 'parameterAndPlaceholder' exists already." + val »parameterAndPlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »placeholderAndResult« = 1; + + () { + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »duplicatePlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »parameterAndPlaceholder« = 1; + // $TEST$ no error r"A parameter or placeholder with name '\w*' exists already\." + val »placeholderAndResult« = 1; + }; +}