diff --git a/src/language/validation/other/expressions/references.ts b/src/language/validation/other/expressions/references.ts index 1bb04dd27..d88b80f98 100644 --- a/src/language/validation/other/expressions/references.ts +++ b/src/language/validation/other/expressions/references.ts @@ -1,8 +1,42 @@ -import { isSdsAnnotation, isSdsPipeline, isSdsSchema, SdsReference } from '../../../generated/ast.js'; -import { ValidationAcceptor } from 'langium'; +import { + isSdsAnnotation, + isSdsCall, + isSdsFunction, + isSdsMemberAccess, + isSdsPipeline, + isSdsSchema, + isSdsSegment, + SdsReference, +} from '../../../generated/ast.js'; +import { AstNode, ValidationAcceptor } from 'langium'; +export const CODE_REFERENCE_FUNCTION_POINTER = 'reference/function-pointer'; export const CODE_REFERENCE_TARGET = 'reference/target'; +export const referenceMustNotBeFunctionPointer = (node: SdsReference, accept: ValidationAcceptor): void => { + const target = node.target?.ref; + if (!isSdsFunction(target) && !isSdsSegment(target)) { + return; + } + + // + let container: AstNode | undefined = node.$container; + if (isSdsMemberAccess(container) && node.$containerProperty === 'member') { + container = container.$container; + } + + if (!isSdsCall(container)) { + accept( + 'error', + 'Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead.', + { + node, + code: CODE_REFERENCE_FUNCTION_POINTER, + }, + ); + } +}; + export const referenceTargetMustNotBeAnnotationPipelineOrSchema = ( node: SdsReference, accept: ValidationAcceptor, diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 1644bb2e3..41e38f8c8 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -45,7 +45,10 @@ import { } from './other/types/callableTypes.js'; import { typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/types/typeArgumentLists.js'; import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js'; -import { referenceTargetMustNotBeAnnotationPipelineOrSchema } from './other/expressions/references.js'; +import { + referenceMustNotBeFunctionPointer, + referenceTargetMustNotBeAnnotationPipelineOrSchema, +} from './other/expressions/references.js'; import { annotationCallAnnotationShouldNotBeDeprecated, argumentCorrespondingParameterShouldNotBeDeprecated, @@ -141,6 +144,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsPipeline: [pipelineMustContainUniqueNames], SdsPlaceholder: [placeholdersMustNotBeAnAlias, placeholderShouldBeUsed(services)], SdsReference: [ + referenceMustNotBeFunctionPointer, referenceTargetMustNotBeAnnotationPipelineOrSchema, referenceTargetShouldNotBeDeprecated(services), referenceTargetShouldNotExperimental(services), diff --git a/tests/resources/validation/other/expressions/references/function pointer/main.sdstest b/tests/resources/validation/other/expressions/references/function pointer/main.sdstest new file mode 100644 index 000000000..e5a957baa --- /dev/null +++ b/tests/resources/validation/other/expressions/references/function pointer/main.sdstest @@ -0,0 +1,68 @@ +package tests.validation.other.expressions.references.target + +class MyClass { + fun myInstanceMethod() + static fun myStaticMethod() +} + +fun myFunction1() +fun myFunction2(p: Any) + +segment mySegment1() {} + +segment mySegment2() { + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass().»myInstanceMethod«; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass().»myInstanceMethod«.a; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass().»myInstanceMethod«.a(); + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + myFunction2(MyClass().»myInstanceMethod«); + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass().»myInstanceMethod«(); + + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass.»myStaticMethod«; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass.»myStaticMethod«.a; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass.»myStaticMethod«.a(); + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + myFunction2(MyClass.»myStaticMethod«); + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + MyClass.»myStaticMethod«(); + + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »myFunction1«; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »myFunction1«.a; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »myFunction1«.a(); + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + myFunction2(»myFunction1«); + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »myFunction1«(); + + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »mySegment1«; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »mySegment1«.a; + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »mySegment1«.a(); + // $TEST$ error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + myFunction2(»mySegment1«); + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »mySegment1«(); + + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »unresolved«; + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »unresolved«.a; + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »unresolved«.a(); + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + myFunction2(»unresolved«); + // $TEST$ no error "Function pointers are not allowed to provide a cleaner graphical view. Use a lambda instead." + »unresolved«(); +}