diff --git a/esbuild.mjs b/esbuild.mjs index 1ab947425..6ef0c7de9 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -1,6 +1,7 @@ //@ts-check import * as esbuild from 'esbuild'; import { copy } from 'esbuild-plugin-copy'; +import fs from 'fs/promises'; const watch = process.argv.includes('--watch'); const minify = process.argv.includes('--minify'); @@ -10,20 +11,18 @@ const success = watch ? 'Watch build succeeded' : 'Build succeeded'; const getTime = function () { const date = new Date(); return `[${`${padZeroes(date.getHours())}:${padZeroes(date.getMinutes())}:${padZeroes(date.getSeconds())}`}] `; -} +}; const padZeroes = function (i) { return i.toString().padStart(2, '0'); -} +}; const plugins = [ { - name: 'watch-plugin', + name: 'clean-old-builtins', setup(build) { - build.onEnd(result => { - if (result.errors.length === 0) { - console.log(getTime() + success); - } + build.onStart(async () => { + await fs.rm('./out/resources', { force: true, recursive: true }); }); }, }, @@ -34,6 +33,16 @@ const plugins = [ }, watch, }), + { + name: 'watch-plugin', + setup(build) { + build.onEnd((result) => { + if (result.errors.length === 0) { + console.log(getTime() + success); + } + }); + }, + }, ]; const ctx = await esbuild.context({ @@ -41,19 +50,19 @@ const ctx = await esbuild.context({ entryPoints: ['src/cli/main.ts', 'src/extension/main.ts', 'src/language/main.ts'], outdir: 'out', bundle: true, - target: "ES2017", + target: 'ES2017', // VSCode's extension host is still using cjs, so we need to transform the code format: 'cjs', // To prevent confusing node, we explicitly use the `.cjs` extension outExtension: { - '.js': '.cjs' + '.js': '.cjs', }, - loader: {'.ts': 'ts'}, + loader: { '.ts': 'ts' }, external: ['vscode'], platform: 'node', sourcemap: !minify, minify, - plugins + plugins, }); if (watch) { diff --git a/src/language/helpers/nodeProperties.ts b/src/language/helpers/nodeProperties.ts index cb8a0717d..7e2dde176 100644 --- a/src/language/helpers/nodeProperties.ts +++ b/src/language/helpers/nodeProperties.ts @@ -1,5 +1,6 @@ import { isSdsAnnotation, + isSdsArgumentList, isSdsAssignment, isSdsAttribute, isSdsBlockLambda, @@ -23,6 +24,7 @@ import { SdsAnnotation, SdsAnnotationCall, SdsArgument, + SdsArgumentList, SdsAssignee, SdsAssignment, SdsBlock, @@ -161,12 +163,18 @@ export const findFirstAnnotationCallOf = ( }); }; -export const argumentsOrEmpty = (node: SdsAbstractCall | undefined): SdsArgument[] => { - return node?.argumentList?.arguments ?? []; +export const argumentsOrEmpty = (node: SdsArgumentList | SdsAbstractCall | undefined): SdsArgument[] => { + if (isSdsArgumentList(node)) { + return node.arguments; + } else { + return node?.argumentList?.arguments ?? []; + } }; + export const assigneesOrEmpty = (node: SdsAssignment | undefined): SdsAssignee[] => { return node?.assigneeList?.assignees ?? []; }; + export const blockLambdaResultsOrEmpty = (node: SdsBlockLambda | undefined): SdsBlockLambdaResult[] => { return stream(statementsOrEmpty(node?.body)) .filter(isSdsAssignment) diff --git a/src/language/validation/builtins/repeatable.ts b/src/language/validation/builtins/repeatable.ts index 6f76a6aad..14aef30b9 100644 --- a/src/language/validation/builtins/repeatable.ts +++ b/src/language/validation/builtins/repeatable.ts @@ -16,6 +16,7 @@ export const singleUseAnnotationsMustNotBeRepeated = for (const duplicate of duplicatesBy(callsOfSingleUseAnnotations, (it) => it.annotation?.ref)) { accept('error', `The annotation '${duplicate.annotation.$refText}' is not repeatable.`, { node: duplicate, + property: 'annotation', code: CODE_ANNOTATION_NOT_REPEATABLE, }); } diff --git a/src/language/validation/other/argumentLists.ts b/src/language/validation/other/argumentLists.ts index ccd50e5bb..6d7dc5dce 100644 --- a/src/language/validation/other/argumentLists.ts +++ b/src/language/validation/other/argumentLists.ts @@ -1,7 +1,15 @@ -import { SdsArgumentList } from '../../generated/ast.js'; -import { ValidationAcceptor } from 'langium'; +import { isSdsAnnotation, isSdsCall, SdsAbstractCall, SdsArgumentList } from '../../generated/ast.js'; +import { getContainerOfType, ValidationAcceptor } from 'langium'; +import { SafeDsServices } from '../../safe-ds-module.js'; +import { argumentsOrEmpty, isRequiredParameter, parametersOrEmpty } from '../../helpers/nodeProperties.js'; +import { duplicatesBy } from '../../helpers/collectionUtils.js'; +import { isEmpty } from 'radash'; +import { pluralize } from '../../helpers/stringUtils.js'; +export const CODE_ARGUMENT_LIST_DUPLICATE_PARAMETER = 'argument-list/duplicate-parameter'; +export const CODE_ARGUMENT_LIST_MISSING_REQUIRED_PARAMETER = 'argument-list/missing-required-parameter'; export const CODE_ARGUMENT_LIST_POSITIONAL_AFTER_NAMED = 'argument-list/positional-after-named'; +export const CODE_ARGUMENT_LIST_TOO_MANY_ARGUMENTS = 'argument-list/too-many-arguments'; export const argumentListMustNotHavePositionalArgumentsAfterNamedArguments = ( node: SdsArgumentList, @@ -19,3 +27,107 @@ export const argumentListMustNotHavePositionalArgumentsAfterNamedArguments = ( } } }; + +export const argumentListMustNotHaveTooManyArguments = (services: SafeDsServices) => { + const nodeMapper = services.helpers.NodeMapper; + + return (node: SdsAbstractCall, accept: ValidationAcceptor): void => { + const actualArgumentCount = argumentsOrEmpty(node).length; + + // We can never have too many arguments in this case + if (actualArgumentCount === 0) { + return; + } + + // We already report other errors in those cases + const callable = nodeMapper.callToCallableOrUndefined(node); + if (!callable || (isSdsCall(node) && isSdsAnnotation(callable))) { + return; + } + + const parameters = parametersOrEmpty(callable); + const maxArgumentCount = parameters.length; + + // All is good + if (actualArgumentCount <= maxArgumentCount) { + return; + } + + const minArgumentCount = parameters.filter((it) => isRequiredParameter(it)).length; + const kind = pluralize(Math.max(minArgumentCount, maxArgumentCount), 'argument'); + if (minArgumentCount === maxArgumentCount) { + accept('error', `Expected exactly ${minArgumentCount} ${kind} but got ${actualArgumentCount}.`, { + node, + property: 'argumentList', + code: CODE_ARGUMENT_LIST_TOO_MANY_ARGUMENTS, + }); + } else { + accept( + 'error', + `Expected between ${minArgumentCount} and ${maxArgumentCount} ${kind} but got ${actualArgumentCount}.`, + { + node, + property: 'argumentList', + code: CODE_ARGUMENT_LIST_TOO_MANY_ARGUMENTS, + }, + ); + } + }; +}; + +export const argumentListMustNotSetParameterMultipleTimes = (services: SafeDsServices) => { + const nodeMapper = services.helpers.NodeMapper; + const argumentToParameterOrUndefined = nodeMapper.argumentToParameterOrUndefined.bind(nodeMapper); + + return (node: SdsArgumentList, accept: ValidationAcceptor): void => { + // We already report other errors in this case + const containingCall = getContainerOfType(node, isSdsCall); + const callable = nodeMapper.callToCallableOrUndefined(containingCall); + if (isSdsAnnotation(callable)) { + return; + } + + const args = argumentsOrEmpty(node); + const duplicates = duplicatesBy(args, argumentToParameterOrUndefined); + + for (const duplicate of duplicates) { + const correspondingParameter = argumentToParameterOrUndefined(duplicate)!; + accept('error', `The parameter '${correspondingParameter.name}' is already set.`, { + node: duplicate, + code: CODE_ARGUMENT_LIST_DUPLICATE_PARAMETER, + }); + } + }; +}; + +export const argumentListMustSetAllRequiredParameters = (services: SafeDsServices) => { + const nodeMapper = services.helpers.NodeMapper; + + return (node: SdsAbstractCall, accept: ValidationAcceptor): void => { + const callable = nodeMapper.callToCallableOrUndefined(node); + + // We already report other errors in those cases + if (!callable || (isSdsCall(node) && isSdsAnnotation(callable))) { + return; + } + + const expectedParameters = parametersOrEmpty(callable).filter((it) => isRequiredParameter(it)); + if (isEmpty(expectedParameters)) { + return; + } + + const actualParameters = argumentsOrEmpty(node).map((it) => nodeMapper.argumentToParameterOrUndefined(it)); + + const missingTypeParameters = expectedParameters.filter((it) => !actualParameters.includes(it)); + if (!isEmpty(missingTypeParameters)) { + const kind = pluralize(missingTypeParameters.length, 'parameter'); + const missingParametersString = missingTypeParameters.map((it) => `'${it.name}'`).join(', '); + + accept('error', `The ${kind} ${missingParametersString} must be set here.`, { + node, + property: 'argumentList', + code: CODE_ARGUMENT_LIST_MISSING_REQUIRED_PARAMETER, + }); + } + }; +}; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 2dd33df42..1f273da3f 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -58,7 +58,12 @@ import { callableTypeMustNotHaveOptionalParameters, callableTypeParameterMustNotHaveConstModifier, } from './other/types/callableTypes.js'; -import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js'; +import { + argumentListMustNotHavePositionalArgumentsAfterNamedArguments, + argumentListMustNotHaveTooManyArguments, + argumentListMustNotSetParameterMultipleTimes, + argumentListMustSetAllRequiredParameters, +} from './other/argumentLists.js'; import { referenceMustNotBeFunctionPointer, referenceMustNotBeStaticClassOrEnumReference, @@ -127,6 +132,10 @@ export const registerValidationChecks = function (services: SafeDsServices) { assignmentShouldNotImplicitlyIgnoreResult(services), assignmentShouldHaveMoreThanWildcardsAsAssignees, ], + SdsAbstractCall: [ + argumentListMustNotHaveTooManyArguments(services), + argumentListMustSetAllRequiredParameters(services), + ], SdsAnnotation: [ annotationMustContainUniqueNames, annotationParameterListShouldNotBeEmpty, @@ -143,7 +152,10 @@ export const registerValidationChecks = function (services: SafeDsServices) { argumentCorrespondingParameterShouldNotBeDeprecated(services), argumentCorrespondingParameterShouldNotBeExperimental(services), ], - SdsArgumentList: [argumentListMustNotHavePositionalArgumentsAfterNamedArguments], + SdsArgumentList: [ + argumentListMustNotHavePositionalArgumentsAfterNamedArguments, + argumentListMustNotSetParameterMultipleTimes(services), + ], SdsAttribute: [attributeMustHaveTypeHint], SdsBlockLambda: [blockLambdaMustContainUniqueNames], SdsCall: [ diff --git a/src/language/validation/types.ts b/src/language/validation/types.ts index 0e2666af1..59b105050 100644 --- a/src/language/validation/types.ts +++ b/src/language/validation/types.ts @@ -46,7 +46,7 @@ export const callReceiverMustBeCallable = (services: SafeDsServices) => { } const callable = nodeMapper.callToCallableOrUndefined(node); - if (!callable) { + if (!callable || isSdsAnnotation(callable)) { accept('error', 'This expression is not callable.', { node: node.receiver, code: CODE_TYPE_CALLABLE_RECEIVER, diff --git a/tests/resources/validation/builtins/annotations/repeatable/main.sdstest b/tests/resources/validation/builtins/annotations/repeatable/main.sdstest index eb642f8e8..ffbe0d362 100644 --- a/tests/resources/validation/builtins/annotations/repeatable/main.sdstest +++ b/tests/resources/validation/builtins/annotations/repeatable/main.sdstest @@ -6,19 +6,19 @@ annotation SingleUse annotation MultiUse // $TEST$ no error r"The annotation '\w*' is not repeatable\." -»@SingleUse« +@»SingleUse« // $TEST$ no error r"The annotation '\w*' is not repeatable\." -»@MultiUse« +@»MultiUse« // $TEST$ no error r"The annotation '\w*' is not repeatable\." -»@MultiUse« +@»MultiUse« // $TEST$ no error r"The annotation '\w*' is not repeatable\." -»@UnresolvedAnnotation« +@»UnresolvedAnnotation« // $TEST$ no error r"The annotation '\w*' is not repeatable\." -»@UnresolvedAnnotation« +@»UnresolvedAnnotation« class CorrectUse // $TEST$ no error r"The annotation '\w*' is not repeatable\." -»@SingleUse« +@»SingleUse« // $TEST$ error "The annotation 'SingleUse' is not repeatable." -»@SingleUse« +@»SingleUse« class IncorrectUse diff --git a/tests/resources/validation/other/argument lists/duplicate parameter/main.sdstest b/tests/resources/validation/other/argument lists/duplicate parameter/main.sdstest new file mode 100644 index 000000000..244b3d03c --- /dev/null +++ b/tests/resources/validation/other/argument lists/duplicate parameter/main.sdstest @@ -0,0 +1,215 @@ +package tests.validation.other.argumentLists.duplicateParameter + +@Repeatable +annotation MyAnnotation(a: Int, b: Int = 0) + +class MyClass(a: Int, b: Int = 0) + +enum MyEnum { + MyEnumVariant(a: Int, b: Int = 0) +} + +fun myFunction(a: Int, b: Int = 0) + +segment mySegment(a: Int, b: Int = 0) {} + +pipeline myPipeline {} + + +@MyAnnotation( + // $TEST$ no error "The parameter 'a' is already set." + »1«, + // $TEST$ error "The parameter 'a' is already set." + »a = 1« +) +@MyAnnotation( + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1«, + // $TEST$ error "The parameter 'b' is already set." + »b = 1« +) +@MyAnnotation( + // $TEST$ no error "The parameter 'a' is already set." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« +) +segment test( + myCallableType: (a: Int, b: Int = 0) -> () +) { + + /* + * We can never call an annotation directly and should avoid showing additional errors. + */ + + val myAnnotationAlias = MyAnnotation; + + MyAnnotation( + // $TEST$ no error "The parameter 'a' is already set." + »1«, + // $TEST$ no error "The parameter 'a' is already set." + »a = 1« + ); + myAnnotationAlias( + // $TEST$ no error "The parameter 'b' is already set." + »b = 1«, + // $TEST$ no error "The parameter 'b' is already set." + »b = 1« + ); + MyAnnotation( + // $TEST$ no error "The parameter 'a' is already set." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« + ); + + + MyClass( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ error "The parameter 'a' is already set." + »a = 1« + ); + MyClass( + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1«, + // $TEST$ error "The parameter 'b' is already set." + »b = 1« + ); + MyClass( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« + ); + + + MyEnum.MyEnumVariant( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ error "The parameter 'a' is already set." + »a = 1« + ); + MyEnum.MyEnumVariant( + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1«, + // $TEST$ error "The parameter 'b' is already set." + »b = 1« + ); + MyEnum.MyEnumVariant( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« + ); + + + myFunction( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ error "The parameter 'a' is already set." + »a = 1« + ); + myFunction( + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1«, + // $TEST$ error "The parameter 'b' is already set." + »b = 1« + ); + myFunction( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« + ); + + + myCallableType( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ error "The parameter 'a' is already set." + »a = 1« + ); + myCallableType( + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1«, + // $TEST$ error "The parameter 'b' is already set." + »b = 1« + ); + myCallableType( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« + ); + + + val myBlockLambda = (a, b) {}; + + ((a, b) {})( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ error "The parameter 'a' is already set." + »a = 1« + ); + myBlockLambda( + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1«, + // $TEST$ error "The parameter 'b' is already set." + »b = 1« + ); + ((a, b) {})( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« + ); + + + val myExpressionLambda = (a, b) -> 1; + + ((a, b) -> 1)( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ error "The parameter 'a' is already set." + »a = 1« + ); + myExpressionLambda( + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1«, + // $TEST$ error "The parameter 'b' is already set." + »b = 1« + ); + ((a, b) -> 1)( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »b = 1« + ); + + + myPipeline( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »1« + ); + myPipeline( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1« + ); + unresolved( + // $TEST$ no error r"The parameter '\w+' is already set\." + »1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »1« + ); + unresolved( + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1«, + // $TEST$ no error r"The parameter '\w+' is already set\." + »a = 1« + ); +} diff --git a/tests/resources/validation/other/argument lists/missing required parameter/main.sdstest b/tests/resources/validation/other/argument lists/missing required parameter/main.sdstest new file mode 100644 index 000000000..167d6fead --- /dev/null +++ b/tests/resources/validation/other/argument lists/missing required parameter/main.sdstest @@ -0,0 +1,175 @@ +package tests.validation.other.argumentLists.missingRequiredParameter + +@Repeatable +annotation MyAnnotation(a: Int, b: Int, c: Int = 0) + +class MyClass(a: Int, b: Int, c: Int = 0) + +enum MyEnum { + MyEnumVariant(a: Int, b: Int, c: Int = 0) +} + +fun myFunction(a: Int, b: Int, c: Int = 0) + +segment mySegment1(a: Int, b: Int, c: Int = 0) {} + +pipeline myPipeline {} + + +// $TEST$ error "The parameters 'a', 'b' must be set here." +@MyAnnotation»()« +// $TEST$ error "The parameter 'b' must be set here." +@MyAnnotation»(1)« +// $TEST$ no error r"The parameters? .* must be set here\." +@MyAnnotation»(1, 2)« +// $TEST$ no error r"The parameters? .* must be set here\." +@MyAnnotation»(1, 2, 3)« +// $TEST$ no error r"The parameters? .* must be set here\." +@MyAnnotation»(1, 2, 3, 4)« +// $TEST$ error "The parameter 'b' must be set here." +@MyAnnotation»(1, c = 2)« +// $TEST$ no error r"The parameters? .* must be set here\." +@MyAnnotation»(b = 1, a = 2)« +segment mySegment2( + myCallableType: (a: Int, b: Int, c: Int = 0) -> () +) { + val myBlockLambda = (a: Int, b: Int, c: Int = 0) {}; + val myExpressionLambda = (a: Int, b: Int, c: Int = 0) -> 1; + + /* + * We can never call an annotation directly and should avoid showing additional errors. + */ + + // $TEST$ no error r"The parameters? .* must be set here\." + MyAnnotation»()«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyAnnotation»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyAnnotation»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyAnnotation»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyAnnotation»(1, 2, 3, 4)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyAnnotation»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyAnnotation»(b = 1, a = 2)«; + + + // $TEST$ error "The parameters 'a', 'b' must be set here." + MyClass»()«; + // $TEST$ error "The parameter 'b' must be set here." + MyClass»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyClass»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyClass»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyClass»(1, 2, 3, 4)«; + // $TEST$ error "The parameter 'b' must be set here." + MyClass»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyClass»(b = 1, a = 2)«; + + + // $TEST$ error "The parameters 'a', 'b' must be set here." + MyEnum.MyEnumVariant»()«; + // $TEST$ error "The parameter 'b' must be set here." + MyEnum.MyEnumVariant»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyEnum.MyEnumVariant»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyEnum.MyEnumVariant»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyEnum.MyEnumVariant»(1, 2, 3, 4)«; + // $TEST$ error "The parameter 'b' must be set here." + MyEnum.MyEnumVariant»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + MyEnum.MyEnumVariant»(b = 1, a = 2)«; + + + // $TEST$ error "The parameters 'a', 'b' must be set here." + myFunction»()«; + // $TEST$ error "The parameter 'b' must be set here." + myFunction»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myFunction»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myFunction»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myFunction»(1, 2, 3, 4)«; + // $TEST$ error "The parameter 'b' must be set here." + myFunction»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myFunction»(b = 1, a = 2)«; + + + // $TEST$ error "The parameters 'a', 'b' must be set here." + mySegment1»()«; + // $TEST$ error "The parameter 'b' must be set here." + mySegment1»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + mySegment1»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + mySegment1»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + mySegment1»(1, 2, 3, 4)«; + // $TEST$ error "The parameter 'b' must be set here." + mySegment1»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + mySegment1»(b = 1, a = 2)«; + + + // $TEST$ error "The parameters 'a', 'b' must be set here." + myCallableType»()«; + // $TEST$ error "The parameter 'b' must be set here." + myCallableType»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myCallableType»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myCallableType»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myCallableType»(1, 2, 3, 4)«; + // $TEST$ error "The parameter 'b' must be set here." + myCallableType»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myCallableType»(b = 1, a = 2)«; + + + // $TEST$ error "The parameters 'a', 'b' must be set here." + myBlockLambda»()«; + // $TEST$ error "The parameter 'b' must be set here." + myBlockLambda»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myBlockLambda»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myBlockLambda»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myBlockLambda»(1, 2, 3, 4)«; + // $TEST$ error "The parameter 'b' must be set here." + myBlockLambda»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myBlockLambda»(b = 1, a = 2)«; + + + // $TEST$ error "The parameters 'a', 'b' must be set here." + myExpressionLambda»()«; + // $TEST$ error "The parameter 'b' must be set here." + myExpressionLambda»(1)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myExpressionLambda»(1, 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myExpressionLambda»(1, 2, 3)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myExpressionLambda»(1, 2, 3, 4)«; + // $TEST$ error "The parameter 'b' must be set here." + myExpressionLambda»(1, c = 2)«; + // $TEST$ no error r"The parameters? .* must be set here\." + myExpressionLambda»(b = 1, a = 2)«; + + + // $TEST$ no error r"The parameters? .* must be set here\." + myPipeline»()«; + // $TEST$ no error r"The parameters? .* must be set here\." + unresolved»()«; +} diff --git a/tests/resources/validation/other/argument lists/too many arguments/main.sdstest b/tests/resources/validation/other/argument lists/too many arguments/main.sdstest new file mode 100644 index 000000000..3934b8a0b --- /dev/null +++ b/tests/resources/validation/other/argument lists/too many arguments/main.sdstest @@ -0,0 +1,186 @@ +package tests.validation.other.argumentLists.tooManyArguments + +@Repeatable +annotation MyAnnotation1(a: Int, b: Int) +@Repeatable +annotation MyAnnotation2(a: Int, b: Int = 0) + +class MyClass1(a: Int, b: Int) +class MyClass2(a: Int, b: Int = 0) + +enum MyEnum { + MyEnumVariant1(a: Int, b: Int) + MyEnumVariant2(a: Int, b: Int = 0) +} + +fun myFunction1(a: Int, b: Int) +fun myFunction2(a: Int, b: Int = 0) + +segment mySegment1(a: Int, b: Int) {} +segment mySegment2(a: Int, b: Int = 0) {} + +pipeline myPipeline {} + + +// $TEST$ no error r"Expected .* arguments? but got \d*\." +@MyAnnotation1»(1)« +// $TEST$ no error r"Expected .* arguments? but got \d*\." +@MyAnnotation1»(1, 2)« +// $TEST$ error "Expected exactly 2 arguments but got 3." +@MyAnnotation1»(1, 2, 3)« +// $TEST$ no error r"Expected .* arguments? but got \d*\." +@MyAnnotation2»()« +// $TEST$ no error r"Expected .* arguments? but got \d*\." +@MyAnnotation2»(1)« +// $TEST$ no error r"Expected .* arguments? but got \d*\." +@MyAnnotation2»(1, 2)« +// $TEST$ error "Expected between 1 and 2 arguments but got 3." +@MyAnnotation2»(1, 2, 3)« +segment mySegment3( + myCallableType1: (a: Int, b: Int) -> (), + myCallableType2: (a: Int, b: Int = 0) -> (), +) { + val myBlockLambda1 = (a: Int, b: Int) {}; + val myBlockLambda2 = (a: Int, b: Int = 0) {}; + + val myExpressionLambda1 = (a: Int, b: Int) -> 1; + val myExpressionLambda2 = (a: Int, b: Int = 0) -> 1; + + + /* + * We can never call an annotation directly and should avoid showing additional errors. + */ + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyAnnotation1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyAnnotation1»(1, 2)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyAnnotation1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyAnnotation2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyAnnotation2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyAnnotation2»(1, 2)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyAnnotation2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyClass1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyClass1»(1, 2)«; + // $TEST$ error "Expected exactly 2 arguments but got 3." + MyClass1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyClass2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyClass2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyClass2»(1, 2)«; + // $TEST$ error "Expected between 1 and 2 arguments but got 3." + MyClass2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyEnum.MyEnumVariant1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyEnum.MyEnumVariant1»(1, 2)«; + // $TEST$ error "Expected exactly 2 arguments but got 3." + MyEnum.MyEnumVariant1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyEnum.MyEnumVariant2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyEnum.MyEnumVariant2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + MyEnum.MyEnumVariant2»(1, 2)«; + // $TEST$ error "Expected between 1 and 2 arguments but got 3." + MyEnum.MyEnumVariant2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myFunction1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myFunction1»(1, 2)«; + // $TEST$ error "Expected exactly 2 arguments but got 3." + myFunction1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myFunction2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myFunction2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myFunction2»(1, 2)«; + // $TEST$ error "Expected between 1 and 2 arguments but got 3." + myFunction2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + mySegment1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + mySegment1»(1, 2)«; + // $TEST$ error "Expected exactly 2 arguments but got 3." + mySegment1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + mySegment2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + mySegment2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + mySegment2»(1, 2)«; + // $TEST$ error "Expected between 1 and 2 arguments but got 3." + mySegment2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myCallableType1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myCallableType1»(1, 2)«; + // $TEST$ error "Expected exactly 2 arguments but got 3." + myCallableType1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myCallableType2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myCallableType2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myCallableType2»(1, 2)«; + // $TEST$ error "Expected between 1 and 2 arguments but got 3." + myCallableType2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myBlockLambda1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myBlockLambda1»(1, 2)«; + // $TEST$ error "Expected exactly 2 arguments but got 3." + myBlockLambda1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myBlockLambda2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myBlockLambda2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myBlockLambda2»(1, 2)«; + // $TEST$ error "Expected between 1 and 2 arguments but got 3." + myBlockLambda2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myExpressionLambda1»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myExpressionLambda1»(1, 2)«; + // $TEST$ error "Expected exactly 2 arguments but got 3." + myExpressionLambda1»(1, 2, 3)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myExpressionLambda2»()«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myExpressionLambda2»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myExpressionLambda2»(1, 2)«; + // $TEST$ error "Expected between 1 and 2 arguments but got 3." + myExpressionLambda2»(1, 2, 3)«; + + + // $TEST$ no error r"Expected .* arguments? but got \d*\." + myPipeline»(1)«; + // $TEST$ no error r"Expected .* arguments? but got \d*\." + unresolved»(1)«; +} diff --git a/tests/resources/validation/types/checking/receiver of call must be callable/main.sdstest b/tests/resources/validation/types/checking/receiver of call must be callable/main.sdstest index 2fe59420b..38355fe0f 100644 --- a/tests/resources/validation/types/checking/receiver of call must be callable/main.sdstest +++ b/tests/resources/validation/types/checking/receiver of call must be callable/main.sdstest @@ -29,6 +29,9 @@ segment mySegment2(a: Int, b: () -> ()) { // $TEST$ no error "This expression is not callable." »MyAnnotation«(); + // $TEST$ error "This expression is not callable." + val myAnnotationAlias = MyAnnotation; + »myAnnotationAlias«(); // $TEST$ no error "This expression is not callable." »MyClass«(); // $TEST$ error "This expression is not callable." @@ -49,8 +52,14 @@ segment mySegment2(a: Int, b: () -> ()) { »myFunction«(); // $TEST$ no error "This expression is not callable." »myPipeline«(); + // $TEST$ error "This expression is not callable." + val myPipelineAlias = MyPipeline; + »myPipelineAlias«(); // $TEST$ no error "This expression is not callable." »MySchema«(); + // $TEST$ error "This expression is not callable." + val mySchemaAlias = MySchema; + »mySchemaAlias«(); // $TEST$ no error "This expression is not callable." »mySegment1«(); // $TEST$ error "This expression is not callable." @@ -69,9 +78,9 @@ segment mySegment2(a: Int, b: () -> ()) { »(e)«(); /* - * If a scoping error for the receiver is reported we should not report another error. If the receiver is + * If a linking error for the receiver is reported we should not report another error. If the receiver is * resolved, but it eventually points to something unresolved, we should still report another error, since the - * distance to the scoping error might be too large. + * distance to the linking error might be too large. */ // $TEST$ no error "This expression is not callable." diff --git a/tests/resources/validation/types/named types/omitted type parameter/main.sdstest b/tests/resources/validation/types/named types/missing type parameter/main.sdstest similarity index 60% rename from tests/resources/validation/types/named types/omitted type parameter/main.sdstest rename to tests/resources/validation/types/named types/missing type parameter/main.sdstest index 39580c97f..6af03c3e6 100644 --- a/tests/resources/validation/types/named types/omitted type parameter/main.sdstest +++ b/tests/resources/validation/types/named types/missing type parameter/main.sdstest @@ -1,5 +1,5 @@ -package tests.validation.types.namedTypes.omittedTypeParameter +package tests.validation.types.namedTypes.missingTypeParameter class MyClassWithoutTypeParameter class MyClassWithOneTypeParameter @@ -12,54 +12,54 @@ enum MyEnum { } fun myFunction( - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." p1: MyClassWithoutTypeParameter»<>«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." p2: MyClassWithoutTypeParameter»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." p3: MyClassWithoutTypeParameter»«, // $TEST$ error "The type parameter 'T' must be set here." p4: MyClassWithOneTypeParameter»<>«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." p5: MyClassWithOneTypeParameter»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." p6: MyClassWithOneTypeParameter»«, // $TEST$ error "The type parameters 'K', 'V' must be set here." p7: MyClassWithTwoTypeParameters»<>«, // $TEST$ error "The type parameter 'V' must be set here." p8: MyClassWithTwoTypeParameters»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." p9: MyClassWithTwoTypeParameters»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." q1: MyEnum.MyEnumVariantWithoutTypeParameter»<>«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." q2: MyEnum.MyEnumVariantWithoutTypeParameter»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." q3: MyEnum.MyEnumVariantWithoutTypeParameter»«, // $TEST$ error "The type parameter 'T' must be set here." q4: MyEnum.MyEnumVariantWithOneTypeParameter»<>«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." q5: MyEnum.MyEnumVariantWithOneTypeParameter»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." q6: MyEnum.MyEnumVariantWithOneTypeParameter»«, // $TEST$ error "The type parameters 'K', 'V' must be set here." q7: MyEnum.MyEnumVariantWithTwoTypeParameters»<>«, // $TEST$ error "The type parameter 'V' must be set here." q8: MyEnum.MyEnumVariantWithTwoTypeParameters»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." q9: MyEnum.MyEnumVariantWithTwoTypeParameters»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." a1: Unresolved»<>«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." a2: Unresolved»«, - // $TEST$ no error r"The type parameters? '\w*' must be set here\." + // $TEST$ no error r"The type parameters? .* must be set here\." a3: Unresolved»«, )