Skip to content

Commit

Permalink
feat: error if @pythonName and @PythonCall are set on a function (#685)
Browse files Browse the repository at this point in the history
### Summary of Changes

`@PythonCall` is a more general version of `@PythonName`. If
`@PythonCall` is set, `@PythonName` gets ignored completely. We now show
an error if both annotation are used together on a function.
  • Loading branch information
lars-reimann authored Oct 23, 2023
1 parent 15114df commit d22c446
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/language/validation/builtins/pythonModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
);
Expand Down
27 changes: 25 additions & 2 deletions src/language/validation/builtins/pythonName.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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,
});
};
Expand Down
11 changes: 9 additions & 2 deletions src/language/validation/safe-ds-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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],
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit d22c446

Please sign in to comment.