diff --git a/src/language/validation/builtins/pythonModule.ts b/src/language/validation/builtins/pythonModule.ts index acd5b650b..4061934da 100644 --- a/src/language/validation/builtins/pythonModule.ts +++ b/src/language/validation/builtins/pythonModule.ts @@ -20,6 +20,7 @@ export const pythonModuleShouldDifferFromSafeDsPackage = (services: SafeDsServic 'The Python module is identical to the Safe-DS package, so the annotation call can be removed.', { node: annotationCall, + property: 'annotation', code: CODE_PYTHON_MODULE_SAME_AS_SAFE_DS_PACKAGE, }, ); diff --git a/src/language/validation/builtins/pythonName.ts b/src/language/validation/builtins/pythonName.ts index 26a8235ef..7d285348d 100644 --- a/src/language/validation/builtins/pythonName.ts +++ b/src/language/validation/builtins/pythonName.ts @@ -1,10 +1,32 @@ import { ValidationAcceptor } from 'langium'; -import { SdsDeclaration } from '../../generated/ast.js'; +import { SdsDeclaration, SdsFunction } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; -import { findFirstAnnotationCallOf } from '../../helpers/nodeProperties.js'; +import { findFirstAnnotationCallOf, hasAnnotationCallOf } from '../../helpers/nodeProperties.js'; +export const CODE_PYTHON_NAME_MUTUALLY_EXCLUSIVE_WITH_PYTHON_CALL = 'python-name/mutually-exclusive-with-python-call'; export const CODE_PYTHON_NAME_SAME_AS_SAFE_DS_NAME = 'python-name/same-as-safe-ds-name'; +export const pythonNameMustNotBeSetIfPythonCallIsSet = (services: SafeDsServices) => { + const builtinAnnotations = services.builtins.Annotations; + + return (node: SdsFunction, accept: ValidationAcceptor) => { + if (!hasAnnotationCallOf(node, builtinAnnotations.PythonCall)) { + return; + } + + const firstPythonName = findFirstAnnotationCallOf(node, builtinAnnotations.PythonName); + if (!firstPythonName) { + return; + } + + accept('error', 'A Python name must not be set if a Python call is set.', { + node: firstPythonName, + property: 'annotation', + code: CODE_PYTHON_NAME_MUTUALLY_EXCLUSIVE_WITH_PYTHON_CALL, + }); + }; +}; + export const pythonNameShouldDifferFromSafeDsName = (services: SafeDsServices) => { const builtinAnnotations = services.builtins.Annotations; @@ -17,6 +39,7 @@ export const pythonNameShouldDifferFromSafeDsName = (services: SafeDsServices) = const annotationCall = findFirstAnnotationCallOf(node, builtinAnnotations.PythonName)!; accept('info', 'The Python name is identical to the Safe-DS name, so the annotation call can be removed.', { node: annotationCall, + property: 'annotation', code: CODE_PYTHON_NAME_SAME_AS_SAFE_DS_NAME, }); }; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 988c725bc..2c65cb745 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -124,7 +124,10 @@ import { namedTypeTypeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments, } from './other/types/namedTypes.js'; import { classMustNotInheritItself, classMustOnlyInheritASingleClass } from './inheritance.js'; -import { pythonNameShouldDifferFromSafeDsName } from './builtins/pythonName.js'; +import { + pythonNameMustNotBeSetIfPythonCallIsSet, + pythonNameShouldDifferFromSafeDsName, +} from './builtins/pythonName.js'; import { pythonModuleShouldDifferFromSafeDsPackage } from './builtins/pythonModule.js'; import { divisionDivisorMustNotBeZero } from './other/expressions/infixOperations.js'; import { constantParameterMustHaveConstantDefaultValue } from './other/declarations/parameters.js'; @@ -209,7 +212,11 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsEnumBody: [enumBodyShouldNotBeEmpty], SdsEnumVariant: [enumVariantMustContainUniqueNames, enumVariantParameterListShouldNotBeEmpty], SdsExpressionLambda: [expressionLambdaMustContainUniqueNames], - SdsFunction: [functionMustContainUniqueNames, functionResultListShouldNotBeEmpty], + SdsFunction: [ + functionMustContainUniqueNames, + functionResultListShouldNotBeEmpty, + pythonNameMustNotBeSetIfPythonCallIsSet(services), + ], SdsImport: [importPackageMustExist(services), importPackageShouldNotBeEmpty(services)], SdsImportedDeclaration: [importedDeclarationAliasShouldDifferFromDeclarationName], SdsIndexedAccess: [indexedAccessesShouldBeUsedWithCaution], diff --git a/tests/resources/validation/builtins/annotations/pythonModule/error.sdstest b/tests/resources/validation/builtins/annotations/pythonModule/error.sdstest index 34fd5c659..97adb3584 100644 --- a/tests/resources/validation/builtins/annotations/pythonModule/error.sdstest +++ b/tests/resources/validation/builtins/annotations/pythonModule/error.sdstest @@ -1,4 +1,4 @@ // $TEST$ info "The Python module is identical to the Safe-DS package, so the annotation call can be removed." -»@PythonModule("tests.validation.builtins.annotations.pythonModule")« +@»PythonModule«("tests.validation.builtins.annotations.pythonModule") package tests.validation.builtins.annotations.pythonModule diff --git a/tests/resources/validation/builtins/annotations/pythonModule/no error.sdstest b/tests/resources/validation/builtins/annotations/pythonModule/no error.sdstest index 41bf3d998..fa3f4fe9b 100644 --- a/tests/resources/validation/builtins/annotations/pythonModule/no error.sdstest +++ b/tests/resources/validation/builtins/annotations/pythonModule/no error.sdstest @@ -1,6 +1,6 @@ // $TEST$ no info "The Python module is identical to the Safe-DS package, so the annotation call can be removed." -»@PythonModule("tests.validation.builtins.annotations.pythonModule.other")« +@»PythonModule«("tests.validation.builtins.annotations.pythonModule.other") // $TEST$ no info "The Python module is identical to the Safe-DS package, so the annotation call can be removed." -»@PythonModule("tests.validation.builtins.annotations.pythonModule")« +@»PythonModule«("tests.validation.builtins.annotations.pythonModule") package tests.validation.builtins.annotations.pythonModule diff --git a/tests/resources/validation/builtins/annotations/pythonName/main.sdstest b/tests/resources/validation/builtins/annotations/pythonName/identical to safe-ds name/main.sdstest similarity index 67% rename from tests/resources/validation/builtins/annotations/pythonName/main.sdstest rename to tests/resources/validation/builtins/annotations/pythonName/identical to safe-ds name/main.sdstest index a787c87d4..cae7182cc 100644 --- a/tests/resources/validation/builtins/annotations/pythonName/main.sdstest +++ b/tests/resources/validation/builtins/annotations/pythonName/identical to safe-ds name/main.sdstest @@ -1,11 +1,11 @@ -package tests.validation.builtins.annotations.pythonName +package tests.validation.builtins.annotations.pythonName.identicalToSafeDsName // $TEST$ info "The Python name is identical to the Safe-DS name, so the annotation call can be removed." -»@PythonName("TestClass1")« +@»PythonName«("TestClass1") class TestClass1 // $TEST$ no info "The Python name is identical to the Safe-DS name, so the annotation call can be removed." -»@PythonName("Test_Class_2")« +@»PythonName«("Test_Class_2") // $TEST$ no info "The Python name is identical to the Safe-DS name, so the annotation call can be removed." -»@PythonName("TestClass2")« +@»PythonName«("TestClass2") class TestClass2 diff --git a/tests/resources/validation/builtins/annotations/pythonName/no annotation.sdstest b/tests/resources/validation/builtins/annotations/pythonName/identical to safe-ds name/no annotation.sdstest similarity index 61% rename from tests/resources/validation/builtins/annotations/pythonName/no annotation.sdstest rename to tests/resources/validation/builtins/annotations/pythonName/identical to safe-ds name/no annotation.sdstest index 400346c7b..de231603b 100644 --- a/tests/resources/validation/builtins/annotations/pythonName/no annotation.sdstest +++ b/tests/resources/validation/builtins/annotations/pythonName/identical to safe-ds name/no annotation.sdstest @@ -1,4 +1,4 @@ -package tests.validation.builtins.annotations.pythonName +package tests.validation.builtins.annotations.pythonName.identicalToSafeDsName // $TEST$ no info "The Python name is identical to the Safe-DS name, so the annotation call can be removed." class TestClass3 diff --git a/tests/resources/validation/builtins/annotations/pythonName/mutually exclusive with python call/main.sdstest b/tests/resources/validation/builtins/annotations/pythonName/mutually exclusive with python call/main.sdstest new file mode 100644 index 000000000..23901ea2a --- /dev/null +++ b/tests/resources/validation/builtins/annotations/pythonName/mutually exclusive with python call/main.sdstest @@ -0,0 +1,26 @@ +package tests.validation.builtins.annotations.pythonName.mutuallyExclusiveWithPythonCall + +@PythonCall("myFunction1()") +// $TEST$ error "A Python name must not be set if a Python call is set." +@»PythonName«("my_function_1") +// $TEST$ no error "A Python name must not be set if a Python call is set." +@»PythonName«("my_function_1") +fun myFunction1() + +// $TEST$ error "A Python name must not be set if a Python call is set." +@»PythonName«("my_function_2") +// $TEST$ no error "A Python name must not be set if a Python call is set." +@»PythonName«("my_function_2") +@PythonCall("myFunction2()") +fun myFunction2() + +// $TEST$ no error "A Python name must not be set if a Python call is set." +@»PythonName«("my_function_3") +fun myFunction3() + +// $TEST$ no error "A Python name must not be set if a Python call is set." +@»PythonName«("my_function_2") +// $TEST$ no error "A Python name must not be set if a Python call is set." +@»PythonName«("my_function_2") +@PythonCall("myFunction2()") +class MyClass()