From 184cf2bc881aac9591b5f776c23e4b76c01ccf4d Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 17:14:38 +0200 Subject: [PATCH 01/13] feat: warn about deprecated/experimental called annotations --- .../builtins/safe-ds-core-annotations.ts | 64 +++++++ src/language/builtins/safe-ds-core-classes.ts | 47 ++---- src/language/safe-ds-module.ts | 3 + .../validation/builtins/deprecated.ts | 24 +++ .../validation/builtins/experimental.ts | 24 +++ src/language/validation/safe-ds-validator.ts | 8 +- .../safeds/lang/coreAnnotations.sdsstub | 10 +- .../deprecated/assigned result/main.sdstest | 159 ++++++++++++++++++ .../deprecated/called annotation/main.sdstest | 13 ++ .../corresponding parameter/main.sdstest | 146 ++++++++++++++++ .../referenced declaration/main.sdstest | 159 ++++++++++++++++++ .../called annotation/main.sdstest | 13 ++ 12 files changed, 627 insertions(+), 43 deletions(-) create mode 100644 src/language/builtins/safe-ds-core-annotations.ts create mode 100644 src/language/validation/builtins/deprecated.ts create mode 100644 src/language/validation/builtins/experimental.ts create mode 100644 tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest create mode 100644 tests/resources/validation/builtins/annotations/deprecated/called annotation/main.sdstest create mode 100644 tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest create mode 100644 tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest create mode 100644 tests/resources/validation/builtins/annotations/experimental/called annotation/main.sdstest diff --git a/src/language/builtins/safe-ds-core-annotations.ts b/src/language/builtins/safe-ds-core-annotations.ts new file mode 100644 index 000000000..6e3441ecc --- /dev/null +++ b/src/language/builtins/safe-ds-core-annotations.ts @@ -0,0 +1,64 @@ +import { SafeDsServices } from '../safe-ds-module.js'; +import { resolveRelativePathToBuiltinFile } from './fileFinder.js'; +import { isSdsAnnotation, isSdsModule, SdsAnnotatedObject, SdsAnnotation } from '../generated/ast.js'; +import { LangiumDocuments } from 'langium'; +import { annotationCallsOrEmpty, moduleMembersOrEmpty } from '../helpers/nodeProperties.js'; + +const CORE_ANNOTATIONS_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreAnnotations.sdsstub'); + +export class SafeDsCoreAnnotations { + private readonly langiumDocuments: LangiumDocuments; + private readonly cache: Map = new Map(); + + constructor(services: SafeDsServices) { + this.langiumDocuments = services.shared.workspace.LangiumDocuments; + } + + isDeprecated(node: SdsAnnotatedObject | undefined): boolean { + return annotationCallsOrEmpty(node).some((it) => { + const annotation = it.annotation?.ref; + return annotation === this.Deprecated; + }); + } + + isExperimental(node: SdsAnnotatedObject | undefined): boolean { + return annotationCallsOrEmpty(node).some((it) => { + const annotation = it.annotation?.ref; + return annotation === this.Experimental; + }); + } + + private get Deprecated(): SdsAnnotation | undefined { + return this.getAnnotation('Deprecated'); + } + + private get Experimental(): SdsAnnotation | undefined { + return this.getAnnotation('Experimental'); + } + + private getAnnotation(name: string): SdsAnnotation | undefined { + if (this.cache.has(name)) { + return this.cache.get(name); + } + + if (!this.langiumDocuments.hasDocument(CORE_ANNOTATIONS_URI)) { + /* c8 ignore next 2 */ + return undefined; + } + + const document = this.langiumDocuments.getOrCreateDocument(CORE_ANNOTATIONS_URI); + const root = document.parseResult.value; + if (!isSdsModule(root)) { + /* c8 ignore next 2 */ + return undefined; + } + + const firstMatchingModuleMember = moduleMembersOrEmpty(root).find((m) => m.name === name); + if (!isSdsAnnotation(firstMatchingModuleMember)) { + /* c8 ignore next 2 */ + return undefined; + } + + return firstMatchingModuleMember; + } +} diff --git a/src/language/builtins/safe-ds-core-classes.ts b/src/language/builtins/safe-ds-core-classes.ts index 51cc491cb..d30366f81 100644 --- a/src/language/builtins/safe-ds-core-classes.ts +++ b/src/language/builtins/safe-ds-core-classes.ts @@ -8,69 +8,44 @@ const CORE_CLASSES_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreClass export class SafeDsCoreClasses { private readonly langiumDocuments: LangiumDocuments; + private readonly cache: Map = new Map(); constructor(services: SafeDsServices) { this.langiumDocuments = services.shared.workspace.LangiumDocuments; } - private cachedAny: SdsClass | undefined; - /* c8 ignore start */ get Any(): SdsClass | undefined { - if (!this.cachedAny) { - this.cachedAny = this.getClass('Any'); - } - return this.cachedAny; + return this.getClass('Any'); } /* c8 ignore stop */ - private cachedBoolean: SdsClass | undefined; - get Boolean(): SdsClass | undefined { - if (!this.cachedBoolean) { - this.cachedBoolean = this.getClass('Boolean'); - } - return this.cachedBoolean; + return this.getClass('Boolean'); } - private cachedFloat: SdsClass | undefined; - get Float(): SdsClass | undefined { - if (!this.cachedFloat) { - this.cachedFloat = this.getClass('Float'); - } - return this.cachedFloat; + return this.getClass('Float'); } - private cachedInt: SdsClass | undefined; - get Int(): SdsClass | undefined { - if (!this.cachedInt) { - this.cachedInt = this.getClass('Int'); - } - return this.cachedInt; + return this.getClass('Int'); } - private cachedNothing: SdsClass | undefined; - get Nothing(): SdsClass | undefined { - if (!this.cachedNothing) { - this.cachedNothing = this.getClass('Nothing'); - } - return this.cachedNothing; + return this.getClass('Nothing'); } - private cachedString: SdsClass | undefined; - get String(): SdsClass | undefined { - if (!this.cachedString) { - this.cachedString = this.getClass('String'); - } - return this.cachedString; + return this.getClass('String'); } private getClass(name: string): SdsClass | undefined { + if (this.cache.has(name)) { + return this.cache.get(name); + } + if (!this.langiumDocuments.hasDocument(CORE_CLASSES_URI)) { /* c8 ignore next 2 */ return undefined; diff --git a/src/language/safe-ds-module.ts b/src/language/safe-ds-module.ts index 4d55aace1..69da654e1 100644 --- a/src/language/safe-ds-module.ts +++ b/src/language/safe-ds-module.ts @@ -20,12 +20,14 @@ import { SafeDsTypeComputer } from './typing/safe-ds-type-computer.js'; import { SafeDsCoreClasses } from './builtins/safe-ds-core-classes.js'; import { SafeDsPackageManager } from './workspace/safe-ds-package-manager.js'; import { SafeDsNodeMapper } from './helpers/safe-ds-node-mapper.js'; +import { SafeDsCoreAnnotations } from './builtins/safe-ds-core-annotations.js'; /** * Declaration of custom services - add your own service classes here. */ export type SafeDsAddedServices = { builtins: { + CoreAnnotations: SafeDsCoreAnnotations; CoreClasses: SafeDsCoreClasses; }; helpers: { @@ -52,6 +54,7 @@ export type SafeDsServices = LangiumServices & SafeDsAddedServices; */ export const SafeDsModule: Module = { builtins: { + CoreAnnotations: (services) => new SafeDsCoreAnnotations(services), CoreClasses: (services) => new SafeDsCoreClasses(services), }, helpers: { diff --git a/src/language/validation/builtins/deprecated.ts b/src/language/validation/builtins/deprecated.ts new file mode 100644 index 000000000..2373ed444 --- /dev/null +++ b/src/language/validation/builtins/deprecated.ts @@ -0,0 +1,24 @@ +import { ValidationAcceptor } from 'langium'; +import { SdsAnnotationCall } from '../../generated/ast.js'; +import { SafeDsServices } from '../../safe-ds-module.js'; + +export const CODE_DEPRECATED_ASSIGNED_RESULT = 'deprecated/assigned-result'; +export const CODE_DEPRECATED_CALLED_ANNOTATION = 'deprecated/called-annotation'; +export const CODE_DEPRECATED_CORRESPONDING_PARAMETER = 'deprecated/corresponding-parameter'; +export const CODE_DEPRECATED_REFERENCED_DECLARATION = 'deprecated/referenced-declaration'; + +export const annotationCallAnnotationShouldNotBeDeprecated = + (services: SafeDsServices) => (node: SdsAnnotationCall, accept: ValidationAcceptor) => { + const annotation = node.annotation?.ref; + if (!annotation) { + return; + } + + if (services.builtins.CoreAnnotations.isDeprecated(annotation)) { + accept('warning', `The called annotation '${annotation.name}' is deprecated.`, { + node, + property: 'annotation', + code: CODE_DEPRECATED_CALLED_ANNOTATION, + }); + } + }; diff --git a/src/language/validation/builtins/experimental.ts b/src/language/validation/builtins/experimental.ts new file mode 100644 index 000000000..20dcb6112 --- /dev/null +++ b/src/language/validation/builtins/experimental.ts @@ -0,0 +1,24 @@ +import { ValidationAcceptor } from 'langium'; +import { SdsAnnotationCall } from '../../generated/ast.js'; +import { SafeDsServices } from '../../safe-ds-module.js'; + +export const CODE_EXPERIMENTAL_ASSIGNED_RESULT = 'experimental/assigned-result'; +export const CODE_EXPERIMENTAL_CALLED_ANNOTATION = 'experimental/called-annotation'; +export const CODE_EXPERIMENTAL_CORRESPONDING_PARAMETER = 'experimental/corresponding-parameter'; +export const CODE_EXPERIMENTAL_REFERENCED_DECLARATION = 'experimental/referenced-declaration'; + +export const annotationCallAnnotationShouldNotBeExperimental = + (services: SafeDsServices) => (node: SdsAnnotationCall, accept: ValidationAcceptor) => { + const annotation = node.annotation?.ref; + if (!annotation) { + return; + } + + if (services.builtins.CoreAnnotations.isExperimental(annotation)) { + accept('warning', `The called annotation '${annotation.name}' is experimental.`, { + node, + property: 'annotation', + code: CODE_EXPERIMENTAL_CALLED_ANNOTATION, + }); + } + }; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 05420011d..213f03231 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -47,6 +47,8 @@ import { typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments } fro import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js'; import { parameterMustNotBeVariadicAndOptional } from './other/declarations/parameters.js'; import { referenceTargetMustNotBeAnnotationPipelineOrSchema } from './other/expressions/references.js'; +import { annotationCallAnnotationShouldNotBeDeprecated } from './builtins/deprecated.js'; +import {annotationCallAnnotationShouldNotBeExperimental} from "./builtins/experimental.js"; /** * Register custom validation checks. @@ -56,7 +58,11 @@ export const registerValidationChecks = function (services: SafeDsServices) { const checks: ValidationChecks = { SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees], SdsAnnotation: [annotationMustContainUniqueNames, annotationParameterListShouldNotBeEmpty], - SdsAnnotationCall: [annotationCallArgumentListShouldBeNeeded], + SdsAnnotationCall: [ + annotationCallAnnotationShouldNotBeDeprecated(services), + annotationCallAnnotationShouldNotBeExperimental(services), + annotationCallArgumentListShouldBeNeeded, + ], SdsArgumentList: [argumentListMustNotHavePositionalArgumentsAfterNamedArguments], SdsAttribute: [attributeMustHaveTypeHint], SdsBlockLambda: [blockLambdaMustContainUniqueNames], diff --git a/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub b/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub index 1ccdcd91d..126c0cc1e 100644 --- a/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub +++ b/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub @@ -39,8 +39,8 @@ enum AnnotationTarget { @Description("The annotation can be called on results.") Result - @Description("The annotation can be called on steps.") - Step + @Description("The annotation can be called on segments.") + Segment @Description("The annotation can be called on type parameters.") TypeParameter @@ -60,8 +60,7 @@ annotation Repeatable AnnotationTarget.Function, AnnotationTarget.Parameter, AnnotationTarget.Result, - AnnotationTarget.Step, - AnnotationTarget.TypeParameter, + AnnotationTarget.Segment, ) annotation Deprecated( @Description("What to use instead.") @@ -87,8 +86,7 @@ annotation Deprecated( AnnotationTarget.Function, AnnotationTarget.Parameter, AnnotationTarget.Result, - AnnotationTarget.Step, - AnnotationTarget.TypeParameter, + AnnotationTarget.Segment, ) annotation Experimental diff --git a/tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest new file mode 100644 index 000000000..8d3d3c6f0 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest @@ -0,0 +1,159 @@ +package tests.validation.builtins.deprecated.correspondingResult + +@Deprecated +annotation DeprecatedAnnotation +annotation ValidAnnotation + +@Deprecated +class DeprecatedClass +class ValidClass { + + @Deprecated + attr deprecatedAttribute: Int + attr validAttribute: Int +} + +@Deprecated +enum DeprecatedEnum +enum ValidEnum { + @Deprecated DeprecatedEnumVariant + ValidEnumVariant +} + +@Deprecated +fun deprecatedFunction() +fun validFunction( + @Deprecated deprecatedParameter: Int = 1, + validParameter: Int = 1 +) -> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) + +fun functionWithTypeParameters<@Deprecated DeprecatedTypeParameter, ValidTypeParameter>() where { + + /* + * Showing an error for constraints is pointless, since constraints must stay. + * Deprecation is only relevant for callers. + */ + + // $TEST$ no warning "The referenced declaration is deprecated." + »DeprecatedTypeParameter« sub Int, + // $TEST$ no warning "The referenced declaration is deprecated." + »ValidTypeParameter« sub Int +} + +@Deprecated segment deprecatedStep() {} +segment validStep() {} + +// $TEST$ warning "The called annotation is deprecated." +@»DeprecatedAnnotation« +// $TEST$ no warning "The called annotation is deprecated." +@»ValidAnnotation« +// $TEST$ no warning "The called annotation is deprecated." +@»Unresolved« +segment testStep1( + + // $TEST$ warning "The referenced declaration is deprecated." + @Deprecated deprecatedParameter: »DeprecatedClass« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + validParameter: »ValidClass« = 1, + + // $TEST$ warning "The referenced declaration is deprecated." + param3: »DeprecatedEnum« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + param4: »ValidEnum« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + param5: »Unresolved« = 1 +) +-> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) { + + // $TEST$ no warning "The referenced declaration is deprecated." + »deprecatedParameter«; + // $TEST$ no warning "The referenced declaration is deprecated." + »validParameter«; + + // $TEST$ warning "The referenced declaration is deprecated." + validParameter.»deprecatedAttribute«; + // $TEST$ no warning "The referenced declaration is deprecated." + validParameter.»validAttribute«; + + // $TEST$ warning "The referenced declaration is deprecated." + ValidEnum.»DeprecatedEnumVariant«; + // $TEST$ no warning "The referenced declaration is deprecated." + ValidEnum.»ValidEnumVariant«; + + // $TEST$ warning "The referenced declaration is deprecated." + »deprecatedFunction«(); + // $TEST$ no warning "The referenced declaration is deprecated." + »validFunction«(); + + validFunction( + // $TEST$ warning "The corresponding parameter is deprecated." + »deprecatedParameter = 1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »validParameter = 1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »unresolved = 1«, + ); + + validFunction( + // $TEST$ warning "The corresponding parameter is deprecated." + »1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »1«, + ); + + // $TEST$ warning "The referenced declaration is deprecated." + validFunction().»deprecatedResult«; + + // $TEST$ no warning "The referenced declaration is deprecated." + validFunction().»validResult«; + + // $TEST$ warning "The referenced declaration is deprecated." + »deprecatedStep«(); + // $TEST$ no warning "The referenced declaration is deprecated." + »validStep«(); + + // $TEST$ no warning "The referenced declaration is deprecated." + »unresolved«; + + /* + * Showing an error for yields is pointless, since we must yield something. + * Deprecation is only relevant for callers. + */ + + // $TEST$ no warning "The referenced declaration is deprecated." + yield »deprecatedResult« = 1; + // $TEST$ no warning "The referenced declaration is deprecated." + yield »validResult« = 1; +} + +segment testStep2() -> (result1: Int, result2: Int, result3: Int) { + // $TEST$ warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »val a«, »val b«, »val c« = validFunction(); + + // $TEST$ warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »yield result1«, »yield result2«, »yield result3« = validFunction(); + + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »_«, »_«, »_« = validFunction(); + + // $TEST$ no warning "The assigned result is deprecated." + »val d« = a; + // $TEST$ no warning "The assigned result is deprecated." + »val e« = b; + // $TEST$ no warning "The assigned result is deprecated." + »val f« = 1; +} diff --git a/tests/resources/validation/builtins/annotations/deprecated/called annotation/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/called annotation/main.sdstest new file mode 100644 index 000000000..c7ac37d19 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/deprecated/called annotation/main.sdstest @@ -0,0 +1,13 @@ +package tests.validation.builtins.deprecated.calledAnnotation + +@Deprecated +annotation DeprecatedAnnotation +annotation ValidAnnotation + +// $TEST$ warning "The called annotation 'DeprecatedAnnotation' is deprecated." +@»DeprecatedAnnotation« +// $TEST$ no warning r"The called annotation '\w*' is deprecated\." +@»ValidAnnotation« +// $TEST$ no warning r"The called annotation '\w*' is deprecated\." +@»Unresolved« +pipeline myPipeline {} diff --git a/tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest new file mode 100644 index 000000000..1b10307e5 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest @@ -0,0 +1,146 @@ +package tests.validation.builtins.deprecated.correspondingParameter + +@Deprecated +annotation DeprecatedAnnotation +annotation ValidAnnotation + +@Deprecated +class DeprecatedClass +class ValidClass { + + @Deprecated + attr deprecatedAttribute: Int + attr validAttribute: Int +} + +@Deprecated +enum DeprecatedEnum +enum ValidEnum { + @Deprecated DeprecatedEnumVariant + ValidEnumVariant +} + +@Deprecated +fun deprecatedFunction() +fun validFunction( + @Deprecated deprecatedParameter: Int = 1, + validParameter: Int = 1 +) -> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) + +@Deprecated segment deprecatedStep() {} +segment validStep() {} + +// $TEST$ warning "The called annotation is deprecated." +@»DeprecatedAnnotation« +// $TEST$ no warning "The called annotation is deprecated." +@»ValidAnnotation« +// $TEST$ no warning "The called annotation is deprecated." +@»Unresolved« +segment testStep1( + + // $TEST$ warning "The referenced declaration is deprecated." + @Deprecated deprecatedParameter: »DeprecatedClass« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + validParameter: »ValidClass« = 1, + + // $TEST$ warning "The referenced declaration is deprecated." + param3: »DeprecatedEnum« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + param4: »ValidEnum« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + param5: »Unresolved« = 1 +) +-> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) { + + // $TEST$ no warning "The referenced declaration is deprecated." + »deprecatedParameter«; + // $TEST$ no warning "The referenced declaration is deprecated." + »validParameter«; + + // $TEST$ warning "The referenced declaration is deprecated." + validParameter.»deprecatedAttribute«; + // $TEST$ no warning "The referenced declaration is deprecated." + validParameter.»validAttribute«; + + // $TEST$ warning "The referenced declaration is deprecated." + ValidEnum.»DeprecatedEnumVariant«; + // $TEST$ no warning "The referenced declaration is deprecated." + ValidEnum.»ValidEnumVariant«; + + // $TEST$ warning "The referenced declaration is deprecated." + »deprecatedFunction«(); + // $TEST$ no warning "The referenced declaration is deprecated." + »validFunction«(); + + validFunction( + // $TEST$ warning "The corresponding parameter is deprecated." + »deprecatedParameter = 1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »validParameter = 1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »unresolved = 1«, + ); + + validFunction( + // $TEST$ warning "The corresponding parameter is deprecated." + »1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »1«, + ); + + // $TEST$ warning "The referenced declaration is deprecated." + validFunction().»deprecatedResult«; + + // $TEST$ no warning "The referenced declaration is deprecated." + validFunction().»validResult«; + + // $TEST$ warning "The referenced declaration is deprecated." + »deprecatedStep«(); + // $TEST$ no warning "The referenced declaration is deprecated." + »validStep«(); + + // $TEST$ no warning "The referenced declaration is deprecated." + »unresolved«; + + /* + * Showing an error for yields is pointless, since we must yield something. + * Deprecation is only relevant for callers. + */ + + // $TEST$ no warning "The referenced declaration is deprecated." + yield »deprecatedResult« = 1; + // $TEST$ no warning "The referenced declaration is deprecated." + yield »validResult« = 1; +} + +segment testStep2() -> (result1: Int, result2: Int, result3: Int) { + // $TEST$ warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »val a«, »val b«, »val c« = validFunction(); + + // $TEST$ warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »yield result1«, »yield result2«, »yield result3« = validFunction(); + + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »_«, »_«, »_« = validFunction(); + + // $TEST$ no warning "The assigned result is deprecated." + »val d« = a; + // $TEST$ no warning "The assigned result is deprecated." + »val e« = b; + // $TEST$ no warning "The assigned result is deprecated." + »val f« = 1; +} diff --git a/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest new file mode 100644 index 000000000..c00592c7d --- /dev/null +++ b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest @@ -0,0 +1,159 @@ +package tests.validation.builtins.deprecated.referencedDeclaration + +@Deprecated +annotation DeprecatedAnnotation +annotation ValidAnnotation + +@Deprecated +class DeprecatedClass +class ValidClass { + + @Deprecated + attr deprecatedAttribute: Int + attr validAttribute: Int +} + +@Deprecated +enum DeprecatedEnum +enum ValidEnum { + @Deprecated DeprecatedEnumVariant + ValidEnumVariant +} + +@Deprecated +fun deprecatedFunction() +fun validFunction( + @Deprecated deprecatedParameter: Int = 1, + validParameter: Int = 1 +) -> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) + +fun functionWithTypeParameters<@Deprecated DeprecatedTypeParameter, ValidTypeParameter>() where { + + /* + * Showing an error for constraints is pointless, since constraints must stay. + * Deprecation is only relevant for callers. + */ + + // $TEST$ no warning "The referenced declaration is deprecated." + »DeprecatedTypeParameter« sub Int, + // $TEST$ no warning "The referenced declaration is deprecated." + »ValidTypeParameter« sub Int +} + +@Deprecated segment deprecatedStep() {} +segment validStep() {} + +// $TEST$ warning "The called annotation is deprecated." +@»DeprecatedAnnotation« +// $TEST$ no warning "The called annotation is deprecated." +@»ValidAnnotation« +// $TEST$ no warning "The called annotation is deprecated." +@»Unresolved« +segment testStep1( + + // $TEST$ warning "The referenced declaration is deprecated." + @Deprecated deprecatedParameter: »DeprecatedClass« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + validParameter: »ValidClass« = 1, + + // $TEST$ warning "The referenced declaration is deprecated." + param3: »DeprecatedEnum« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + param4: »ValidEnum« = 1, + // $TEST$ no warning "The referenced declaration is deprecated." + param5: »Unresolved« = 1 +) +-> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) { + + // $TEST$ no warning "The referenced declaration is deprecated." + »deprecatedParameter«; + // $TEST$ no warning "The referenced declaration is deprecated." + »validParameter«; + + // $TEST$ warning "The referenced declaration is deprecated." + validParameter.»deprecatedAttribute«; + // $TEST$ no warning "The referenced declaration is deprecated." + validParameter.»validAttribute«; + + // $TEST$ warning "The referenced declaration is deprecated." + ValidEnum.»DeprecatedEnumVariant«; + // $TEST$ no warning "The referenced declaration is deprecated." + ValidEnum.»ValidEnumVariant«; + + // $TEST$ warning "The referenced declaration is deprecated." + »deprecatedFunction«(); + // $TEST$ no warning "The referenced declaration is deprecated." + »validFunction«(); + + validFunction( + // $TEST$ warning "The corresponding parameter is deprecated." + »deprecatedParameter = 1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »validParameter = 1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »unresolved = 1«, + ); + + validFunction( + // $TEST$ warning "The corresponding parameter is deprecated." + »1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »1«, + // $TEST$ no warning "The corresponding parameter is deprecated." + »1«, + ); + + // $TEST$ warning "The referenced declaration is deprecated." + validFunction().»deprecatedResult«; + + // $TEST$ no warning "The referenced declaration is deprecated." + validFunction().»validResult«; + + // $TEST$ warning "The referenced declaration is deprecated." + »deprecatedStep«(); + // $TEST$ no warning "The referenced declaration is deprecated." + »validStep«(); + + // $TEST$ no warning "The referenced declaration is deprecated." + »unresolved«; + + /* + * Showing an error for yields is pointless, since we must yield something. + * Deprecation is only relevant for callers. + */ + + // $TEST$ no warning "The referenced declaration is deprecated." + yield »deprecatedResult« = 1; + // $TEST$ no warning "The referenced declaration is deprecated." + yield »validResult« = 1; +} + +segment testStep2() -> (result1: Int, result2: Int, result3: Int) { + // $TEST$ warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »val a«, »val b«, »val c« = validFunction(); + + // $TEST$ warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »yield result1«, »yield result2«, »yield result3« = validFunction(); + + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning "The assigned result is deprecated." + »_«, »_«, »_« = validFunction(); + + // $TEST$ no warning "The assigned result is deprecated." + »val d« = a; + // $TEST$ no warning "The assigned result is deprecated." + »val e« = b; + // $TEST$ no warning "The assigned result is deprecated." + »val f« = 1; +} diff --git a/tests/resources/validation/builtins/annotations/experimental/called annotation/main.sdstest b/tests/resources/validation/builtins/annotations/experimental/called annotation/main.sdstest new file mode 100644 index 000000000..3792aadae --- /dev/null +++ b/tests/resources/validation/builtins/annotations/experimental/called annotation/main.sdstest @@ -0,0 +1,13 @@ +package tests.validation.builtins.experimental.calledAnnotation + +@Experimental +annotation ExperimentalAnnotation +annotation ValidAnnotation + +// $TEST$ warning "The called annotation 'ExperimentalAnnotation' is experimental." +@»ExperimentalAnnotation« +// $TEST$ no warning r"The called annotation '\w*' is experimental\." +@»ValidAnnotation« +// $TEST$ no warning r"The called annotation '\w*' is experimental\." +@»Unresolved« +pipeline myPipeline {} From f44059d99a08308efb407b1a0f092acb5368c14f Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 19:00:05 +0200 Subject: [PATCH 02/13] feat: map assignee to assigned objects --- src/language/helpers/nodeProperties.ts | 21 +++ src/language/helpers/safe-ds-node-mapper.ts | 39 +++++ src/language/typing/model.ts | 4 + .../argumentToParameterOrUndefined.test.ts | 2 +- ...ssigneeToAssignedObjectOrUndefined.test.ts | 164 ++++++++++++++++++ .../callToCallableOrUndefined.test.ts | 6 +- ...ArgumentToTypeParameterOrUndefined.test.ts | 2 +- 7 files changed, 233 insertions(+), 5 deletions(-) create mode 100644 tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts diff --git a/src/language/helpers/nodeProperties.ts b/src/language/helpers/nodeProperties.ts index 9770f2ca7..f7a98aca5 100644 --- a/src/language/helpers/nodeProperties.ts +++ b/src/language/helpers/nodeProperties.ts @@ -1,7 +1,9 @@ import { isSdsAssignment, isSdsAttribute, + isSdsBlockLambda, isSdsBlockLambdaResult, + isSdsCallableType, isSdsClass, isSdsDeclaration, isSdsEnum, @@ -13,6 +15,7 @@ import { isSdsSegment, isSdsTypeParameterList, SdsAbstractCall, + SdsAbstractResult, SdsAnnotatedObject, SdsAnnotationCall, SdsArgument, @@ -84,6 +87,24 @@ export const isStatic = (node: SdsClassMember): boolean => { // Accessors for list elements // ------------------------------------------------------------------------------------------------- +export const abstractResultsOrEmpty = (node: SdsCallable | undefined): SdsAbstractResult[] => { + if (!node) { + return []; + } + + if (isSdsBlockLambda(node)) { + return blockLambdaResultsOrEmpty(node); + } else if (isSdsCallableType(node)) { + return resultsOrEmpty(node.resultList); + } else if (isSdsFunction(node)) { + return resultsOrEmpty(node.resultList); + } else if (isSdsSegment(node)) { + return resultsOrEmpty(node.resultList); + } /* c8 ignore start */ else { + return []; + } /* c8 ignore stop */ +}; + export const annotationCallsOrEmpty = (node: SdsAnnotatedObject | undefined): SdsAnnotationCall[] => { if (!node) { /* c8 ignore next 2 */ diff --git a/src/language/helpers/safe-ds-node-mapper.ts b/src/language/helpers/safe-ds-node-mapper.ts index e4d68a9b5..f3dcbc21b 100644 --- a/src/language/helpers/safe-ds-node-mapper.ts +++ b/src/language/helpers/safe-ds-node-mapper.ts @@ -3,6 +3,7 @@ import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js'; import { isSdsAbstractCall, isSdsAnnotationCall, + isSdsAssignment, isSdsBlock, isSdsCall, isSdsCallable, @@ -12,8 +13,11 @@ import { isSdsType, isSdsYield, SdsAbstractCall, + SdsAbstractResult, SdsArgument, + SdsAssignee, SdsCallable, + SdsExpression, SdsParameter, SdsPlaceholder, SdsReference, @@ -25,6 +29,7 @@ import { import { CallableType, StaticType } from '../typing/model.js'; import { findLocalReferences, getContainerOfType } from 'langium'; import { + abstractResultsOrEmpty, argumentsOrEmpty, isNamedArgument, isNamedTypeArgument, @@ -81,6 +86,40 @@ export class SafeDsNodeMapper { return undefined; } + /** + * Returns the result, block lambda result, or expression that is assigned to the given assignee. If nothing is + * assigned, `undefined` is returned. + */ + assigneeToAssignedObjectOrUndefined(node: SdsAssignee | undefined): SdsAbstractResult | SdsExpression | undefined { + if (!node) { + return undefined; + } + + const containingAssignment = getContainerOfType(node, isSdsAssignment); + /* c8 ignore start */ + if (!containingAssignment) { + return undefined; + } + /* c8 ignore stop */ + + const assigneePosition = node.$containerIndex ?? -1; + const expression = containingAssignment.expression; + + // If the RHS is not a call, the first assignee gets the entire RHS + if (!isSdsCall(expression)) { + if (assigneePosition === 0) { + return expression; + } else { + return undefined; + } + } + + // If the RHS is a call, the assignee gets the corresponding result + const callable = this.callToCallableOrUndefined(expression); + const abstractResults = abstractResultsOrEmpty(callable); + return abstractResults[assigneePosition]; + } + /** * Returns the callable that is called by the given call. If no callable can be found, returns undefined. */ diff --git a/src/language/typing/model.ts b/src/language/typing/model.ts index 22c10882c..a7df36b50 100644 --- a/src/language/typing/model.ts +++ b/src/language/typing/model.ts @@ -106,6 +106,10 @@ export class NamedTupleType extends Type { return this.entries[position]?.type; } + get length (): number { + return this.entries.length; + } + override copyWithNullability(_isNullable: boolean): NamedTupleType { return this; } diff --git a/tests/language/helpers/safe-ds-node-mapper/argumentToParameterOrUndefined.test.ts b/tests/language/helpers/safe-ds-node-mapper/argumentToParameterOrUndefined.test.ts index f65f6be0b..844a1315d 100644 --- a/tests/language/helpers/safe-ds-node-mapper/argumentToParameterOrUndefined.test.ts +++ b/tests/language/helpers/safe-ds-node-mapper/argumentToParameterOrUndefined.test.ts @@ -16,7 +16,7 @@ describe('SafeDsNodeMapper', () => { describe('argumentToParameterOrUndefined', () => { it('should return undefined if passed undefined', () => { - expect(nodeMapper.argumentToParameterOrUndefined(undefined)).toBeUndefined(); + expect(nodeMapper.argumentToParameterOrUndefined(undefined)?.$type).toBeUndefined(); }); describe('named argument', () => { diff --git a/tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts b/tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts new file mode 100644 index 000000000..9b136d290 --- /dev/null +++ b/tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts @@ -0,0 +1,164 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { createSafeDsServices } from '../../../../src/language/safe-ds-module.js'; +import { clearDocuments } from 'langium/test'; +import { EmptyFileSystem } from 'langium'; +import { getNodeOfType } from '../../../helpers/nodeFinder.js'; +import { + isSdsAbstractResult, + isSdsAssignment, + isSdsPlaceholder, + SdsAssignee, +} from '../../../../src/language/generated/ast.js'; +import { assigneesOrEmpty } from '../../../../src/language/helpers/nodeProperties.js'; +import { NodeFileSystem } from 'langium/node'; + +const services = createSafeDsServices(NodeFileSystem).SafeDs; +const nodeMapper = services.helpers.NodeMapper; + +describe('SafeDsNodeMapper', () => { + beforeEach(async () => { + // Load the builtin library + await services.shared.workspace.WorkspaceManager.initializeWorkspace([]); + }); + + afterEach(async () => { + await clearDocuments(services); + }); + + describe('assigneeToAssignedObjectOrUndefined', () => { + it('should return an empty list if passed undefined', async () => { + expect(nodeMapper.assigneeToAssignedObjectOrUndefined(undefined)?.$type).toBeUndefined(); + }); + + it.each([ + { + name: "no value", + code: ` + fun f() + + segment mySegment() { + val a = f(); + }; + `, + }, + { + name: "only one value", + code: ` + segment mySegment() { + _, val a = 1; + }; + `, + }, + + { + name: 'unresolved receiver of call', + code: ` + segment mySegment() { + val a = unresolved(); + }; + `, + }, + ])('should return undefined if nothing is assigned ($name)', async ({ code }) => { + const placeholder = await getNodeOfType(services, code, isSdsPlaceholder); + expect(nodeMapper.assigneeToAssignedObjectOrUndefined(placeholder)?.$type).toBeUndefined(); + }); + + it('should return the entire RHS of an assignment if it is not a call (constant)', async () => { + const code = ` + segment mySegment() { + val a = 1; + }; + `; + + const placeholder = await getNodeOfType(services, code, isSdsPlaceholder); + expect(nodeMapper.assigneeToAssignedObjectOrUndefined(placeholder)?.$type).toBe('SdsInt'); + }); + + it('should return the entire RHS of an assignment if it is not a call (unresolved reference)', async () => { + const code = ` + segment mySegment() { + val a = unresolved; + }; + `; + + const placeholder = await getNodeOfType(services, code, isSdsPlaceholder); + expect(nodeMapper.assigneeToAssignedObjectOrUndefined(placeholder)?.$type).toBe('SdsReference'); + }); + + it.each([ + { + name: 'block lambda', + code: ` + segment mySegment() { + val f = () { + yield r1 = 1; + yield r2 = 2; + }; + + val a, val b = f(); + }; + `, + expected: ["r1", "r2"], + index: 3, + }, + { + name: 'callable type', + code: ` + segment mySegment(f: () -> (r1: Int, r2: Int)) { + val a, val b = f(); + }; + `, + expected: ["r1", "r2"], + }, + { + name: 'function (one result)', + code: ` + fun f() -> (r1: Int) + + segment mySegment() { + val a = f(); + }; + `, + expected: ["r1"], + }, + { + name: 'function (multiple results)', + code: ` + fun f() -> (r1: Int, r2: Int) + + segment mySegment() { + val a, val b = f(); + }; + `, + expected: ["r1", "r2"], + }, + { + name: 'segment', + code: ` + segment s() -> (r1: Int, r2: Int) + + segment mySegment() { + val a, val b = s(); + }; + `, + expected: ["r1", "r2"], + }, + ])( + 'should return the corresponding result if the RHS is a call of a $name', + async ({ code, expected, index = 0 }) => { + const assignment = await getNodeOfType(services, code, isSdsAssignment, index); + const abstractResultNames = assigneesOrEmpty(assignment).map(abstractResultNameOrNull); + expect(abstractResultNames).toStrictEqual(expected); + }, + ); + + const abstractResultNameOrNull = (node: SdsAssignee): string | undefined => { + const assignedObject = nodeMapper.assigneeToAssignedObjectOrUndefined(node); + if (isSdsAbstractResult(assignedObject)) { + return assignedObject.name; + } else { + return undefined; + } + }; + }); +}); diff --git a/tests/language/helpers/safe-ds-node-mapper/callToCallableOrUndefined.test.ts b/tests/language/helpers/safe-ds-node-mapper/callToCallableOrUndefined.test.ts index b973f8fd5..48da24a42 100644 --- a/tests/language/helpers/safe-ds-node-mapper/callToCallableOrUndefined.test.ts +++ b/tests/language/helpers/safe-ds-node-mapper/callToCallableOrUndefined.test.ts @@ -30,7 +30,7 @@ describe('SafeDsNodeMapper', () => { `; const call = await getNodeOfType(services, code, isSdsAbstractCall); - expect(nodeMapper.callToCallableOrUndefined(call)).toBeUndefined(); + expect(nodeMapper.callToCallableOrUndefined(call)?.$type).toBeUndefined(); }); it('should return the called annotation', async () => { @@ -59,7 +59,7 @@ describe('SafeDsNodeMapper', () => { `; const call = await getNodeOfType(services, code, isSdsAbstractCall); - expect(nodeMapper.callToCallableOrUndefined(call)).toBeUndefined(); + expect(nodeMapper.callToCallableOrUndefined(call)?.$type).toBeUndefined(); }); it('should return undefined if receiver is not callable', async () => { @@ -72,7 +72,7 @@ describe('SafeDsNodeMapper', () => { `; const call = await getNodeOfType(services, code, isSdsAbstractCall); - expect(nodeMapper.callToCallableOrUndefined(call)).toBeUndefined(); + expect(nodeMapper.callToCallableOrUndefined(call)?.$type).toBeUndefined(); }); it('should return the called annotation', async () => { diff --git a/tests/language/helpers/safe-ds-node-mapper/typeArgumentToTypeParameterOrUndefined.test.ts b/tests/language/helpers/safe-ds-node-mapper/typeArgumentToTypeParameterOrUndefined.test.ts index 36a7eb39f..a5032f248 100644 --- a/tests/language/helpers/safe-ds-node-mapper/typeArgumentToTypeParameterOrUndefined.test.ts +++ b/tests/language/helpers/safe-ds-node-mapper/typeArgumentToTypeParameterOrUndefined.test.ts @@ -16,7 +16,7 @@ describe('SafeDsNodeMapper', () => { describe('typeArgumentToTypeParameterOrUndefined', () => { it('should return undefined if passed undefined', () => { - expect(nodeMapper.typeArgumentToTypeParameterOrUndefined(undefined)).toBeUndefined(); + expect(nodeMapper.typeArgumentToTypeParameterOrUndefined(undefined)?.$type).toBeUndefined(); }); describe('named type argument', () => { From 9f30d1ebd2d3d410576061c901b979c388f742da Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 19:14:57 +0200 Subject: [PATCH 03/13] feat: warn about deprecated/experimental assigned results --- ...-annotations.ts => safe-ds-annotations.ts} | 2 +- ...-ds-core-classes.ts => safe-ds-classes.ts} | 2 +- src/language/safe-ds-module.ts | 12 +- src/language/typing/safe-ds-type-computer.ts | 6 +- .../validation/builtins/deprecated.ts | 24 ++- .../validation/builtins/experimental.ts | 24 ++- src/language/validation/safe-ds-validator.ts | 44 +++-- .../safeds/lang/coreAnnotations.sdsstub | 6 +- .../deprecated/assigned result/main.sdstest | 165 ++---------------- .../experimental/assigned result/main.sdstest | 30 ++++ 10 files changed, 134 insertions(+), 181 deletions(-) rename src/language/builtins/{safe-ds-core-annotations.ts => safe-ds-annotations.ts} (98%) rename src/language/builtins/{safe-ds-core-classes.ts => safe-ds-classes.ts} (98%) create mode 100644 tests/resources/validation/builtins/annotations/experimental/assigned result/main.sdstest diff --git a/src/language/builtins/safe-ds-core-annotations.ts b/src/language/builtins/safe-ds-annotations.ts similarity index 98% rename from src/language/builtins/safe-ds-core-annotations.ts rename to src/language/builtins/safe-ds-annotations.ts index 6e3441ecc..10b52d345 100644 --- a/src/language/builtins/safe-ds-core-annotations.ts +++ b/src/language/builtins/safe-ds-annotations.ts @@ -6,7 +6,7 @@ import { annotationCallsOrEmpty, moduleMembersOrEmpty } from '../helpers/nodePro const CORE_ANNOTATIONS_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreAnnotations.sdsstub'); -export class SafeDsCoreAnnotations { +export class SafeDsAnnotations { private readonly langiumDocuments: LangiumDocuments; private readonly cache: Map = new Map(); diff --git a/src/language/builtins/safe-ds-core-classes.ts b/src/language/builtins/safe-ds-classes.ts similarity index 98% rename from src/language/builtins/safe-ds-core-classes.ts rename to src/language/builtins/safe-ds-classes.ts index d30366f81..cf5831e5a 100644 --- a/src/language/builtins/safe-ds-core-classes.ts +++ b/src/language/builtins/safe-ds-classes.ts @@ -6,7 +6,7 @@ import { moduleMembersOrEmpty } from '../helpers/nodeProperties.js'; const CORE_CLASSES_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreClasses.sdsstub'); -export class SafeDsCoreClasses { +export class SafeDsClasses { private readonly langiumDocuments: LangiumDocuments; private readonly cache: Map = new Map(); diff --git a/src/language/safe-ds-module.ts b/src/language/safe-ds-module.ts index 69da654e1..d8cd13302 100644 --- a/src/language/safe-ds-module.ts +++ b/src/language/safe-ds-module.ts @@ -17,18 +17,18 @@ import { SafeDsScopeComputation } from './scoping/safe-ds-scope-computation.js'; import { SafeDsScopeProvider } from './scoping/safe-ds-scope-provider.js'; import { SafeDsValueConverter } from './grammar/safe-ds-value-converter.js'; import { SafeDsTypeComputer } from './typing/safe-ds-type-computer.js'; -import { SafeDsCoreClasses } from './builtins/safe-ds-core-classes.js'; +import { SafeDsClasses } from './builtins/safe-ds-classes.js'; import { SafeDsPackageManager } from './workspace/safe-ds-package-manager.js'; import { SafeDsNodeMapper } from './helpers/safe-ds-node-mapper.js'; -import { SafeDsCoreAnnotations } from './builtins/safe-ds-core-annotations.js'; +import { SafeDsAnnotations } from './builtins/safe-ds-annotations.js'; /** * Declaration of custom services - add your own service classes here. */ export type SafeDsAddedServices = { builtins: { - CoreAnnotations: SafeDsCoreAnnotations; - CoreClasses: SafeDsCoreClasses; + Annotations: SafeDsAnnotations; + Classes: SafeDsClasses; }; helpers: { NodeMapper: SafeDsNodeMapper; @@ -54,8 +54,8 @@ export type SafeDsServices = LangiumServices & SafeDsAddedServices; */ export const SafeDsModule: Module = { builtins: { - CoreAnnotations: (services) => new SafeDsCoreAnnotations(services), - CoreClasses: (services) => new SafeDsCoreClasses(services), + Annotations: (services) => new SafeDsAnnotations(services), + Classes: (services) => new SafeDsClasses(services), }, helpers: { NodeMapper: (services) => new SafeDsNodeMapper(services), diff --git a/src/language/typing/safe-ds-type-computer.ts b/src/language/typing/safe-ds-type-computer.ts index 996be7aaa..79fea1b3f 100644 --- a/src/language/typing/safe-ds-type-computer.ts +++ b/src/language/typing/safe-ds-type-computer.ts @@ -1,6 +1,6 @@ import { AstNode, AstNodeLocator, getContainerOfType, getDocument, WorkspaceCache } from 'langium'; import { SafeDsServices } from '../safe-ds-module.js'; -import { SafeDsCoreClasses } from '../builtins/safe-ds-core-classes.js'; +import { SafeDsClasses } from '../builtins/safe-ds-classes.js'; import { CallableType, ClassType, @@ -83,14 +83,14 @@ import { export class SafeDsTypeComputer { private readonly astNodeLocator: AstNodeLocator; - private readonly coreClasses: SafeDsCoreClasses; + private readonly coreClasses: SafeDsClasses; private readonly nodeMapper: SafeDsNodeMapper; readonly typeCache: WorkspaceCache; constructor(readonly services: SafeDsServices) { this.astNodeLocator = services.workspace.AstNodeLocator; - this.coreClasses = services.builtins.CoreClasses; + this.coreClasses = services.builtins.Classes; this.nodeMapper = services.helpers.NodeMapper; this.typeCache = new WorkspaceCache(services.shared); diff --git a/src/language/validation/builtins/deprecated.ts b/src/language/validation/builtins/deprecated.ts index 2373ed444..edf4eaf3b 100644 --- a/src/language/validation/builtins/deprecated.ts +++ b/src/language/validation/builtins/deprecated.ts @@ -1,5 +1,5 @@ import { ValidationAcceptor } from 'langium'; -import { SdsAnnotationCall } from '../../generated/ast.js'; +import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsAssignee } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; export const CODE_DEPRECATED_ASSIGNED_RESULT = 'deprecated/assigned-result'; @@ -7,6 +7,26 @@ export const CODE_DEPRECATED_CALLED_ANNOTATION = 'deprecated/called-annotation'; export const CODE_DEPRECATED_CORRESPONDING_PARAMETER = 'deprecated/corresponding-parameter'; export const CODE_DEPRECATED_REFERENCED_DECLARATION = 'deprecated/referenced-declaration'; +export const assigneeAssignedResultShouldNotBeDeprecated = + (services: SafeDsServices) => (node: SdsAssignee, accept: ValidationAcceptor) => { + if (isSdsWildcard(node)) { + return; + } + + const assignedObject = services.helpers.NodeMapper.assigneeToAssignedObjectOrUndefined(node); + if (!isSdsResult(assignedObject)) { + return; + } + + if (services.builtins.Annotations.isDeprecated(assignedObject)) { + accept('warning', `The assigned result '${assignedObject.name}' is deprecated.`, { + node, + property: 'assignedObject', + code: CODE_DEPRECATED_ASSIGNED_RESULT, + }); + } + }; + export const annotationCallAnnotationShouldNotBeDeprecated = (services: SafeDsServices) => (node: SdsAnnotationCall, accept: ValidationAcceptor) => { const annotation = node.annotation?.ref; @@ -14,7 +34,7 @@ export const annotationCallAnnotationShouldNotBeDeprecated = return; } - if (services.builtins.CoreAnnotations.isDeprecated(annotation)) { + if (services.builtins.Annotations.isDeprecated(annotation)) { accept('warning', `The called annotation '${annotation.name}' is deprecated.`, { node, property: 'annotation', diff --git a/src/language/validation/builtins/experimental.ts b/src/language/validation/builtins/experimental.ts index 20dcb6112..f3d94e320 100644 --- a/src/language/validation/builtins/experimental.ts +++ b/src/language/validation/builtins/experimental.ts @@ -1,5 +1,5 @@ import { ValidationAcceptor } from 'langium'; -import { SdsAnnotationCall } from '../../generated/ast.js'; +import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsAssignee } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; export const CODE_EXPERIMENTAL_ASSIGNED_RESULT = 'experimental/assigned-result'; @@ -7,6 +7,26 @@ export const CODE_EXPERIMENTAL_CALLED_ANNOTATION = 'experimental/called-annotati export const CODE_EXPERIMENTAL_CORRESPONDING_PARAMETER = 'experimental/corresponding-parameter'; export const CODE_EXPERIMENTAL_REFERENCED_DECLARATION = 'experimental/referenced-declaration'; +export const assigneeAssignedResultShouldNotBeExperimental = + (services: SafeDsServices) => (node: SdsAssignee, accept: ValidationAcceptor) => { + if (isSdsWildcard(node)) { + return; + } + + const assignedObject = services.helpers.NodeMapper.assigneeToAssignedObjectOrUndefined(node); + if (!isSdsResult(assignedObject)) { + return; + } + + if (services.builtins.Annotations.isExperimental(assignedObject)) { + accept('warning', `The assigned result '${assignedObject.name}' is experimental.`, { + node, + property: 'assignedObject', + code: CODE_EXPERIMENTAL_ASSIGNED_RESULT, + }); + } + }; + export const annotationCallAnnotationShouldNotBeExperimental = (services: SafeDsServices) => (node: SdsAnnotationCall, accept: ValidationAcceptor) => { const annotation = node.annotation?.ref; @@ -14,7 +34,7 @@ export const annotationCallAnnotationShouldNotBeExperimental = return; } - if (services.builtins.CoreAnnotations.isExperimental(annotation)) { + if (services.builtins.Annotations.isExperimental(annotation)) { accept('warning', `The called annotation '${annotation.name}' is experimental.`, { node, property: 'annotation', diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 213f03231..b2986b284 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -1,6 +1,6 @@ -import { ValidationChecks } from 'langium'; -import { SafeDsAstType } from '../generated/ast.js'; -import type { SafeDsServices } from '../safe-ds-module.js'; +import {ValidationChecks} from 'langium'; +import {SafeDsAstType} from '../generated/ast.js'; +import type {SafeDsServices} from '../safe-ds-module.js'; import { annotationMustContainUniqueNames, blockLambdaMustContainUniqueNames, @@ -31,24 +31,32 @@ import { typeParameterListShouldNotBeEmpty, unionTypeShouldNotHaveASingularTypeArgument, } from './style.js'; -import { templateStringMustHaveExpressionBetweenTwoStringParts } from './other/expressions/templateStrings.js'; -import { yieldMustNotBeUsedInPipeline } from './other/statements/assignments.js'; -import { attributeMustHaveTypeHint, parameterMustHaveTypeHint, resultMustHaveTypeHint } from './types.js'; -import { moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage } from './other/modules.js'; -import { typeParameterConstraintLeftOperandMustBeOwnTypeParameter } from './other/declarations/typeParameterConstraints.js'; +import {templateStringMustHaveExpressionBetweenTwoStringParts} from './other/expressions/templateStrings.js'; +import {yieldMustNotBeUsedInPipeline} from './other/statements/assignments.js'; +import {attributeMustHaveTypeHint, parameterMustHaveTypeHint, resultMustHaveTypeHint} from './types.js'; +import {moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage} from './other/modules.js'; +import { + typeParameterConstraintLeftOperandMustBeOwnTypeParameter +} from './other/declarations/typeParameterConstraints.js'; import { parameterListMustNotHaveOptionalAndVariadicParameters, parameterListMustNotHaveRequiredParametersAfterOptionalParameters, parameterListVariadicParameterMustBeLast, } from './other/declarations/parameterLists.js'; -import { unionTypeMustHaveTypeArguments } from './other/types/unionTypes.js'; -import { callableTypeMustNotHaveOptionalParameters } from './other/types/callableTypes.js'; -import { typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/types/typeArgumentLists.js'; -import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js'; -import { parameterMustNotBeVariadicAndOptional } from './other/declarations/parameters.js'; -import { referenceTargetMustNotBeAnnotationPipelineOrSchema } from './other/expressions/references.js'; -import { annotationCallAnnotationShouldNotBeDeprecated } from './builtins/deprecated.js'; -import {annotationCallAnnotationShouldNotBeExperimental} from "./builtins/experimental.js"; +import {unionTypeMustHaveTypeArguments} from './other/types/unionTypes.js'; +import {callableTypeMustNotHaveOptionalParameters} from './other/types/callableTypes.js'; +import {typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments} from './other/types/typeArgumentLists.js'; +import {argumentListMustNotHavePositionalArgumentsAfterNamedArguments} from './other/argumentLists.js'; +import {parameterMustNotBeVariadicAndOptional} from './other/declarations/parameters.js'; +import {referenceTargetMustNotBeAnnotationPipelineOrSchema} from './other/expressions/references.js'; +import { + annotationCallAnnotationShouldNotBeDeprecated, + assigneeAssignedResultShouldNotBeDeprecated +} from './builtins/deprecated.js'; +import { + annotationCallAnnotationShouldNotBeExperimental, + assigneeAssignedResultShouldNotBeExperimental +} from "./builtins/experimental.js"; /** * Register custom validation checks. @@ -56,6 +64,10 @@ import {annotationCallAnnotationShouldNotBeExperimental} from "./builtins/experi export const registerValidationChecks = function (services: SafeDsServices) { const registry = services.validation.ValidationRegistry; const checks: ValidationChecks = { + SdsAssignee: [ + assigneeAssignedResultShouldNotBeDeprecated(services), + assigneeAssignedResultShouldNotBeExperimental(services), + ], SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees], SdsAnnotation: [annotationMustContainUniqueNames, annotationParameterListShouldNotBeEmpty], SdsAnnotationCall: [ diff --git a/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub b/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub index 126c0cc1e..c634b7e1a 100644 --- a/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub +++ b/src/resources/builtins/safeds/lang/coreAnnotations.sdsstub @@ -18,9 +18,6 @@ enum AnnotationTarget { @Description("The annotation can be called on classes.") Class - @Description("The annotation can be called on compilation units (i.e. files).") - CompilationUnit - @Description("The annotation can be called on enums.") Enum @@ -30,6 +27,9 @@ enum AnnotationTarget { @Description("The annotation can be called on functions.") Function + @Description("The annotation can be called on modules (i.e. files).") + Module + @Description("The annotation can be called on parameters.") Parameter diff --git a/tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest index 8d3d3c6f0..f4cc1bd1d 100644 --- a/tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest +++ b/tests/resources/validation/builtins/annotations/deprecated/assigned result/main.sdstest @@ -1,159 +1,30 @@ -package tests.validation.builtins.deprecated.correspondingResult +package tests.validation.builtins.deprecated.assignedResult -@Deprecated -annotation DeprecatedAnnotation -annotation ValidAnnotation - -@Deprecated -class DeprecatedClass -class ValidClass { - - @Deprecated - attr deprecatedAttribute: Int - attr validAttribute: Int -} - -@Deprecated -enum DeprecatedEnum -enum ValidEnum { - @Deprecated DeprecatedEnumVariant - ValidEnumVariant -} - -@Deprecated -fun deprecatedFunction() -fun validFunction( - @Deprecated deprecatedParameter: Int = 1, - validParameter: Int = 1 -) -> ( +fun myFunction() -> ( @Deprecated deprecatedResult: Int, validResult: Int ) -fun functionWithTypeParameters<@Deprecated DeprecatedTypeParameter, ValidTypeParameter>() where { - - /* - * Showing an error for constraints is pointless, since constraints must stay. - * Deprecation is only relevant for callers. - */ - - // $TEST$ no warning "The referenced declaration is deprecated." - »DeprecatedTypeParameter« sub Int, - // $TEST$ no warning "The referenced declaration is deprecated." - »ValidTypeParameter« sub Int -} - -@Deprecated segment deprecatedStep() {} -segment validStep() {} - -// $TEST$ warning "The called annotation is deprecated." -@»DeprecatedAnnotation« -// $TEST$ no warning "The called annotation is deprecated." -@»ValidAnnotation« -// $TEST$ no warning "The called annotation is deprecated." -@»Unresolved« -segment testStep1( - - // $TEST$ warning "The referenced declaration is deprecated." - @Deprecated deprecatedParameter: »DeprecatedClass« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - validParameter: »ValidClass« = 1, - - // $TEST$ warning "The referenced declaration is deprecated." - param3: »DeprecatedEnum« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - param4: »ValidEnum« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - param5: »Unresolved« = 1 -) --> ( - @Deprecated deprecatedResult: Int, - validResult: Int -) { - - // $TEST$ no warning "The referenced declaration is deprecated." - »deprecatedParameter«; - // $TEST$ no warning "The referenced declaration is deprecated." - »validParameter«; - - // $TEST$ warning "The referenced declaration is deprecated." - validParameter.»deprecatedAttribute«; - // $TEST$ no warning "The referenced declaration is deprecated." - validParameter.»validAttribute«; - - // $TEST$ warning "The referenced declaration is deprecated." - ValidEnum.»DeprecatedEnumVariant«; - // $TEST$ no warning "The referenced declaration is deprecated." - ValidEnum.»ValidEnumVariant«; - - // $TEST$ warning "The referenced declaration is deprecated." - »deprecatedFunction«(); - // $TEST$ no warning "The referenced declaration is deprecated." - »validFunction«(); - - validFunction( - // $TEST$ warning "The corresponding parameter is deprecated." - »deprecatedParameter = 1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »validParameter = 1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »unresolved = 1«, - ); - - validFunction( - // $TEST$ warning "The corresponding parameter is deprecated." - »1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »1«, - ); - - // $TEST$ warning "The referenced declaration is deprecated." - validFunction().»deprecatedResult«; - - // $TEST$ no warning "The referenced declaration is deprecated." - validFunction().»validResult«; - - // $TEST$ warning "The referenced declaration is deprecated." - »deprecatedStep«(); - // $TEST$ no warning "The referenced declaration is deprecated." - »validStep«(); - - // $TEST$ no warning "The referenced declaration is deprecated." - »unresolved«; - - /* - * Showing an error for yields is pointless, since we must yield something. - * Deprecation is only relevant for callers. - */ - - // $TEST$ no warning "The referenced declaration is deprecated." - yield »deprecatedResult« = 1; - // $TEST$ no warning "The referenced declaration is deprecated." - yield »validResult« = 1; -} - -segment testStep2() -> (result1: Int, result2: Int, result3: Int) { - // $TEST$ warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »val a«, »val b«, »val c« = validFunction(); +segment mySegment() -> (result1: Int, result2: Int, result3: Int) { + // $TEST$ warning "The assigned result 'deprecatedResult' is deprecated." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." + »val a«, »val b«, »val c« = myFunction(); - // $TEST$ warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »yield result1«, »yield result2«, »yield result3« = validFunction(); + // $TEST$ warning "The assigned result 'deprecatedResult' is deprecated." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." + »yield result1«, »yield result2«, »yield result3« = myFunction(); - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »_«, »_«, »_« = validFunction(); + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." + »_«, »_«, »_« = myFunction(); - // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." »val d« = a; - // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." »val e« = b; - // $TEST$ no warning "The assigned result is deprecated." + // $TEST$ no warning r"The assigned result '\w*' is deprecated\." »val f« = 1; } diff --git a/tests/resources/validation/builtins/annotations/experimental/assigned result/main.sdstest b/tests/resources/validation/builtins/annotations/experimental/assigned result/main.sdstest new file mode 100644 index 000000000..97c2171c6 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/experimental/assigned result/main.sdstest @@ -0,0 +1,30 @@ +package tests.validation.builtins.experimental.assignedResult + +fun myFunction() -> ( + @Experimental experimentalResult: Int, + validResult: Int +) + +segment mySegment() -> (result1: Int, result2: Int, result3: Int) { + // $TEST$ warning "The assigned result 'experimentalResult' is experimental." + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + »val a«, »val b«, »val c« = myFunction(); + + // $TEST$ warning "The assigned result 'experimentalResult' is experimental." + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + »yield result1«, »yield result2«, »yield result3« = myFunction(); + + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + »_«, »_«, »_« = myFunction(); + + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + »val d« = a; + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + »val e« = b; + // $TEST$ no warning r"The assigned result '\w*' is experimental\." + »val f« = 1; +} From 533e84f579c2a7773a00549c9306e301a21c0d59 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 20:39:51 +0200 Subject: [PATCH 04/13] feat: warning if parameter is deprecated/experimental --- .../validation/builtins/deprecated.ts | 18 ++- .../validation/builtins/experimental.ts | 18 ++- src/language/validation/safe-ds-validator.ts | 14 +- .../corresponding parameter/main.sdstest | 140 ++---------------- .../corresponding parameter/main.sdstest | 26 ++++ 5 files changed, 77 insertions(+), 139 deletions(-) create mode 100644 tests/resources/validation/builtins/annotations/experimental/corresponding parameter/main.sdstest diff --git a/src/language/validation/builtins/deprecated.ts b/src/language/validation/builtins/deprecated.ts index edf4eaf3b..42d2b83d7 100644 --- a/src/language/validation/builtins/deprecated.ts +++ b/src/language/validation/builtins/deprecated.ts @@ -1,5 +1,5 @@ import { ValidationAcceptor } from 'langium'; -import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsAssignee } from '../../generated/ast.js'; +import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsArgument, SdsAssignee } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; export const CODE_DEPRECATED_ASSIGNED_RESULT = 'deprecated/assigned-result'; @@ -21,7 +21,6 @@ export const assigneeAssignedResultShouldNotBeDeprecated = if (services.builtins.Annotations.isDeprecated(assignedObject)) { accept('warning', `The assigned result '${assignedObject.name}' is deprecated.`, { node, - property: 'assignedObject', code: CODE_DEPRECATED_ASSIGNED_RESULT, }); } @@ -42,3 +41,18 @@ export const annotationCallAnnotationShouldNotBeDeprecated = }); } }; + +export const argumentCorrespondingParameterShouldNotBeDeprecated = + (services: SafeDsServices) => (node: SdsArgument, accept: ValidationAcceptor) => { + const parameter = services.helpers.NodeMapper.argumentToParameterOrUndefined(node); + if (!parameter) { + return; + } + + if (services.builtins.Annotations.isDeprecated(parameter)) { + accept('warning', `The corresponding parameter '${parameter.name}' is deprecated.`, { + node, + code: CODE_DEPRECATED_CALLED_ANNOTATION, + }); + } + }; diff --git a/src/language/validation/builtins/experimental.ts b/src/language/validation/builtins/experimental.ts index f3d94e320..122fd5c0a 100644 --- a/src/language/validation/builtins/experimental.ts +++ b/src/language/validation/builtins/experimental.ts @@ -1,5 +1,5 @@ import { ValidationAcceptor } from 'langium'; -import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsAssignee } from '../../generated/ast.js'; +import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsArgument, SdsAssignee } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; export const CODE_EXPERIMENTAL_ASSIGNED_RESULT = 'experimental/assigned-result'; @@ -21,7 +21,6 @@ export const assigneeAssignedResultShouldNotBeExperimental = if (services.builtins.Annotations.isExperimental(assignedObject)) { accept('warning', `The assigned result '${assignedObject.name}' is experimental.`, { node, - property: 'assignedObject', code: CODE_EXPERIMENTAL_ASSIGNED_RESULT, }); } @@ -42,3 +41,18 @@ export const annotationCallAnnotationShouldNotBeExperimental = }); } }; + +export const argumentCorrespondingParameterShouldNotBeExperimental = + (services: SafeDsServices) => (node: SdsArgument, accept: ValidationAcceptor) => { + const parameter = services.helpers.NodeMapper.argumentToParameterOrUndefined(node); + if (!parameter) { + return; + } + + if (services.builtins.Annotations.isExperimental(parameter)) { + accept('warning', `The corresponding parameter '${parameter.name}' is experimental.`, { + node, + code: CODE_EXPERIMENTAL_CORRESPONDING_PARAMETER, + }); + } + }; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index b2986b284..d9c5f8542 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -50,13 +50,13 @@ import {argumentListMustNotHavePositionalArgumentsAfterNamedArguments} from './o import {parameterMustNotBeVariadicAndOptional} from './other/declarations/parameters.js'; import {referenceTargetMustNotBeAnnotationPipelineOrSchema} from './other/expressions/references.js'; import { - annotationCallAnnotationShouldNotBeDeprecated, - assigneeAssignedResultShouldNotBeDeprecated + annotationCallAnnotationShouldNotBeDeprecated, argumentCorrespondingParameterShouldNotBeDeprecated, + assigneeAssignedResultShouldNotBeDeprecated, } from './builtins/deprecated.js'; import { - annotationCallAnnotationShouldNotBeExperimental, - assigneeAssignedResultShouldNotBeExperimental -} from "./builtins/experimental.js"; + annotationCallAnnotationShouldNotBeExperimental, argumentCorrespondingParameterShouldNotBeExperimental, + assigneeAssignedResultShouldNotBeExperimental, +} from './builtins/experimental.js'; /** * Register custom validation checks. @@ -75,6 +75,10 @@ export const registerValidationChecks = function (services: SafeDsServices) { annotationCallAnnotationShouldNotBeExperimental(services), annotationCallArgumentListShouldBeNeeded, ], + SdsArgument: [ + argumentCorrespondingParameterShouldNotBeDeprecated(services), + argumentCorrespondingParameterShouldNotBeExperimental(services), + ], SdsArgumentList: [argumentListMustNotHavePositionalArgumentsAfterNamedArguments], SdsAttribute: [attributeMustHaveTypeHint], SdsBlockLambda: [blockLambdaMustContainUniqueNames], diff --git a/tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest index 1b10307e5..752ebb63b 100644 --- a/tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest +++ b/tests/resources/validation/builtins/annotations/deprecated/corresponding parameter/main.sdstest @@ -1,146 +1,26 @@ package tests.validation.builtins.deprecated.correspondingParameter -@Deprecated -annotation DeprecatedAnnotation -annotation ValidAnnotation - -@Deprecated -class DeprecatedClass -class ValidClass { - - @Deprecated - attr deprecatedAttribute: Int - attr validAttribute: Int -} - -@Deprecated -enum DeprecatedEnum -enum ValidEnum { - @Deprecated DeprecatedEnumVariant - ValidEnumVariant -} - -@Deprecated -fun deprecatedFunction() -fun validFunction( +fun f( @Deprecated deprecatedParameter: Int = 1, validParameter: Int = 1 -) -> ( - @Deprecated deprecatedResult: Int, - validResult: Int ) -@Deprecated segment deprecatedStep() {} -segment validStep() {} - -// $TEST$ warning "The called annotation is deprecated." -@»DeprecatedAnnotation« -// $TEST$ no warning "The called annotation is deprecated." -@»ValidAnnotation« -// $TEST$ no warning "The called annotation is deprecated." -@»Unresolved« -segment testStep1( - - // $TEST$ warning "The referenced declaration is deprecated." - @Deprecated deprecatedParameter: »DeprecatedClass« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - validParameter: »ValidClass« = 1, - - // $TEST$ warning "The referenced declaration is deprecated." - param3: »DeprecatedEnum« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - param4: »ValidEnum« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - param5: »Unresolved« = 1 -) --> ( - @Deprecated deprecatedResult: Int, - validResult: Int -) { - - // $TEST$ no warning "The referenced declaration is deprecated." - »deprecatedParameter«; - // $TEST$ no warning "The referenced declaration is deprecated." - »validParameter«; - - // $TEST$ warning "The referenced declaration is deprecated." - validParameter.»deprecatedAttribute«; - // $TEST$ no warning "The referenced declaration is deprecated." - validParameter.»validAttribute«; - - // $TEST$ warning "The referenced declaration is deprecated." - ValidEnum.»DeprecatedEnumVariant«; - // $TEST$ no warning "The referenced declaration is deprecated." - ValidEnum.»ValidEnumVariant«; - - // $TEST$ warning "The referenced declaration is deprecated." - »deprecatedFunction«(); - // $TEST$ no warning "The referenced declaration is deprecated." - »validFunction«(); - - validFunction( - // $TEST$ warning "The corresponding parameter is deprecated." +pipeline myPipeline { + f( + // $TEST$ warning "The corresponding parameter 'deprecatedParameter' is deprecated." »deprecatedParameter = 1«, - // $TEST$ no warning "The corresponding parameter is deprecated." + // $TEST$ no warning r"The corresponding parameter '\w*' is deprecated\." »validParameter = 1«, - // $TEST$ no warning "The corresponding parameter is deprecated." + // $TEST$ no warning r"The corresponding parameter '\w*' is deprecated\." »unresolved = 1«, ); - validFunction( - // $TEST$ warning "The corresponding parameter is deprecated." + f( + // $TEST$ warning "The corresponding parameter 'deprecatedParameter' is deprecated." »1«, - // $TEST$ no warning "The corresponding parameter is deprecated." + // $TEST$ no warning r"The corresponding parameter '\w*' is deprecated\." »1«, - // $TEST$ no warning "The corresponding parameter is deprecated." + // $TEST$ no warning r"The corresponding parameter '\w*' is deprecated\." »1«, ); - - // $TEST$ warning "The referenced declaration is deprecated." - validFunction().»deprecatedResult«; - - // $TEST$ no warning "The referenced declaration is deprecated." - validFunction().»validResult«; - - // $TEST$ warning "The referenced declaration is deprecated." - »deprecatedStep«(); - // $TEST$ no warning "The referenced declaration is deprecated." - »validStep«(); - - // $TEST$ no warning "The referenced declaration is deprecated." - »unresolved«; - - /* - * Showing an error for yields is pointless, since we must yield something. - * Deprecation is only relevant for callers. - */ - - // $TEST$ no warning "The referenced declaration is deprecated." - yield »deprecatedResult« = 1; - // $TEST$ no warning "The referenced declaration is deprecated." - yield »validResult« = 1; -} - -segment testStep2() -> (result1: Int, result2: Int, result3: Int) { - // $TEST$ warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »val a«, »val b«, »val c« = validFunction(); - - // $TEST$ warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »yield result1«, »yield result2«, »yield result3« = validFunction(); - - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »_«, »_«, »_« = validFunction(); - - // $TEST$ no warning "The assigned result is deprecated." - »val d« = a; - // $TEST$ no warning "The assigned result is deprecated." - »val e« = b; - // $TEST$ no warning "The assigned result is deprecated." - »val f« = 1; } diff --git a/tests/resources/validation/builtins/annotations/experimental/corresponding parameter/main.sdstest b/tests/resources/validation/builtins/annotations/experimental/corresponding parameter/main.sdstest new file mode 100644 index 000000000..ba5934cf6 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/experimental/corresponding parameter/main.sdstest @@ -0,0 +1,26 @@ +package tests.validation.builtins.experimental.correspondingParameter + +fun f( + @Experimental experimentalParameter: Int = 1, + validParameter: Int = 1 +) + +pipeline myPipeline { + f( + // $TEST$ warning "The corresponding parameter 'experimentalParameter' is experimental." + »experimentalParameter = 1«, + // $TEST$ no warning r"The corresponding parameter '\w*' is experimental\." + »validParameter = 1«, + // $TEST$ no warning r"The corresponding parameter '\w*' is experimental\." + »unresolved = 1«, + ); + + f( + // $TEST$ warning "The corresponding parameter 'experimentalParameter' is experimental." + »1«, + // $TEST$ no warning r"The corresponding parameter '\w*' is experimental\." + »1«, + // $TEST$ no warning r"The corresponding parameter '\w*' is experimental\." + »1«, + ); +} From 94b513ba63920334216ff41521fbc51ef39c4b4e Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 21:02:05 +0200 Subject: [PATCH 05/13] feat: warning if reference named type declaration is deprecated/experimental --- src/language/grammar/safe-ds.langium | 2 +- .../validation/builtins/deprecated.ts | 27 ++- .../validation/builtins/experimental.ts | 26 ++- src/language/validation/safe-ds-validator.ts | 10 +- .../from named type/main.sdstest | 32 ++++ .../from reference/main.sdstest | 96 +++++++++++ .../referenced declaration/main.sdstest | 159 ------------------ .../from named type/main.sdstest | 32 ++++ .../from reference/main.sdstest | 96 +++++++++++ 9 files changed, 312 insertions(+), 168 deletions(-) create mode 100644 tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from named type/main.sdstest create mode 100644 tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest delete mode 100644 tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest create mode 100644 tests/resources/validation/builtins/annotations/experimental/referenced declaration/from named type/main.sdstest create mode 100644 tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest diff --git a/src/language/grammar/safe-ds.langium b/src/language/grammar/safe-ds.langium index 6c7fa01e9..a97cfb619 100644 --- a/src/language/grammar/safe-ds.langium +++ b/src/language/grammar/safe-ds.langium @@ -334,7 +334,7 @@ fragment SdsSegmentFragment: interface SdsAnnotationCallList extends SdsAnnotatedObject {} interface SdsAnnotationCall extends SdsAbstractCall { - annotation?: @SdsAnnotation + annotation: @SdsAnnotation } SdsAnnotationCall returns SdsAnnotationCall: diff --git a/src/language/validation/builtins/deprecated.ts b/src/language/validation/builtins/deprecated.ts index 42d2b83d7..196cf4657 100644 --- a/src/language/validation/builtins/deprecated.ts +++ b/src/language/validation/builtins/deprecated.ts @@ -1,5 +1,12 @@ import { ValidationAcceptor } from 'langium'; -import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsArgument, SdsAssignee } from '../../generated/ast.js'; +import { + isSdsResult, + isSdsWildcard, + SdsAnnotationCall, + SdsArgument, + SdsAssignee, + SdsNamedType, +} from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; export const CODE_DEPRECATED_ASSIGNED_RESULT = 'deprecated/assigned-result'; @@ -28,7 +35,7 @@ export const assigneeAssignedResultShouldNotBeDeprecated = export const annotationCallAnnotationShouldNotBeDeprecated = (services: SafeDsServices) => (node: SdsAnnotationCall, accept: ValidationAcceptor) => { - const annotation = node.annotation?.ref; + const annotation = node.annotation.ref; if (!annotation) { return; } @@ -52,7 +59,21 @@ export const argumentCorrespondingParameterShouldNotBeDeprecated = if (services.builtins.Annotations.isDeprecated(parameter)) { accept('warning', `The corresponding parameter '${parameter.name}' is deprecated.`, { node, - code: CODE_DEPRECATED_CALLED_ANNOTATION, + code: CODE_DEPRECATED_CORRESPONDING_PARAMETER, }); } }; + +export const namedTypeDeclarationShouldNotBeDeprecated = (services: SafeDsServices) => (node: SdsNamedType, accept: ValidationAcceptor) => { + const declaration = node.declaration.ref; + if (!declaration) { + return; + } + + if (services.builtins.Annotations.isDeprecated(declaration)) { + accept('warning', `The referenced declaration '${declaration.name}' is deprecated.`, { + node, + code: CODE_DEPRECATED_REFERENCED_DECLARATION, + }); + } +}; diff --git a/src/language/validation/builtins/experimental.ts b/src/language/validation/builtins/experimental.ts index 122fd5c0a..e2ee84f82 100644 --- a/src/language/validation/builtins/experimental.ts +++ b/src/language/validation/builtins/experimental.ts @@ -1,5 +1,12 @@ import { ValidationAcceptor } from 'langium'; -import { isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsArgument, SdsAssignee } from '../../generated/ast.js'; +import { + isSdsResult, + isSdsWildcard, + SdsAnnotationCall, + SdsArgument, + SdsAssignee, + SdsNamedType, +} from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; export const CODE_EXPERIMENTAL_ASSIGNED_RESULT = 'experimental/assigned-result'; @@ -28,7 +35,7 @@ export const assigneeAssignedResultShouldNotBeExperimental = export const annotationCallAnnotationShouldNotBeExperimental = (services: SafeDsServices) => (node: SdsAnnotationCall, accept: ValidationAcceptor) => { - const annotation = node.annotation?.ref; + const annotation = node.annotation.ref; if (!annotation) { return; } @@ -56,3 +63,18 @@ export const argumentCorrespondingParameterShouldNotBeExperimental = }); } }; + +export const namedTypeDeclarationShouldNotBeExperimental = + (services: SafeDsServices) => (node: SdsNamedType, accept: ValidationAcceptor) => { + const declaration = node.declaration.ref; + if (!declaration) { + return; + } + + if (services.builtins.Annotations.isExperimental(declaration)) { + accept('warning', `The referenced declaration '${declaration.name}' is experimental.`, { + node, + code: CODE_EXPERIMENTAL_REFERENCED_DECLARATION, + }); + } + }; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index d9c5f8542..46fd49cad 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -51,11 +51,11 @@ import {parameterMustNotBeVariadicAndOptional} from './other/declarations/parame import {referenceTargetMustNotBeAnnotationPipelineOrSchema} from './other/expressions/references.js'; import { annotationCallAnnotationShouldNotBeDeprecated, argumentCorrespondingParameterShouldNotBeDeprecated, - assigneeAssignedResultShouldNotBeDeprecated, + assigneeAssignedResultShouldNotBeDeprecated, namedTypeDeclarationShouldNotBeDeprecated, } from './builtins/deprecated.js'; import { annotationCallAnnotationShouldNotBeExperimental, argumentCorrespondingParameterShouldNotBeExperimental, - assigneeAssignedResultShouldNotBeExperimental, + assigneeAssignedResultShouldNotBeExperimental, namedTypeDeclarationShouldNotBeExperimental, } from './builtins/experimental.js'; /** @@ -95,7 +95,11 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsFunction: [functionMustContainUniqueNames, functionResultListShouldNotBeEmpty], SdsMemberAccess: [memberAccessNullSafetyShouldBeNeeded(services)], SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage], - SdsNamedType: [namedTypeTypeArgumentListShouldBeNeeded], + SdsNamedType: [ + namedTypeDeclarationShouldNotBeDeprecated(services), + namedTypeDeclarationShouldNotBeExperimental(services), + namedTypeTypeArgumentListShouldBeNeeded + ], SdsParameter: [parameterMustHaveTypeHint, parameterMustNotBeVariadicAndOptional], SdsParameterList: [ parameterListMustNotHaveOptionalAndVariadicParameters, diff --git a/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from named type/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from named type/main.sdstest new file mode 100644 index 000000000..8de795830 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from named type/main.sdstest @@ -0,0 +1,32 @@ +package tests.validation.builtins.deprecated.referencedDeclaration.fromNamedType + +@Deprecated +class DeprecatedClass +class ValidClass + +@Deprecated +enum DeprecatedEnum +enum ValidEnum { + @Deprecated DeprecatedEnumVariant + ValidEnumVariant +} + +segment mySegment( + // $TEST$ warning "The referenced declaration 'DeprecatedClass' is deprecated." + p1: »DeprecatedClass«, + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + p2: »ValidClass«, + + // $TEST$ warning "The referenced declaration 'DeprecatedEnum' is deprecated." + p3: »DeprecatedEnum«, + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + p4: »ValidEnum«, + + // $TEST$ warning "The referenced declaration 'DeprecatedEnumVariant' is deprecated." + p5: ValidEnum.»DeprecatedEnumVariant«, + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + p6: ValidEnum.»ValidEnumVariant«, + + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + p7: »Unresolved« +) {} diff --git a/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest new file mode 100644 index 000000000..c16a48acc --- /dev/null +++ b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest @@ -0,0 +1,96 @@ +package tests.validation.builtins.deprecated.referencedDeclaration.fromReference + +@Deprecated +class DeprecatedClass +class ValidClass { + + @Deprecated + static attr deprecatedAttribute: Int + static attr validAttribute: Int +} + +@Deprecated +enum DeprecatedEnum +enum ValidEnum { + @Deprecated DeprecatedEnumVariant + + ValidEnumVariant +} + +@Deprecated +fun deprecatedFunction() +fun validFunction( + @Deprecated deprecatedParameter: Int = 1, + validParameter: Int = 1 +) -> ( + @Deprecated + deprecatedResult: Int, + validResult: Int +) + +@Deprecated +segment deprecatedSegment() {} +segment validSegment() {} + +segment mySegment( + // $TEST$ warning "The referenced declaration 'DeprecatedClass' is deprecated." + @Deprecated deprecatedParameter: »DeprecatedClass« = 1, + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + validParameter: »ValidClass« = 1, + + // $TEST$ warning "The referenced declaration 'DeprecatedEnum' is deprecated." + param3: »DeprecatedEnum« = 1, + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + param4: »ValidEnum« = 1, + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + param5: »Unresolved« = 1 +) +-> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) { + /* + * Deprecation is only relevant for callers + */ + + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + »deprecatedParameter«; + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + »validParameter«; + + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + yield »deprecatedResult« = 1; + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + yield »validResult« = 1; + + + // $TEST$ warning "The referenced declaration 'deprecatedAttribute' is deprecated." + ValidClass.»deprecatedAttribute«; + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + ValidClass.»validAttribute«; + + // $TEST$ warning "The referenced declaration 'DeprecatedEnumVariant' is deprecated." + ValidEnum.»DeprecatedEnumVariant«; + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + ValidEnum.»ValidEnumVariant«; + + // $TEST$ warning "The referenced declaration 'deprecatedFunction' is deprecated." + »deprecatedFunction«(); + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + »validFunction«(); + + // $TEST$ warning "The referenced declaration 'deprecatedResult' is deprecated." + validFunction().»deprecatedResult«; + + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + validFunction().»validResult«; + + // $TEST$ warning "The referenced declaration 'deprecatedSegment' is deprecated." + »deprecatedSegment«(); + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + »validSegment«(); + + + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + »unresolved«; +} diff --git a/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest deleted file mode 100644 index c00592c7d..000000000 --- a/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/main.sdstest +++ /dev/null @@ -1,159 +0,0 @@ -package tests.validation.builtins.deprecated.referencedDeclaration - -@Deprecated -annotation DeprecatedAnnotation -annotation ValidAnnotation - -@Deprecated -class DeprecatedClass -class ValidClass { - - @Deprecated - attr deprecatedAttribute: Int - attr validAttribute: Int -} - -@Deprecated -enum DeprecatedEnum -enum ValidEnum { - @Deprecated DeprecatedEnumVariant - ValidEnumVariant -} - -@Deprecated -fun deprecatedFunction() -fun validFunction( - @Deprecated deprecatedParameter: Int = 1, - validParameter: Int = 1 -) -> ( - @Deprecated deprecatedResult: Int, - validResult: Int -) - -fun functionWithTypeParameters<@Deprecated DeprecatedTypeParameter, ValidTypeParameter>() where { - - /* - * Showing an error for constraints is pointless, since constraints must stay. - * Deprecation is only relevant for callers. - */ - - // $TEST$ no warning "The referenced declaration is deprecated." - »DeprecatedTypeParameter« sub Int, - // $TEST$ no warning "The referenced declaration is deprecated." - »ValidTypeParameter« sub Int -} - -@Deprecated segment deprecatedStep() {} -segment validStep() {} - -// $TEST$ warning "The called annotation is deprecated." -@»DeprecatedAnnotation« -// $TEST$ no warning "The called annotation is deprecated." -@»ValidAnnotation« -// $TEST$ no warning "The called annotation is deprecated." -@»Unresolved« -segment testStep1( - - // $TEST$ warning "The referenced declaration is deprecated." - @Deprecated deprecatedParameter: »DeprecatedClass« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - validParameter: »ValidClass« = 1, - - // $TEST$ warning "The referenced declaration is deprecated." - param3: »DeprecatedEnum« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - param4: »ValidEnum« = 1, - // $TEST$ no warning "The referenced declaration is deprecated." - param5: »Unresolved« = 1 -) --> ( - @Deprecated deprecatedResult: Int, - validResult: Int -) { - - // $TEST$ no warning "The referenced declaration is deprecated." - »deprecatedParameter«; - // $TEST$ no warning "The referenced declaration is deprecated." - »validParameter«; - - // $TEST$ warning "The referenced declaration is deprecated." - validParameter.»deprecatedAttribute«; - // $TEST$ no warning "The referenced declaration is deprecated." - validParameter.»validAttribute«; - - // $TEST$ warning "The referenced declaration is deprecated." - ValidEnum.»DeprecatedEnumVariant«; - // $TEST$ no warning "The referenced declaration is deprecated." - ValidEnum.»ValidEnumVariant«; - - // $TEST$ warning "The referenced declaration is deprecated." - »deprecatedFunction«(); - // $TEST$ no warning "The referenced declaration is deprecated." - »validFunction«(); - - validFunction( - // $TEST$ warning "The corresponding parameter is deprecated." - »deprecatedParameter = 1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »validParameter = 1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »unresolved = 1«, - ); - - validFunction( - // $TEST$ warning "The corresponding parameter is deprecated." - »1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »1«, - // $TEST$ no warning "The corresponding parameter is deprecated." - »1«, - ); - - // $TEST$ warning "The referenced declaration is deprecated." - validFunction().»deprecatedResult«; - - // $TEST$ no warning "The referenced declaration is deprecated." - validFunction().»validResult«; - - // $TEST$ warning "The referenced declaration is deprecated." - »deprecatedStep«(); - // $TEST$ no warning "The referenced declaration is deprecated." - »validStep«(); - - // $TEST$ no warning "The referenced declaration is deprecated." - »unresolved«; - - /* - * Showing an error for yields is pointless, since we must yield something. - * Deprecation is only relevant for callers. - */ - - // $TEST$ no warning "The referenced declaration is deprecated." - yield »deprecatedResult« = 1; - // $TEST$ no warning "The referenced declaration is deprecated." - yield »validResult« = 1; -} - -segment testStep2() -> (result1: Int, result2: Int, result3: Int) { - // $TEST$ warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »val a«, »val b«, »val c« = validFunction(); - - // $TEST$ warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »yield result1«, »yield result2«, »yield result3« = validFunction(); - - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - // $TEST$ no warning "The assigned result is deprecated." - »_«, »_«, »_« = validFunction(); - - // $TEST$ no warning "The assigned result is deprecated." - »val d« = a; - // $TEST$ no warning "The assigned result is deprecated." - »val e« = b; - // $TEST$ no warning "The assigned result is deprecated." - »val f« = 1; -} diff --git a/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from named type/main.sdstest b/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from named type/main.sdstest new file mode 100644 index 000000000..b2be9f453 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from named type/main.sdstest @@ -0,0 +1,32 @@ +package tests.validation.builtins.experimental.referencedDeclaration.fromNamedType + +@Experimental +class ExperimentalClass +class ValidClass + +@Experimental +enum ExperimentalEnum +enum ValidEnum { + @Experimental ExperimentalEnumVariant + ValidEnumVariant +} + +segment mySegment( + // $TEST$ warning "The referenced declaration 'ExperimentalClass' is experimental." + p1: »ExperimentalClass«, + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + p2: »ValidClass«, + + // $TEST$ warning "The referenced declaration 'ExperimentalEnum' is experimental." + p3: »ExperimentalEnum«, + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + p4: »ValidEnum«, + + // $TEST$ warning "The referenced declaration 'ExperimentalEnumVariant' is experimental." + p5: ValidEnum.»ExperimentalEnumVariant«, + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + p6: ValidEnum.»ValidEnumVariant«, + + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + p7: »Unresolved« +) {} diff --git a/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest b/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest new file mode 100644 index 000000000..9e4e8c4e8 --- /dev/null +++ b/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest @@ -0,0 +1,96 @@ +package tests.validation.builtins.experimental.referencedDeclaration.fromReference + +@Experimental +class ExperimentalClass +class ValidClass { + + @Experimental + static attr experimentalAttribute: Int + static attr validAttribute: Int +} + +@Experimental +enum ExperimentalEnum +enum ValidEnum { + @Experimental ExperimentalEnumVariant + + ValidEnumVariant +} + +@Experimental +fun experimentalFunction() +fun validFunction( + @Experimental experimentalParameter: Int = 1, + validParameter: Int = 1 +) -> ( + @Experimental + experimentalResult: Int, + validResult: Int +) + +@Experimental +segment experimentalSegment() {} +segment validSegment() {} + +segment mySegment( + // $TEST$ warning "The referenced declaration 'ExperimentalClass' is experimental." + @Experimental experimentalParameter: »ExperimentalClass« = 1, + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + validParameter: »ValidClass« = 1, + + // $TEST$ warning "The referenced declaration 'ExperimentalEnum' is experimental." + param3: »ExperimentalEnum« = 1, + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + param4: »ValidEnum« = 1, + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + param5: »Unresolved« = 1 +) +-> ( + @Experimental experimentalResult: Int, + validResult: Int +) { + /* + * Deprecation is only relevant for callers + */ + + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + »experimentalParameter«; + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + »validParameter«; + + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + yield »experimentalResult« = 1; + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + yield »validResult« = 1; + + + // $TEST$ warning "The referenced declaration 'experimentalAttribute' is experimental." + ValidClass.»experimentalAttribute«; + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + ValidClass.»validAttribute«; + + // $TEST$ warning "The referenced declaration 'ExperimentalEnumVariant' is experimental." + ValidEnum.»ExperimentalEnumVariant«; + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + ValidEnum.»ValidEnumVariant«; + + // $TEST$ warning "The referenced declaration 'experimentalFunction' is experimental." + »experimentalFunction«(); + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + »validFunction«(); + + // $TEST$ warning "The referenced declaration 'experimentalResult' is experimental." + validFunction().»experimentalResult«; + + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + validFunction().»validResult«; + + // $TEST$ warning "The referenced declaration 'experimentalSegment' is experimental." + »experimentalSegment«(); + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + »validSegment«(); + + + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + »unresolved«; +} From 6bdbc14e6b61e92c542e96c0aad9901892bdb77d Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 21:36:49 +0200 Subject: [PATCH 06/13] feat: warning if referenced declaration is deprecated/experimental --- .../validation/builtins/deprecated.ts | 17 +- .../validation/builtins/experimental.ts | 17 + src/language/validation/safe-ds-validator.ts | 52 +- .../to results/of block lambdas/main.sdstest | 643 ++++++++++++++++++ .../from reference/main.sdstest | 32 +- .../from reference/main.sdstest | 32 +- 6 files changed, 738 insertions(+), 55 deletions(-) create mode 100644 tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest diff --git a/src/language/validation/builtins/deprecated.ts b/src/language/validation/builtins/deprecated.ts index 196cf4657..522853551 100644 --- a/src/language/validation/builtins/deprecated.ts +++ b/src/language/validation/builtins/deprecated.ts @@ -1,11 +1,12 @@ import { ValidationAcceptor } from 'langium'; import { + isSdsParameter, isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsArgument, SdsAssignee, - SdsNamedType, + SdsNamedType, SdsReference, } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; @@ -77,3 +78,17 @@ export const namedTypeDeclarationShouldNotBeDeprecated = (services: SafeDsServic }); } }; + +export const referenceTargetShouldNotBeDeprecated = (services: SafeDsServices) => (node: SdsReference, accept: ValidationAcceptor) => { + const target = node.target.ref; + if (!target || isSdsParameter(target)) { + return; + } + + if (services.builtins.Annotations.isDeprecated(target)) { + accept('warning', `The referenced declaration '${target.name}' is deprecated.`, { + node, + code: CODE_DEPRECATED_REFERENCED_DECLARATION, + }); + } +}; diff --git a/src/language/validation/builtins/experimental.ts b/src/language/validation/builtins/experimental.ts index e2ee84f82..09b28618d 100644 --- a/src/language/validation/builtins/experimental.ts +++ b/src/language/validation/builtins/experimental.ts @@ -1,11 +1,13 @@ import { ValidationAcceptor } from 'langium'; import { + isSdsParameter, isSdsResult, isSdsWildcard, SdsAnnotationCall, SdsArgument, SdsAssignee, SdsNamedType, + SdsReference, } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; @@ -78,3 +80,18 @@ export const namedTypeDeclarationShouldNotBeExperimental = }); } }; + +export const referenceTargetShouldNotExperimental = + (services: SafeDsServices) => (node: SdsReference, accept: ValidationAcceptor) => { + const target = node.target.ref; + if (!target || isSdsParameter(target)) { + return; + } + + if (services.builtins.Annotations.isExperimental(target)) { + accept('warning', `The referenced declaration '${target.name}' is experimental.`, { + node, + code: CODE_EXPERIMENTAL_REFERENCED_DECLARATION, + }); + } + }; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 46fd49cad..db15a5a3b 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -1,6 +1,6 @@ -import {ValidationChecks} from 'langium'; -import {SafeDsAstType} from '../generated/ast.js'; -import type {SafeDsServices} from '../safe-ds-module.js'; +import { ValidationChecks } from 'langium'; +import { SafeDsAstType } from '../generated/ast.js'; +import type { SafeDsServices } from '../safe-ds-module.js'; import { annotationMustContainUniqueNames, blockLambdaMustContainUniqueNames, @@ -31,31 +31,35 @@ import { typeParameterListShouldNotBeEmpty, unionTypeShouldNotHaveASingularTypeArgument, } from './style.js'; -import {templateStringMustHaveExpressionBetweenTwoStringParts} from './other/expressions/templateStrings.js'; -import {yieldMustNotBeUsedInPipeline} from './other/statements/assignments.js'; -import {attributeMustHaveTypeHint, parameterMustHaveTypeHint, resultMustHaveTypeHint} from './types.js'; -import {moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage} from './other/modules.js'; -import { - typeParameterConstraintLeftOperandMustBeOwnTypeParameter -} from './other/declarations/typeParameterConstraints.js'; +import { templateStringMustHaveExpressionBetweenTwoStringParts } from './other/expressions/templateStrings.js'; +import { yieldMustNotBeUsedInPipeline } from './other/statements/assignments.js'; +import { attributeMustHaveTypeHint, parameterMustHaveTypeHint, resultMustHaveTypeHint } from './types.js'; +import { moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage } from './other/modules.js'; +import { typeParameterConstraintLeftOperandMustBeOwnTypeParameter } from './other/declarations/typeParameterConstraints.js'; import { parameterListMustNotHaveOptionalAndVariadicParameters, parameterListMustNotHaveRequiredParametersAfterOptionalParameters, parameterListVariadicParameterMustBeLast, } from './other/declarations/parameterLists.js'; -import {unionTypeMustHaveTypeArguments} from './other/types/unionTypes.js'; -import {callableTypeMustNotHaveOptionalParameters} from './other/types/callableTypes.js'; -import {typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments} from './other/types/typeArgumentLists.js'; -import {argumentListMustNotHavePositionalArgumentsAfterNamedArguments} from './other/argumentLists.js'; -import {parameterMustNotBeVariadicAndOptional} from './other/declarations/parameters.js'; -import {referenceTargetMustNotBeAnnotationPipelineOrSchema} from './other/expressions/references.js'; +import { unionTypeMustHaveTypeArguments } from './other/types/unionTypes.js'; +import { callableTypeMustNotHaveOptionalParameters } from './other/types/callableTypes.js'; +import { typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/types/typeArgumentLists.js'; +import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js'; +import { parameterMustNotBeVariadicAndOptional } from './other/declarations/parameters.js'; +import { referenceTargetMustNotBeAnnotationPipelineOrSchema } from './other/expressions/references.js'; import { - annotationCallAnnotationShouldNotBeDeprecated, argumentCorrespondingParameterShouldNotBeDeprecated, - assigneeAssignedResultShouldNotBeDeprecated, namedTypeDeclarationShouldNotBeDeprecated, + annotationCallAnnotationShouldNotBeDeprecated, + argumentCorrespondingParameterShouldNotBeDeprecated, + assigneeAssignedResultShouldNotBeDeprecated, + namedTypeDeclarationShouldNotBeDeprecated, + referenceTargetShouldNotBeDeprecated, } from './builtins/deprecated.js'; import { - annotationCallAnnotationShouldNotBeExperimental, argumentCorrespondingParameterShouldNotBeExperimental, - assigneeAssignedResultShouldNotBeExperimental, namedTypeDeclarationShouldNotBeExperimental, + annotationCallAnnotationShouldNotBeExperimental, + argumentCorrespondingParameterShouldNotBeExperimental, + assigneeAssignedResultShouldNotBeExperimental, + namedTypeDeclarationShouldNotBeExperimental, + referenceTargetShouldNotExperimental, } from './builtins/experimental.js'; /** @@ -98,7 +102,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsNamedType: [ namedTypeDeclarationShouldNotBeDeprecated(services), namedTypeDeclarationShouldNotBeExperimental(services), - namedTypeTypeArgumentListShouldBeNeeded + namedTypeTypeArgumentListShouldBeNeeded, ], SdsParameter: [parameterMustHaveTypeHint, parameterMustNotBeVariadicAndOptional], SdsParameterList: [ @@ -107,7 +111,11 @@ export const registerValidationChecks = function (services: SafeDsServices) { parameterListVariadicParameterMustBeLast, ], SdsPipeline: [pipelineMustContainUniqueNames], - SdsReference: [referenceTargetMustNotBeAnnotationPipelineOrSchema], + SdsReference: [ + referenceTargetMustNotBeAnnotationPipelineOrSchema, + referenceTargetShouldNotBeDeprecated(services), + referenceTargetShouldNotExperimental(services), + ], SdsResult: [resultMustHaveTypeHint], SdsSegment: [segmentMustContainUniqueNames, segmentResultListShouldNotBeEmpty], SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts], diff --git a/tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest b/tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest new file mode 100644 index 000000000..88ad96660 --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest @@ -0,0 +1,643 @@ +package tests.scoping.memberAccesses.toResults.ofBlockLambdas + +/* +inner class Reference { + + @Nested + inner class MemberAccess { + + @Test + fun `should resolve static class attribute accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classStaticAttributeInSameFile = + findUniqueDeclarationOrFail("classStaticAttributeInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classStaticAttributeInSameFile) + } + + @Test + fun `should resolve instance class attribute accessed from class instance`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classInstanceAttributeInSameFile = + findUniqueDeclarationOrFail("classInstanceAttributeInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInstanceAttributeInSameFile) + } + + @Test + fun `should resolve nested class accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classInClassInSameFile = + findUniqueDeclarationOrFail("ClassInClassInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInClassInSameFile) + } + + @Test + fun `should resolve nested enum accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val enumInClassInSameFile = + findUniqueDeclarationOrFail("EnumInClassInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumInClassInSameFile) + } + + @Test + fun `should resolve static class method accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classStaticMethodInSameFile = + findUniqueDeclarationOrFail("classStaticMethodInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[9].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classStaticMethodInSameFile) + } + + @Test + fun `should resolve instance class method accessed from class instance`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classInstanceMethodInSameFile = + findUniqueDeclarationOrFail("classInstanceMethodInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[11].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInstanceMethodInSameFile) + } + + @Test + fun `should resolve enum variants`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToEnumVariants") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val references = step.body.descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants from step annotation`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToEnumVariants") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val annotations = step.annotationCallsOrEmpty() + annotations.shouldHaveSize(1) + + val references = annotations[0].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants from parameter annotation`() = withResource(REFERENCE) { + val parameter = + findUniqueDeclarationOrFail("referenceToEnumVariantFromParameterAnnotation") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val annotations = parameter.annotationCallsOrEmpty() + annotations.shouldHaveSize(1) + + val references = annotations[0].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants of nested enum from class annotation`() = withResource(REFERENCE) { + val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameClass") + + val annotations = parameter.annotationCallsOrEmpty() + annotations.shouldHaveSize(2) + + val references = annotations[0].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants of global enum from class annotation`() = withResource(REFERENCE) { + val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") + val enumVariantInSameClass = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val annotations = parameter.annotationCallsOrEmpty() + annotations.shouldHaveSize(2) + + val references = annotations[1].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameClass) + } + + @Test + fun `should resolve parameters of enum variants`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToEnumVariantParameters") + val enumVariantParameterInSameFile = + findUniqueDeclarationOrFail("enumVariantParameterInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(3) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantParameterInSameFile) + } + + @Test + fun `should resolve inherited static class attribute accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassStaticAttribute = + findUniqueDeclarationOrFail("superClassStaticAttribute") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassStaticAttribute) + } + + @Test + fun `should resolve inherited instance class attribute accessed from class instance`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassInstanceAttribute = + findUniqueDeclarationOrFail("superClassInstanceAttribute") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassInstanceAttribute) + } + + @Test + fun `should resolve inherited nested class accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val classInSuperClass = + findUniqueDeclarationOrFail("ClassInSuperClass") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInSuperClass) + } + + @Test + fun `should resolve inherited nested enum accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val enumInSuperClass = + findUniqueDeclarationOrFail("EnumInSuperClass") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumInSuperClass) + } + + @Test + fun `should resolve inherited static class method accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassStaticMethod = + findUniqueDeclarationOrFail("superClassStaticMethod") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[9].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassStaticMethod) + } + + @Test + fun `should resolve inherited instance class method accessed from class instance`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassInstanceMethod = + findUniqueDeclarationOrFail("superClassInstanceMethod") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[11].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassInstanceMethod) + } + + @Test + fun `should resolve overridden instance attribute`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") + val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") + val instanceAttributeForOverriding = + subClassForOverriding.findUniqueDeclarationOrFail("instanceAttributeForOverriding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(instanceAttributeForOverriding) + } + + @Test + fun `should resolve overridden instance method`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") + val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") + val instanceMethodForOverriding = + subClassForOverriding.findUniqueDeclarationOrFail("instanceMethodForOverriding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(instanceMethodForOverriding) + } + + @Test + fun `should resolve hidden static attribute`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val staticAttributeForHiding = + subClassForHiding.findUniqueDeclarationOrFail("staticAttributeForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(staticAttributeForHiding) + } + + @Test + fun `should resolve hidden nested class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val nestedClassForHiding = + subClassForHiding.findUniqueDeclarationOrFail("NestedClassForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(nestedClassForHiding) + } + + @Test + fun `should resolve hidden nested enum`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val nestedEnumForHiding = + subClassForHiding.findUniqueDeclarationOrFail("NestedEnumForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(nestedEnumForHiding) + } + + @Test + fun `should resolve hidden static method`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val staticMethodForHiding = + subClassForHiding.findUniqueDeclarationOrFail("staticMethodForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(staticMethodForHiding) + } + + @Test + fun `should not resolve static class members accessed from instance`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStaticClassMembersFromInstance") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") + + val references = step.descendants() + .filter { it.declaration != classInSameFile } + .toList() + references.shouldHaveSize(8) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve instance class members accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInstanceClassMembersFromClass") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") + + val references = step.descendants() + .filter { it.declaration != classInSameFile } + .toList() + references.shouldHaveSize(4) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve class members with unqualified access`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("unqualifiedReferencesToClassMembers") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve enum variants with unqualified access`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariants") + + val references = step.descendants().toList() + references.shouldHaveSize(1) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve parameters of enum variants with unqualified access`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariantParameters") + + val references = step.descendants().toList() + references.shouldHaveSize(1) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve result of callable type with one result without matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val singleResult = step.findUniqueDeclarationOrFail("singleResult") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(singleResult) + } + + @Test + fun `should resolve attribute for callable type with one result with matching class attribute`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result for callable type with one result with matching enum variant`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val callableWithOneResultWithIdenticalEnumVariant = + step.findUniqueDeclarationOrFail("callableWithOneResultWithIdenticalEnumVariant") + val result = + callableWithOneResultWithIdenticalEnumVariant.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of callable type with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val result1 = step.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + + @Test + fun `should resolve result of function with one result without matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToFunctionResults") + val globalFunctionResultInSameFile = + findUniqueDeclarationOrFail("globalFunctionResultInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(globalFunctionResultInSameFile) + } + + @Test + fun `should resolve member for function with one result with matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToFunctionResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of function with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToFunctionResults") + val globalFunctionWithTwoResults = + findUniqueDeclarationOrFail("globalFunctionWithTwoResults") + val result1 = globalFunctionWithTwoResults.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + + @Test + fun `should resolve result of lambda with one result without matching member`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToLambdaResults") + val singleResult = step.findUniqueDeclarationOrFail("singleResult") + + val references = step.descendants().toList() + references.shouldHaveSize(7) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.shouldBe(singleResult) + } + + @Test + fun `should resolve member for lambda with one result with matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToLambdaResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(7) + + val declaration = references[4].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of lambda with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToLambdaResults") + val result1 = step.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(7) + + val declaration = references[6].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + + @Test + fun `should resolve result of step with one result without matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStepResults") + val stepResultInSameFile = findUniqueDeclarationOrFail("stepResultInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(stepResultInSameFile) + } + + @Test + fun `should resolve member for step with one result with matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStepResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of step with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStepResults") + val stepInSameFileWithTwoResults = + findUniqueDeclarationOrFail("stepWithTwoResults") + val result1 = stepInSameFileWithTwoResults.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + } + } +*/ + +class ClassForResultMemberAccess() { + attr result: Int +} +enum EnumForResultMemberAccess { + result +} + +pipeline myPipeline { + val lambdaWithOneResult = () { + yield singleResult = 1; + }; + val lambdaWithOneResultWithIdenticalMember = () { + yield result = ClassForResultMemberAccess(); + }; + val lambdaWithTwoResults = () { + yield result1 = 1; + yield result2 = 1; + }; + + lambdaWithOneResult().singleResult; + lambdaWithOneResultWithIdenticalMember().result; + lambdaWithTwoResults().result1; +} + +step referencesToStepResults() { + stepInSameFile(1).stepResultInSameFile; + stepWithOneResultWithIdenticalMember().result; + stepWithTwoResults().result1; +} diff --git a/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest index c16a48acc..71a502031 100644 --- a/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest +++ b/tests/resources/validation/builtins/annotations/deprecated/referenced declaration/from reference/main.sdstest @@ -19,10 +19,7 @@ enum ValidEnum { @Deprecated fun deprecatedFunction() -fun validFunction( - @Deprecated deprecatedParameter: Int = 1, - validParameter: Int = 1 -) -> ( +fun validFunction() -> ( @Deprecated deprecatedResult: Int, validResult: Int @@ -33,17 +30,8 @@ segment deprecatedSegment() {} segment validSegment() {} segment mySegment( - // $TEST$ warning "The referenced declaration 'DeprecatedClass' is deprecated." - @Deprecated deprecatedParameter: »DeprecatedClass« = 1, - // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." - validParameter: »ValidClass« = 1, - - // $TEST$ warning "The referenced declaration 'DeprecatedEnum' is deprecated." - param3: »DeprecatedEnum« = 1, - // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." - param4: »ValidEnum« = 1, - // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." - param5: »Unresolved« = 1 + @Deprecated deprecatedParameter: ValidClass, + validParameter: ValidClass, ) -> ( @Deprecated deprecatedResult: Int, @@ -64,16 +52,28 @@ segment mySegment( yield »validResult« = 1; + // $TEST$ warning "The referenced declaration 'DeprecatedClass' is deprecated." + »DeprecatedClass«; + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + »ValidClass«; + // $TEST$ warning "The referenced declaration 'deprecatedAttribute' is deprecated." ValidClass.»deprecatedAttribute«; // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." ValidClass.»validAttribute«; + + // $TEST$ warning "The referenced declaration 'DeprecatedEnum' is deprecated." + »DeprecatedEnum«; + // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." + »ValidEnum«; + // $TEST$ warning "The referenced declaration 'DeprecatedEnumVariant' is deprecated." ValidEnum.»DeprecatedEnumVariant«; // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." ValidEnum.»ValidEnumVariant«; + // $TEST$ warning "The referenced declaration 'deprecatedFunction' is deprecated." »deprecatedFunction«(); // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." @@ -81,10 +81,10 @@ segment mySegment( // $TEST$ warning "The referenced declaration 'deprecatedResult' is deprecated." validFunction().»deprecatedResult«; - // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." validFunction().»validResult«; + // $TEST$ warning "The referenced declaration 'deprecatedSegment' is deprecated." »deprecatedSegment«(); // $TEST$ no warning r"The referenced declaration '\w*' is deprecated\." diff --git a/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest b/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest index 9e4e8c4e8..10745a354 100644 --- a/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest +++ b/tests/resources/validation/builtins/annotations/experimental/referenced declaration/from reference/main.sdstest @@ -19,10 +19,7 @@ enum ValidEnum { @Experimental fun experimentalFunction() -fun validFunction( - @Experimental experimentalParameter: Int = 1, - validParameter: Int = 1 -) -> ( +fun validFunction() -> ( @Experimental experimentalResult: Int, validResult: Int @@ -33,17 +30,8 @@ segment experimentalSegment() {} segment validSegment() {} segment mySegment( - // $TEST$ warning "The referenced declaration 'ExperimentalClass' is experimental." - @Experimental experimentalParameter: »ExperimentalClass« = 1, - // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." - validParameter: »ValidClass« = 1, - - // $TEST$ warning "The referenced declaration 'ExperimentalEnum' is experimental." - param3: »ExperimentalEnum« = 1, - // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." - param4: »ValidEnum« = 1, - // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." - param5: »Unresolved« = 1 + @Experimental experimentalParameter: ValidClass, + validParameter: ValidClass, ) -> ( @Experimental experimentalResult: Int, @@ -64,16 +52,28 @@ segment mySegment( yield »validResult« = 1; + // $TEST$ warning "The referenced declaration 'ExperimentalClass' is experimental." + »ExperimentalClass«; + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + »ValidClass«; + // $TEST$ warning "The referenced declaration 'experimentalAttribute' is experimental." ValidClass.»experimentalAttribute«; // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." ValidClass.»validAttribute«; + + // $TEST$ warning "The referenced declaration 'ExperimentalEnum' is experimental." + »ExperimentalEnum«; + // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." + »ValidEnum«; + // $TEST$ warning "The referenced declaration 'ExperimentalEnumVariant' is experimental." ValidEnum.»ExperimentalEnumVariant«; // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." ValidEnum.»ValidEnumVariant«; + // $TEST$ warning "The referenced declaration 'experimentalFunction' is experimental." »experimentalFunction«(); // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." @@ -81,10 +81,10 @@ segment mySegment( // $TEST$ warning "The referenced declaration 'experimentalResult' is experimental." validFunction().»experimentalResult«; - // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." validFunction().»validResult«; + // $TEST$ warning "The referenced declaration 'experimentalSegment' is experimental." »experimentalSegment«(); // $TEST$ no warning r"The referenced declaration '\w*' is experimental\." From a176f32447d4d6e5f597c26bcdcd7944d4ff8a55 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 22:04:04 +0200 Subject: [PATCH 07/13] feat: scoping for member accesses to results --- .../scoping/safe-ds-scope-provider.ts | 59 +- .../scoping/member accesses/skip-main.sdstest | 233 ------- .../to results/of block lambdas/main.sdstest | 639 +----------------- .../to results/of callable types/main.sdstest | 15 + .../to results/of functions/main.sdstest | 15 + .../to results/of segments/main.sdstest | 15 + .../main.sdstest | 15 + .../main.sdstest | 13 + .../main.sdstest | 13 + .../main.sdstest | 13 + 10 files changed, 140 insertions(+), 890 deletions(-) create mode 100644 tests/resources/scoping/member accesses/to results/of callable types/main.sdstest create mode 100644 tests/resources/scoping/member accesses/to results/of functions/main.sdstest create mode 100644 tests/resources/scoping/member accesses/to results/of segments/main.sdstest create mode 100644 tests/resources/scoping/member accesses/to results/skip-of block lambdas (matching member)/main.sdstest create mode 100644 tests/resources/scoping/member accesses/to results/skip-of callable types (matching member)/main.sdstest create mode 100644 tests/resources/scoping/member accesses/to results/skip-of functions (matching member)/main.sdstest create mode 100644 tests/resources/scoping/member accesses/to results/skip-of segments (matching member)/main.sdstest diff --git a/src/language/scoping/safe-ds-scope-provider.ts b/src/language/scoping/safe-ds-scope-provider.ts index c94bc2ebf..f1efbc7d7 100644 --- a/src/language/scoping/safe-ds-scope-provider.ts +++ b/src/language/scoping/safe-ds-scope-provider.ts @@ -13,7 +13,7 @@ import { isSdsAbstractCall, isSdsArgument, isSdsAssignment, - isSdsBlock, + isSdsBlock, isSdsCall, isSdsCallable, isSdsClass, isSdsEnum, @@ -49,6 +49,7 @@ import { } from '../generated/ast.js'; import { isContainedIn } from '../helpers/astUtils.js'; import { + abstractResultsOrEmpty, assigneesOrEmpty, classMembersOrEmpty, enumVariantsOrEmpty, @@ -185,35 +186,41 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { return this.createScopeForNodes(enumVariantsOrEmpty(declaration)); } - // // Call results - // var resultScope = IScope.NULLSCOPE - // if (receiver is SdsCall) { - // val results = receiver.resultsOrNull() - // when { - // results == null -> return IScope.NULLSCOPE - // results.size > 1 -> return Scopes.scopeFor(results) - // results.size == 1 -> resultScope = Scopes.scopeFor(results) - // } - // } - // - // // Members - // val type = (receiver.type() as? NamedType) ?: return resultScope + // Call results + let resultScope = EMPTY_SCOPE; + if (isSdsCall(node.receiver)) { + const callable = this.nodeMapper.callToCallableOrUndefined(node.receiver); + const results = abstractResultsOrEmpty(callable); + + if (results.length === 0) { + return EMPTY_SCOPE; + } else if (results.length > 1) { + return this.createScopeForNodes(results); + } else { + // If there is only one result, it can be accessed by name but members of the result with the same name + // take precedence. + resultScope = this.createScopeForNodes(results); + } + } + + // // Members + // val type = (receiver.type() as? NamedType) ?: return resultScope // - // return when { - // type.isNullable && !context.isNullSafe -> resultScope - // type is ClassType -> { - // val members = type.sdsClass.classMembersOrEmpty().filter { !it.isStatic() } - // val superTypeMembers = type.sdsClass.superClassMembers() - // .filter { !it.isStatic() } - // .toList() + // return when { + // type.isNullable && !context.isNullSafe -> resultScope + // type is ClassType -> { + // val members = type.sdsClass.classMembersOrEmpty().filter { !it.isStatic() } + // val superTypeMembers = type.sdsClass.superClassMembers() + // .filter { !it.isStatic() } + // .toList() // - // Scopes.scopeFor(members, Scopes.scopeFor(superTypeMembers, resultScope)) - // } - // type is EnumVariantType -> Scopes.scopeFor(type.sdsEnumVariant.parametersOrEmpty()) - // else -> resultScope + // Scopes.scopeFor(members, Scopes.scopeFor(superTypeMembers, resultScope)) // } + // type is EnumVariantType -> Scopes.scopeFor(type.sdsEnumVariant.parametersOrEmpty()) + // else -> resultScope + // } - return EMPTY_SCOPE; + return resultScope; } /** diff --git a/tests/resources/scoping/member accesses/skip-main.sdstest b/tests/resources/scoping/member accesses/skip-main.sdstest index d48b0881b..eaadc197c 100644 --- a/tests/resources/scoping/member accesses/skip-main.sdstest +++ b/tests/resources/scoping/member accesses/skip-main.sdstest @@ -428,195 +428,6 @@ inner class Reference { references.shouldHaveSize(1) references[0].declaration.shouldNotBeResolved() } - - @Test - fun `should resolve result of callable type with one result without matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val singleResult = step.findUniqueDeclarationOrFail("singleResult") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(singleResult) - } - - @Test - fun `should resolve attribute for callable type with one result with matching class attribute`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result for callable type with one result with matching enum variant`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val callableWithOneResultWithIdenticalEnumVariant = - step.findUniqueDeclarationOrFail("callableWithOneResultWithIdenticalEnumVariant") - val result = - callableWithOneResultWithIdenticalEnumVariant.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of callable type with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val result1 = step.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } - - @Test - fun `should resolve result of function with one result without matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToFunctionResults") - val globalFunctionResultInSameFile = - findUniqueDeclarationOrFail("globalFunctionResultInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(globalFunctionResultInSameFile) - } - - @Test - fun `should resolve member for function with one result with matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToFunctionResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of function with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToFunctionResults") - val globalFunctionWithTwoResults = - findUniqueDeclarationOrFail("globalFunctionWithTwoResults") - val result1 = globalFunctionWithTwoResults.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } - - @Test - fun `should resolve result of lambda with one result without matching member`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToLambdaResults") - val singleResult = step.findUniqueDeclarationOrFail("singleResult") - - val references = step.descendants().toList() - references.shouldHaveSize(7) - - val declaration = references[2].declaration - declaration.shouldBeResolved() - declaration.shouldBe(singleResult) - } - - @Test - fun `should resolve member for lambda with one result with matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToLambdaResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(7) - - val declaration = references[4].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of lambda with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToLambdaResults") - val result1 = step.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(7) - - val declaration = references[6].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } - - @Test - fun `should resolve result of step with one result without matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStepResults") - val stepResultInSameFile = findUniqueDeclarationOrFail("stepResultInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(stepResultInSameFile) - } - - @Test - fun `should resolve member for step with one result with matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStepResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of step with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStepResults") - val stepInSameFileWithTwoResults = - findUniqueDeclarationOrFail("stepWithTwoResults") - val result1 = stepInSameFileWithTwoResults.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } } } */ @@ -781,47 +592,3 @@ step referencesToInstanceClassMembersFromClass() { ClassInSameFile.superClassInstanceAttribute; ClassInSameFile.superClassInstanceMethod(); } - - -// Access to results of callable ----------------------------------------------- - -step referencesToCallableTypeResults( - callableWithOneResult: () -> (singleResult: Int), - callableWithOneResultWithIdenticalClassAttribute: () -> (result: ClassForResultMemberAccess), - callableWithOneResultWithIdenticalEnumVariant: () -> (result: EnumForResultMemberAccess), - callableWithTwoResults: () -> (result1: Int, result2: Int) -) { - callableWithOneResult().singleResult; - callableWithOneResultWithIdenticalClassAttribute().result; - callableWithOneResultWithIdenticalEnumVariant().result; - callableWithTwoResults().result1; -} - -step referencesToFunctionResults() { - globalFunctionInSameFile(1).globalFunctionResultInSameFile; - globalFunctionWithOneResultWithIdenticalMember().result; - globalFunctionWithTwoResults().result1; -} - -step referencesToLambdaResults() { - val lambdaWithOneResult = () { - yield singleResult = 1; - }; - val lambdaWithOneResultWithIdenticalMember = () { - yield result = ClassForResultMemberAccess(); - }; - val lambdaWithTwoResults = () { - yield result1 = 1; - yield result2 = 1; - }; - - lambdaWithOneResult().singleResult; - lambdaWithOneResultWithIdenticalMember().result; - lambdaWithTwoResults().result1; -} - -step referencesToStepResults() { - stepInSameFile(1).stepResultInSameFile; - stepWithOneResultWithIdenticalMember().result; - stepWithTwoResults().result1; -} diff --git a/tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest b/tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest index 88ad96660..208ee9b3d 100644 --- a/tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest +++ b/tests/resources/scoping/member accesses/to results/of block lambdas/main.sdstest @@ -1,643 +1,20 @@ package tests.scoping.memberAccesses.toResults.ofBlockLambdas -/* -inner class Reference { - - @Nested - inner class MemberAccess { - - @Test - fun `should resolve static class attribute accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classStaticAttributeInSameFile = - findUniqueDeclarationOrFail("classStaticAttributeInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classStaticAttributeInSameFile) - } - - @Test - fun `should resolve instance class attribute accessed from class instance`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classInstanceAttributeInSameFile = - findUniqueDeclarationOrFail("classInstanceAttributeInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInstanceAttributeInSameFile) - } - - @Test - fun `should resolve nested class accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classInClassInSameFile = - findUniqueDeclarationOrFail("ClassInClassInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInClassInSameFile) - } - - @Test - fun `should resolve nested enum accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val enumInClassInSameFile = - findUniqueDeclarationOrFail("EnumInClassInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumInClassInSameFile) - } - - @Test - fun `should resolve static class method accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classStaticMethodInSameFile = - findUniqueDeclarationOrFail("classStaticMethodInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[9].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classStaticMethodInSameFile) - } - - @Test - fun `should resolve instance class method accessed from class instance`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToClassMembers") - val classInstanceMethodInSameFile = - findUniqueDeclarationOrFail("classInstanceMethodInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[11].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInstanceMethodInSameFile) - } - - @Test - fun `should resolve enum variants`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToEnumVariants") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val references = step.body.descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants from step annotation`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToEnumVariants") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val annotations = step.annotationCallsOrEmpty() - annotations.shouldHaveSize(1) - - val references = annotations[0].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants from parameter annotation`() = withResource(REFERENCE) { - val parameter = - findUniqueDeclarationOrFail("referenceToEnumVariantFromParameterAnnotation") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val annotations = parameter.annotationCallsOrEmpty() - annotations.shouldHaveSize(1) - - val references = annotations[0].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants of nested enum from class annotation`() = withResource(REFERENCE) { - val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") - val enumVariantInSameFile = - findUniqueDeclarationOrFail("EnumVariantInSameClass") - - val annotations = parameter.annotationCallsOrEmpty() - annotations.shouldHaveSize(2) - - val references = annotations[0].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameFile) - } - - @Test - fun `should resolve enum variants of global enum from class annotation`() = withResource(REFERENCE) { - val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") - val enumVariantInSameClass = - findUniqueDeclarationOrFail("EnumVariantInSameFile") - - val annotations = parameter.annotationCallsOrEmpty() - annotations.shouldHaveSize(2) - - val references = annotations[1].descendants().toList() - references.shouldHaveSize(2) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantInSameClass) - } - - @Test - fun `should resolve parameters of enum variants`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToEnumVariantParameters") - val enumVariantParameterInSameFile = - findUniqueDeclarationOrFail("enumVariantParameterInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(3) - - val declaration = references[2].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumVariantParameterInSameFile) - } - - @Test - fun `should resolve inherited static class attribute accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassStaticAttribute = - findUniqueDeclarationOrFail("superClassStaticAttribute") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassStaticAttribute) - } - - @Test - fun `should resolve inherited instance class attribute accessed from class instance`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassInstanceAttribute = - findUniqueDeclarationOrFail("superClassInstanceAttribute") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassInstanceAttribute) - } - - @Test - fun `should resolve inherited nested class accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val classInSuperClass = - findUniqueDeclarationOrFail("ClassInSuperClass") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(classInSuperClass) - } - - @Test - fun `should resolve inherited nested enum accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val enumInSuperClass = - findUniqueDeclarationOrFail("EnumInSuperClass") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(enumInSuperClass) - } - - @Test - fun `should resolve inherited static class method accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassStaticMethod = - findUniqueDeclarationOrFail("superClassStaticMethod") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[9].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassStaticMethod) - } - - @Test - fun `should resolve inherited instance class method accessed from class instance`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") - val superClassInstanceMethod = - findUniqueDeclarationOrFail("superClassInstanceMethod") - - val references = step.descendants().toList() - references.shouldHaveSize(12) - - val declaration = references[11].declaration - declaration.shouldBeResolved() - declaration.shouldBe(superClassInstanceMethod) - } - - @Test - fun `should resolve overridden instance attribute`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") - val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") - val instanceAttributeForOverriding = - subClassForOverriding.findUniqueDeclarationOrFail("instanceAttributeForOverriding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(instanceAttributeForOverriding) - } - - @Test - fun `should resolve overridden instance method`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") - val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") - val instanceMethodForOverriding = - subClassForOverriding.findUniqueDeclarationOrFail("instanceMethodForOverriding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(instanceMethodForOverriding) - } - - @Test - fun `should resolve hidden static attribute`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val staticAttributeForHiding = - subClassForHiding.findUniqueDeclarationOrFail("staticAttributeForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(staticAttributeForHiding) - } - - @Test - fun `should resolve hidden nested class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val nestedClassForHiding = - subClassForHiding.findUniqueDeclarationOrFail("NestedClassForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(nestedClassForHiding) - } - - @Test - fun `should resolve hidden nested enum`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val nestedEnumForHiding = - subClassForHiding.findUniqueDeclarationOrFail("NestedEnumForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(nestedEnumForHiding) - } - - @Test - fun `should resolve hidden static method`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") - val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") - val staticMethodForHiding = - subClassForHiding.findUniqueDeclarationOrFail("staticMethodForHiding") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(staticMethodForHiding) - } - - @Test - fun `should not resolve static class members accessed from instance`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStaticClassMembersFromInstance") - val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") - - val references = step.descendants() - .filter { it.declaration != classInSameFile } - .toList() - references.shouldHaveSize(8) - references.forEachAsClue { - it.declaration.shouldNotBeResolved() - } - } - - @Test - fun `should not resolve instance class members accessed from class`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToInstanceClassMembersFromClass") - val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") - - val references = step.descendants() - .filter { it.declaration != classInSameFile } - .toList() - references.shouldHaveSize(4) - references.forEachAsClue { - it.declaration.shouldNotBeResolved() - } - } - - @Test - fun `should not resolve class members with unqualified access`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("unqualifiedReferencesToClassMembers") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - references.forEachAsClue { - it.declaration.shouldNotBeResolved() - } - } - - @Test - fun `should not resolve enum variants with unqualified access`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariants") - - val references = step.descendants().toList() - references.shouldHaveSize(1) - references[0].declaration.shouldNotBeResolved() - } - - @Test - fun `should not resolve parameters of enum variants with unqualified access`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariantParameters") - - val references = step.descendants().toList() - references.shouldHaveSize(1) - references[0].declaration.shouldNotBeResolved() - } - - @Test - fun `should resolve result of callable type with one result without matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val singleResult = step.findUniqueDeclarationOrFail("singleResult") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(singleResult) - } - - @Test - fun `should resolve attribute for callable type with one result with matching class attribute`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result for callable type with one result with matching enum variant`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val callableWithOneResultWithIdenticalEnumVariant = - step.findUniqueDeclarationOrFail("callableWithOneResultWithIdenticalEnumVariant") - val result = - callableWithOneResultWithIdenticalEnumVariant.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of callable type with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") - val result1 = step.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(8) - - val declaration = references[7].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } - - @Test - fun `should resolve result of function with one result without matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToFunctionResults") - val globalFunctionResultInSameFile = - findUniqueDeclarationOrFail("globalFunctionResultInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(globalFunctionResultInSameFile) - } - - @Test - fun `should resolve member for function with one result with matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToFunctionResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of function with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToFunctionResults") - val globalFunctionWithTwoResults = - findUniqueDeclarationOrFail("globalFunctionWithTwoResults") - val result1 = globalFunctionWithTwoResults.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } - - @Test - fun `should resolve result of lambda with one result without matching member`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToLambdaResults") - val singleResult = step.findUniqueDeclarationOrFail("singleResult") - - val references = step.descendants().toList() - references.shouldHaveSize(7) - - val declaration = references[2].declaration - declaration.shouldBeResolved() - declaration.shouldBe(singleResult) - } - - @Test - fun `should resolve member for lambda with one result with matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToLambdaResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(7) - - val declaration = references[4].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of lambda with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToLambdaResults") - val result1 = step.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(7) - - val declaration = references[6].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } - - @Test - fun `should resolve result of step with one result without matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStepResults") - val stepResultInSameFile = findUniqueDeclarationOrFail("stepResultInSameFile") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[1].declaration - declaration.shouldBeResolved() - declaration.shouldBe(stepResultInSameFile) - } - - @Test - fun `should resolve member for step with one result with matching member`() = - withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStepResults") - val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") - val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[3].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result) - } - - @Test - fun `should resolve result of step with multiple results`() = withResource(REFERENCE) { - val step = findUniqueDeclarationOrFail("referencesToStepResults") - val stepInSameFileWithTwoResults = - findUniqueDeclarationOrFail("stepWithTwoResults") - val result1 = stepInSameFileWithTwoResults.findUniqueDeclarationOrFail("result1") - - val references = step.descendants().toList() - references.shouldHaveSize(6) - - val declaration = references[5].declaration - declaration.shouldBeResolved() - declaration.shouldBe(result1) - } - } - } -*/ - -class ClassForResultMemberAccess() { - attr result: Int -} -enum EnumForResultMemberAccess { - result -} - pipeline myPipeline { val lambdaWithOneResult = () { - yield singleResult = 1; - }; - val lambdaWithOneResultWithIdenticalMember = () { - yield result = ClassForResultMemberAccess(); + // $TEST$ target lambdaWithOneResult_singleResult + yield »singleResult« = 1; }; val lambdaWithTwoResults = () { + // $TEST$ target lambdaWithTwoResults_result1 + yield »result1« = 1; yield result1 = 1; yield result2 = 1; }; - lambdaWithOneResult().singleResult; - lambdaWithOneResultWithIdenticalMember().result; - lambdaWithTwoResults().result1; -} + // $TEST$ references lambdaWithOneResult_singleResult + lambdaWithOneResult().»singleResult«; -step referencesToStepResults() { - stepInSameFile(1).stepResultInSameFile; - stepWithOneResultWithIdenticalMember().result; - stepWithTwoResults().result1; + // $TEST$ references lambdaWithTwoResults_result1 + lambdaWithTwoResults().»result1«; } diff --git a/tests/resources/scoping/member accesses/to results/of callable types/main.sdstest b/tests/resources/scoping/member accesses/to results/of callable types/main.sdstest new file mode 100644 index 000000000..1e4d3c66f --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/of callable types/main.sdstest @@ -0,0 +1,15 @@ +package tests.scoping.memberAccesses.toResults.ofCallableTypes + +segment mySegment( + // $TEST$ target callableWithOneResult_singleResult + callableWithOneResult: () -> »singleResult«: Int, + + // $TEST$ target callableWithTwoResults_result1 + callableWithTwoResults: () -> (»result1«: Int, result2: Int), +) { + // $TEST$ references callableWithOneResult_singleResult + callableWithOneResult().»singleResult«; + + // $TEST$ references callableWithTwoResults_result1 + callableWithTwoResults().»result1«; +} diff --git a/tests/resources/scoping/member accesses/to results/of functions/main.sdstest b/tests/resources/scoping/member accesses/to results/of functions/main.sdstest new file mode 100644 index 000000000..eeba6f3f9 --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/of functions/main.sdstest @@ -0,0 +1,15 @@ +package tests.scoping.memberAccesses.toResults.ofFunctions + +// $TEST$ target functionWithOneResult_singleResult +fun functionWithOneResult() -> »singleResult«: Int + +// $TEST$ target functionWithTwoResults_result1 +fun functionWithTwoResults() -> (»result1«: Int, result2: Int) + +pipeline myPipeline { + // $TEST$ references functionWithOneResult_singleResult + functionWithOneResult().»singleResult«; + + // $TEST$ references functionWithTwoResults_result1 + functionWithTwoResults().»result1«; +} diff --git a/tests/resources/scoping/member accesses/to results/of segments/main.sdstest b/tests/resources/scoping/member accesses/to results/of segments/main.sdstest new file mode 100644 index 000000000..ca1dbcaa5 --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/of segments/main.sdstest @@ -0,0 +1,15 @@ +package tests.scoping.memberAccesses.toResults.ofSegments + +// $TEST$ target segmentWithOneResult_singleResult +segment segmentWithOneResult() -> »singleResult«: Int {} + +// $TEST$ target segmentWithTwoResults_result1 +segment segmentWithTwoResults() -> (»result1«: Int, result2: Int) {} + +pipeline myPipeline { + // $TEST$ references segmentWithOneResult_singleResult + segmentWithOneResult().»singleResult«; + + // $TEST$ references segmentWithTwoResults_result1 + segmentWithTwoResults().»result1«; +} diff --git a/tests/resources/scoping/member accesses/to results/skip-of block lambdas (matching member)/main.sdstest b/tests/resources/scoping/member accesses/to results/skip-of block lambdas (matching member)/main.sdstest new file mode 100644 index 000000000..fb53b4374 --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/skip-of block lambdas (matching member)/main.sdstest @@ -0,0 +1,15 @@ +package tests.scoping.memberAccesses.toResults.ofBlockLambdas.matchingMember + +class MyClass() { + // $TEST$ target MyClass_result + attr »result«: Int +} + +pipeline myPipeline { + val lambdaWithOneResultWithIdenticalMember = () { + yield result = MyClass(); + }; + + // $TEST$ references MyClass_result + lambdaWithOneResultWithIdenticalMember().»result«; +} diff --git a/tests/resources/scoping/member accesses/to results/skip-of callable types (matching member)/main.sdstest b/tests/resources/scoping/member accesses/to results/skip-of callable types (matching member)/main.sdstest new file mode 100644 index 000000000..3d14a472b --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/skip-of callable types (matching member)/main.sdstest @@ -0,0 +1,13 @@ +package tests.scoping.memberAccesses.toResults.ofCallableTypes.matchingMember + +class MyClass() { + // $TEST$ target MyClass_result + attr »result«: Int +} + +segment mySegment( + callableWithIdenticalMember: () -> result: MyClass +) { + // $TEST$ references MyClass_result + callableWithIdenticalMember().»result«; +} diff --git a/tests/resources/scoping/member accesses/to results/skip-of functions (matching member)/main.sdstest b/tests/resources/scoping/member accesses/to results/skip-of functions (matching member)/main.sdstest new file mode 100644 index 000000000..6ac04e80e --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/skip-of functions (matching member)/main.sdstest @@ -0,0 +1,13 @@ +package tests.scoping.memberAccesses.toResults.ofFunctions.matchingMember + +class MyClass() { + // $TEST$ target MyClass_result + attr »result«: Int +} + +fun functionWithOneResultWithIdenticalMember() -> result: MyClass + +pipeline myPipeline { + // $TEST$ references MyClass_result + functionWithOneResultWithIdenticalMember().»result«; +} diff --git a/tests/resources/scoping/member accesses/to results/skip-of segments (matching member)/main.sdstest b/tests/resources/scoping/member accesses/to results/skip-of segments (matching member)/main.sdstest new file mode 100644 index 000000000..c90dae517 --- /dev/null +++ b/tests/resources/scoping/member accesses/to results/skip-of segments (matching member)/main.sdstest @@ -0,0 +1,13 @@ +package tests.scoping.memberAccesses.toResults.ofSegments.matchingMember + +class MyClass() { + // $TEST$ target MyClass_result + attr »result«: Int +} + +segment segmentWithOneResultWithIdenticalMember() -> result: MyClass {} + +pipeline myPipeline { + // $TEST$ references MyClass_result + segmentWithOneResultWithIdenticalMember().»result«; +} From bf88e1f6be755394161d87dc54b30e799a85de7c Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 22:16:06 +0200 Subject: [PATCH 08/13] fix: bind caches to workspace --- src/language/builtins/safe-ds-annotations.ts | 6 ++++-- src/language/builtins/safe-ds-classes.ts | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/language/builtins/safe-ds-annotations.ts b/src/language/builtins/safe-ds-annotations.ts index 10b52d345..cef387bc8 100644 --- a/src/language/builtins/safe-ds-annotations.ts +++ b/src/language/builtins/safe-ds-annotations.ts @@ -1,17 +1,18 @@ import { SafeDsServices } from '../safe-ds-module.js'; import { resolveRelativePathToBuiltinFile } from './fileFinder.js'; import { isSdsAnnotation, isSdsModule, SdsAnnotatedObject, SdsAnnotation } from '../generated/ast.js'; -import { LangiumDocuments } from 'langium'; +import { LangiumDocuments, WorkspaceCache } from 'langium'; import { annotationCallsOrEmpty, moduleMembersOrEmpty } from '../helpers/nodeProperties.js'; const CORE_ANNOTATIONS_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreAnnotations.sdsstub'); export class SafeDsAnnotations { private readonly langiumDocuments: LangiumDocuments; - private readonly cache: Map = new Map(); + private readonly cache: WorkspaceCache; constructor(services: SafeDsServices) { this.langiumDocuments = services.shared.workspace.LangiumDocuments; + this.cache = new WorkspaceCache(services.shared); } isDeprecated(node: SdsAnnotatedObject | undefined): boolean { @@ -59,6 +60,7 @@ export class SafeDsAnnotations { return undefined; } + this.cache.set(name, firstMatchingModuleMember); return firstMatchingModuleMember; } } diff --git a/src/language/builtins/safe-ds-classes.ts b/src/language/builtins/safe-ds-classes.ts index cf5831e5a..5645d1ada 100644 --- a/src/language/builtins/safe-ds-classes.ts +++ b/src/language/builtins/safe-ds-classes.ts @@ -1,17 +1,18 @@ import { SafeDsServices } from '../safe-ds-module.js'; import { resolveRelativePathToBuiltinFile } from './fileFinder.js'; import { isSdsClass, isSdsModule, SdsClass } from '../generated/ast.js'; -import { LangiumDocuments } from 'langium'; +import { LangiumDocuments, WorkspaceCache } from 'langium'; import { moduleMembersOrEmpty } from '../helpers/nodeProperties.js'; const CORE_CLASSES_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreClasses.sdsstub'); export class SafeDsClasses { private readonly langiumDocuments: LangiumDocuments; - private readonly cache: Map = new Map(); + private readonly cache: WorkspaceCache; constructor(services: SafeDsServices) { this.langiumDocuments = services.shared.workspace.LangiumDocuments; + this.cache = new WorkspaceCache(services.shared); } /* c8 ignore start */ @@ -42,9 +43,11 @@ export class SafeDsClasses { } private getClass(name: string): SdsClass | undefined { + /* c8 ignore start */ if (this.cache.has(name)) { return this.cache.get(name); } + /* c8 ignore stop */ if (!this.langiumDocuments.hasDocument(CORE_CLASSES_URI)) { /* c8 ignore next 2 */ @@ -64,6 +67,7 @@ export class SafeDsClasses { return undefined; } + this.cache.set(name, firstMatchingModuleMember); return firstMatchingModuleMember; } } From dd9253ce05cbfb23fa03ab3fce392d00f0bd7087 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 22:44:33 +0200 Subject: [PATCH 09/13] refactor: move cache handling to common superclass --- src/language/builtins/safe-ds-annotations.ts | 42 +++--------------- src/language/builtins/safe-ds-classes.ts | 43 ++----------------- .../builtins/safe-ds-module-members.ts | 43 +++++++++++++++++++ 3 files changed, 52 insertions(+), 76 deletions(-) create mode 100644 src/language/builtins/safe-ds-module-members.ts diff --git a/src/language/builtins/safe-ds-annotations.ts b/src/language/builtins/safe-ds-annotations.ts index cef387bc8..2b54b241c 100644 --- a/src/language/builtins/safe-ds-annotations.ts +++ b/src/language/builtins/safe-ds-annotations.ts @@ -1,20 +1,11 @@ -import { SafeDsServices } from '../safe-ds-module.js'; import { resolveRelativePathToBuiltinFile } from './fileFinder.js'; -import { isSdsAnnotation, isSdsModule, SdsAnnotatedObject, SdsAnnotation } from '../generated/ast.js'; -import { LangiumDocuments, WorkspaceCache } from 'langium'; -import { annotationCallsOrEmpty, moduleMembersOrEmpty } from '../helpers/nodeProperties.js'; +import { isSdsAnnotation, SdsAnnotatedObject, SdsAnnotation } from '../generated/ast.js'; +import { annotationCallsOrEmpty } from '../helpers/nodeProperties.js'; +import { SafeDsModuleMembers } from './safe-ds-module-members.js'; const CORE_ANNOTATIONS_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreAnnotations.sdsstub'); -export class SafeDsAnnotations { - private readonly langiumDocuments: LangiumDocuments; - private readonly cache: WorkspaceCache; - - constructor(services: SafeDsServices) { - this.langiumDocuments = services.shared.workspace.LangiumDocuments; - this.cache = new WorkspaceCache(services.shared); - } - +export class SafeDsAnnotations extends SafeDsModuleMembers { isDeprecated(node: SdsAnnotatedObject | undefined): boolean { return annotationCallsOrEmpty(node).some((it) => { const annotation = it.annotation?.ref; @@ -38,29 +29,6 @@ export class SafeDsAnnotations { } private getAnnotation(name: string): SdsAnnotation | undefined { - if (this.cache.has(name)) { - return this.cache.get(name); - } - - if (!this.langiumDocuments.hasDocument(CORE_ANNOTATIONS_URI)) { - /* c8 ignore next 2 */ - return undefined; - } - - const document = this.langiumDocuments.getOrCreateDocument(CORE_ANNOTATIONS_URI); - const root = document.parseResult.value; - if (!isSdsModule(root)) { - /* c8 ignore next 2 */ - return undefined; - } - - const firstMatchingModuleMember = moduleMembersOrEmpty(root).find((m) => m.name === name); - if (!isSdsAnnotation(firstMatchingModuleMember)) { - /* c8 ignore next 2 */ - return undefined; - } - - this.cache.set(name, firstMatchingModuleMember); - return firstMatchingModuleMember; + return this.getModuleMember(CORE_ANNOTATIONS_URI, name, isSdsAnnotation); } } diff --git a/src/language/builtins/safe-ds-classes.ts b/src/language/builtins/safe-ds-classes.ts index 5645d1ada..a42551246 100644 --- a/src/language/builtins/safe-ds-classes.ts +++ b/src/language/builtins/safe-ds-classes.ts @@ -1,20 +1,10 @@ -import { SafeDsServices } from '../safe-ds-module.js'; import { resolveRelativePathToBuiltinFile } from './fileFinder.js'; -import { isSdsClass, isSdsModule, SdsClass } from '../generated/ast.js'; -import { LangiumDocuments, WorkspaceCache } from 'langium'; -import { moduleMembersOrEmpty } from '../helpers/nodeProperties.js'; +import { isSdsClass, SdsClass } from '../generated/ast.js'; +import { SafeDsModuleMembers } from './safe-ds-module-members.js'; const CORE_CLASSES_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreClasses.sdsstub'); -export class SafeDsClasses { - private readonly langiumDocuments: LangiumDocuments; - private readonly cache: WorkspaceCache; - - constructor(services: SafeDsServices) { - this.langiumDocuments = services.shared.workspace.LangiumDocuments; - this.cache = new WorkspaceCache(services.shared); - } - +export class SafeDsClasses extends SafeDsModuleMembers { /* c8 ignore start */ get Any(): SdsClass | undefined { return this.getClass('Any'); @@ -43,31 +33,6 @@ export class SafeDsClasses { } private getClass(name: string): SdsClass | undefined { - /* c8 ignore start */ - if (this.cache.has(name)) { - return this.cache.get(name); - } - /* c8 ignore stop */ - - if (!this.langiumDocuments.hasDocument(CORE_CLASSES_URI)) { - /* c8 ignore next 2 */ - return undefined; - } - - const document = this.langiumDocuments.getOrCreateDocument(CORE_CLASSES_URI); - const root = document.parseResult.value; - if (!isSdsModule(root)) { - /* c8 ignore next 2 */ - return undefined; - } - - const firstMatchingModuleMember = moduleMembersOrEmpty(root).find((m) => m.name === name); - if (!isSdsClass(firstMatchingModuleMember)) { - /* c8 ignore next 2 */ - return undefined; - } - - this.cache.set(name, firstMatchingModuleMember); - return firstMatchingModuleMember; + return this.getModuleMember(CORE_CLASSES_URI, name, isSdsClass); } } diff --git a/src/language/builtins/safe-ds-module-members.ts b/src/language/builtins/safe-ds-module-members.ts new file mode 100644 index 000000000..92d4abee3 --- /dev/null +++ b/src/language/builtins/safe-ds-module-members.ts @@ -0,0 +1,43 @@ +import { SafeDsServices } from '../safe-ds-module.js'; +import { isSdsModule, SdsModuleMember } from '../generated/ast.js'; +import { LangiumDocuments, URI, WorkspaceCache } from 'langium'; +import { moduleMembersOrEmpty } from '../helpers/nodeProperties.js'; + +export abstract class SafeDsModuleMembers { + private readonly langiumDocuments: LangiumDocuments; + private readonly cache: WorkspaceCache; + + constructor(services: SafeDsServices) { + this.langiumDocuments = services.shared.workspace.LangiumDocuments; + this.cache = new WorkspaceCache(services.shared); + } + + protected getModuleMember(uri: URI, name: string, predicate: (node: unknown) => node is T): T | undefined { + const key = `${uri.toString()}#${name}`; + + if (this.cache.has(key)) { + return this.cache.get(name); + } + + if (!this.langiumDocuments.hasDocument(uri)) { + /* c8 ignore next 2 */ + return undefined; + } + + const document = this.langiumDocuments.getOrCreateDocument(uri); + const root = document.parseResult.value; + if (!isSdsModule(root)) { + /* c8 ignore next 2 */ + return undefined; + } + + const firstMatchingModuleMember = moduleMembersOrEmpty(root).find((m) => m.name === name); + if (!predicate(firstMatchingModuleMember)) { + /* c8 ignore next 2 */ + return undefined; + } + + this.cache.set(name, firstMatchingModuleMember); + return firstMatchingModuleMember; + } +} From 59dee9ff6350fa573fe0f6a1369a9eaca32759b1 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 22:45:37 +0200 Subject: [PATCH 10/13] style: fix linter issues --- .../assigneeToAssignedObjectOrUndefined.test.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts b/tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts index 9b136d290..288dd77b7 100644 --- a/tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts +++ b/tests/language/helpers/safe-ds-node-mapper/assigneeToAssignedObjectOrUndefined.test.ts @@ -1,7 +1,6 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createSafeDsServices } from '../../../../src/language/safe-ds-module.js'; import { clearDocuments } from 'langium/test'; -import { EmptyFileSystem } from 'langium'; import { getNodeOfType } from '../../../helpers/nodeFinder.js'; import { isSdsAbstractResult, @@ -32,7 +31,7 @@ describe('SafeDsNodeMapper', () => { it.each([ { - name: "no value", + name: 'no value', code: ` fun f() @@ -42,7 +41,7 @@ describe('SafeDsNodeMapper', () => { `, }, { - name: "only one value", + name: 'only one value', code: ` segment mySegment() { _, val a = 1; @@ -98,7 +97,7 @@ describe('SafeDsNodeMapper', () => { val a, val b = f(); }; `, - expected: ["r1", "r2"], + expected: ['r1', 'r2'], index: 3, }, { @@ -108,7 +107,7 @@ describe('SafeDsNodeMapper', () => { val a, val b = f(); }; `, - expected: ["r1", "r2"], + expected: ['r1', 'r2'], }, { name: 'function (one result)', @@ -119,7 +118,7 @@ describe('SafeDsNodeMapper', () => { val a = f(); }; `, - expected: ["r1"], + expected: ['r1'], }, { name: 'function (multiple results)', @@ -130,7 +129,7 @@ describe('SafeDsNodeMapper', () => { val a, val b = f(); }; `, - expected: ["r1", "r2"], + expected: ['r1', 'r2'], }, { name: 'segment', @@ -141,7 +140,7 @@ describe('SafeDsNodeMapper', () => { val a, val b = s(); }; `, - expected: ["r1", "r2"], + expected: ['r1', 'r2'], }, ])( 'should return the corresponding result if the RHS is a call of a $name', From d39e3d2678e5cf5bc9b17f10aa0fbf46f76fb8ed Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 22:50:41 +0200 Subject: [PATCH 11/13] fix: remove caching of primitive types This will not work when we check type conformance later. --- src/language/typing/safe-ds-type-computer.ts | 39 ++++---------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/src/language/typing/safe-ds-type-computer.ts b/src/language/typing/safe-ds-type-computer.ts index 79fea1b3f..ebdf168cd 100644 --- a/src/language/typing/safe-ds-type-computer.ts +++ b/src/language/typing/safe-ds-type-computer.ts @@ -377,7 +377,7 @@ export class SafeDsTypeComputer { const leftOperandType = this.computeType(node.leftOperand); const rightOperandType = this.computeType(node.rightOperand); - if (leftOperandType === this.Int() && rightOperandType === this.Int()) { + if (leftOperandType.equals(this.Int()) && rightOperandType.equals(this.Int())) { return this.Int(); } else { return this.Float(); @@ -398,7 +398,7 @@ export class SafeDsTypeComputer { private computeTypeOfArithmeticPrefixOperation(node: SdsPrefixOperation): Type { const leftOperandType = this.computeType(node.operand); - if (leftOperandType === this.Int()) { + if (leftOperandType.equals(this.Int())) { return this.Int(); } else { return this.Float(); @@ -501,49 +501,24 @@ export class SafeDsTypeComputer { // Builtin types // ----------------------------------------------------------------------------------------------------------------- - private cachedBoolean: Type = UnknownType; - private Boolean(): Type { - if (this.cachedBoolean === UnknownType) { - this.cachedBoolean = this.createCoreType(this.coreClasses.Boolean); - } - return this.cachedBoolean; + return this.createCoreType(this.coreClasses.Boolean); } - private cachedFloat: Type = UnknownType; - private Float(): Type { - if (this.cachedFloat === UnknownType) { - this.cachedFloat = this.createCoreType(this.coreClasses.Float); - } - return this.cachedFloat; + return this.createCoreType(this.coreClasses.Float); } - private cachedInt: Type = UnknownType; - private Int(): Type { - if (this.cachedInt === UnknownType) { - this.cachedInt = this.createCoreType(this.coreClasses.Int); - } - return this.cachedInt; + return this.createCoreType(this.coreClasses.Int); } - private cachedNothingOrNull: Type = UnknownType; - private NothingOrNull(): Type { - if (this.cachedNothingOrNull === UnknownType) { - this.cachedNothingOrNull = this.createCoreType(this.coreClasses.Nothing, true); - } - return this.cachedNothingOrNull; + return this.createCoreType(this.coreClasses.Nothing, true); } - private cachedString: Type = UnknownType; - private String(): Type { - if (this.cachedString === UnknownType) { - this.cachedString = this.createCoreType(this.coreClasses.String); - } - return this.cachedString; + return this.createCoreType(this.coreClasses.String); } private createCoreType(coreClass: SdsClass | undefined, isNullable: boolean = false): Type { From 4bb09a3f5c35890bbadd0127095a00740a400798 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Fri, 6 Oct 2023 20:52:50 +0000 Subject: [PATCH 12/13] style: apply automated linter fixes --- .../scoping/safe-ds-scope-provider.ts | 3 +- src/language/typing/model.ts | 2 +- .../validation/builtins/deprecated.ts | 53 ++++++++++--------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/language/scoping/safe-ds-scope-provider.ts b/src/language/scoping/safe-ds-scope-provider.ts index f1efbc7d7..eb45363b0 100644 --- a/src/language/scoping/safe-ds-scope-provider.ts +++ b/src/language/scoping/safe-ds-scope-provider.ts @@ -13,7 +13,8 @@ import { isSdsAbstractCall, isSdsArgument, isSdsAssignment, - isSdsBlock, isSdsCall, + isSdsBlock, + isSdsCall, isSdsCallable, isSdsClass, isSdsEnum, diff --git a/src/language/typing/model.ts b/src/language/typing/model.ts index a7df36b50..16e190d7d 100644 --- a/src/language/typing/model.ts +++ b/src/language/typing/model.ts @@ -106,7 +106,7 @@ export class NamedTupleType extends Type { return this.entries[position]?.type; } - get length (): number { + get length(): number { return this.entries.length; } diff --git a/src/language/validation/builtins/deprecated.ts b/src/language/validation/builtins/deprecated.ts index 522853551..9b2335602 100644 --- a/src/language/validation/builtins/deprecated.ts +++ b/src/language/validation/builtins/deprecated.ts @@ -6,7 +6,8 @@ import { SdsAnnotationCall, SdsArgument, SdsAssignee, - SdsNamedType, SdsReference, + SdsNamedType, + SdsReference, } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; @@ -65,30 +66,32 @@ export const argumentCorrespondingParameterShouldNotBeDeprecated = } }; -export const namedTypeDeclarationShouldNotBeDeprecated = (services: SafeDsServices) => (node: SdsNamedType, accept: ValidationAcceptor) => { - const declaration = node.declaration.ref; - if (!declaration) { - return; - } +export const namedTypeDeclarationShouldNotBeDeprecated = + (services: SafeDsServices) => (node: SdsNamedType, accept: ValidationAcceptor) => { + const declaration = node.declaration.ref; + if (!declaration) { + return; + } - if (services.builtins.Annotations.isDeprecated(declaration)) { - accept('warning', `The referenced declaration '${declaration.name}' is deprecated.`, { - node, - code: CODE_DEPRECATED_REFERENCED_DECLARATION, - }); - } -}; + if (services.builtins.Annotations.isDeprecated(declaration)) { + accept('warning', `The referenced declaration '${declaration.name}' is deprecated.`, { + node, + code: CODE_DEPRECATED_REFERENCED_DECLARATION, + }); + } + }; -export const referenceTargetShouldNotBeDeprecated = (services: SafeDsServices) => (node: SdsReference, accept: ValidationAcceptor) => { - const target = node.target.ref; - if (!target || isSdsParameter(target)) { - return; - } +export const referenceTargetShouldNotBeDeprecated = + (services: SafeDsServices) => (node: SdsReference, accept: ValidationAcceptor) => { + const target = node.target.ref; + if (!target || isSdsParameter(target)) { + return; + } - if (services.builtins.Annotations.isDeprecated(target)) { - accept('warning', `The referenced declaration '${target.name}' is deprecated.`, { - node, - code: CODE_DEPRECATED_REFERENCED_DECLARATION, - }); - } -}; + if (services.builtins.Annotations.isDeprecated(target)) { + accept('warning', `The referenced declaration '${target.name}' is deprecated.`, { + node, + code: CODE_DEPRECATED_REFERENCED_DECLARATION, + }); + } + }; From 64b32f86a5560bc82b94e9450c2a4be5ea4af272 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 23:05:36 +0200 Subject: [PATCH 13/13] fix: use correct cache keys --- src/language/builtins/safe-ds-module-members.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/language/builtins/safe-ds-module-members.ts b/src/language/builtins/safe-ds-module-members.ts index 92d4abee3..743d6e5d0 100644 --- a/src/language/builtins/safe-ds-module-members.ts +++ b/src/language/builtins/safe-ds-module-members.ts @@ -16,7 +16,7 @@ export abstract class SafeDsModuleMembers { const key = `${uri.toString()}#${name}`; if (this.cache.has(key)) { - return this.cache.get(name); + return this.cache.get(key); } if (!this.langiumDocuments.hasDocument(uri)) { @@ -37,7 +37,7 @@ export abstract class SafeDsModuleMembers { return undefined; } - this.cache.set(name, firstMatchingModuleMember); + this.cache.set(key, firstMatchingModuleMember); return firstMatchingModuleMember; } }