From ea8fe29c87ee88a4b4e1fa29d243d8fc1ce78c66 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Tue, 24 Oct 2023 11:17:04 +0200 Subject: [PATCH] feat: error if type parameters don't have sufficient context (#687) ### Summary of Changes Show an error if we already know at the declaration-site that we won't be able to infer type parameters in a call. A type parameter must occur in the parameter list of the containing callable. However, it does not suffice if they appear inside a union type or the parameter list of a callable type. --- src/cli/generator.ts | 2 +- src/language/grammar/safe-ds.langium | 6 +- src/language/helpers/safe-ds-node-mapper.ts | 2 +- .../lsp/safe-ds-semantic-token-provider.ts | 4 +- .../scoping/safe-ds-scope-provider.ts | 10 +- src/language/typing/safe-ds-type-checker.ts | 1 + src/language/typing/safe-ds-type-computer.ts | 4 +- src/language/typing/typeConformance.ts | 141 ------------------ .../validation/builtins/deprecated.ts | 2 +- .../validation/builtins/experimental.ts | 2 +- .../experimentalLanguageFeatures.ts | 2 + src/language/validation/names.ts | 2 +- .../declarations/typeParameterConstraints.ts | 2 +- .../other/declarations/typeParameters.ts | 47 ++++++ .../validation/other/types/namedTypes.ts | 5 +- src/language/validation/safe-ds-validator.ts | 2 + src/language/validation/style.ts | 4 +- src/language/validation/types.ts | 4 +- .../literal types/main.sdstest | 2 +- .../union types/main.sdstest | 6 +- .../insufficient context/main.sdstest | 66 ++++++++ 21 files changed, 147 insertions(+), 169 deletions(-) delete mode 100644 src/language/typing/typeConformance.ts create mode 100644 src/language/validation/other/declarations/typeParameters.ts create mode 100644 tests/resources/validation/other/declarations/type parameters/insufficient context/main.sdstest diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 2d64c035a..5284ffb39 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -528,7 +528,7 @@ const getExternalReferenceNeededImport = function ( if (isSdsQualifiedImport(value)) { const importedDeclarations = getImportedDeclarations(value); for (const importedDeclaration of importedDeclarations) { - if (declaration === importedDeclaration.declaration.ref) { + if (declaration === importedDeclaration.declaration?.ref) { if (importedDeclaration.alias !== undefined) { return { importPath: services.builtins.Annotations.getPythonModule(targetModule) || value.package, diff --git a/src/language/grammar/safe-ds.langium b/src/language/grammar/safe-ds.langium index 22219846b..5623dfa28 100644 --- a/src/language/grammar/safe-ds.langium +++ b/src/language/grammar/safe-ds.langium @@ -77,7 +77,7 @@ SdsImportedDeclarationList returns SdsImportedDeclarationList: ; interface SdsImportedDeclaration extends SdsObject { - declaration: @SdsModuleMember; + declaration?: @SdsModuleMember; alias?: SdsImportedDeclarationAlias; } @@ -384,7 +384,7 @@ SdsConstraint returns SdsConstraint: ; interface SdsTypeParameterConstraint extends SdsConstraint { - leftOperand: @SdsTypeParameter + leftOperand?: @SdsTypeParameter operator: string rightOperand: SdsType } @@ -922,7 +922,7 @@ SdsLiteralList returns SdsLiteralList: ; interface SdsNamedType extends SdsType { - declaration: @SdsNamedTypeDeclaration + declaration?: @SdsNamedTypeDeclaration typeArgumentList?: SdsTypeArgumentList isNullable: boolean } diff --git a/src/language/helpers/safe-ds-node-mapper.ts b/src/language/helpers/safe-ds-node-mapper.ts index 98cf7a8a2..069291d74 100644 --- a/src/language/helpers/safe-ds-node-mapper.ts +++ b/src/language/helpers/safe-ds-node-mapper.ts @@ -239,7 +239,7 @@ export class SafeDsNodeMapper { } // Find type parameter at the same position - const namedTypeDeclaration = containingType.declaration.ref; + const namedTypeDeclaration = containingType.declaration?.ref; const typeParameters = getTypeParameters(namedTypeDeclaration); return typeParameters[typeArgumentPosition]; } diff --git a/src/language/lsp/safe-ds-semantic-token-provider.ts b/src/language/lsp/safe-ds-semantic-token-provider.ts index a2be26037..794e2281f 100644 --- a/src/language/lsp/safe-ds-semantic-token-provider.ts +++ b/src/language/lsp/safe-ds-semantic-token-provider.ts @@ -87,7 +87,7 @@ export class SafeDsSemanticTokenProvider extends AbstractSemanticTokenProvider { type: SemanticTokenTypes.namespace, }); } else if (isSdsImportedDeclaration(node)) { - const info = this.computeSemanticTokenInfoForDeclaration(node.declaration.ref); + const info = this.computeSemanticTokenInfoForDeclaration(node.declaration?.ref); if (info) { acceptor({ node, @@ -96,7 +96,7 @@ export class SafeDsSemanticTokenProvider extends AbstractSemanticTokenProvider { }); } } else if (isSdsNamedType(node)) { - const info = this.computeSemanticTokenInfoForDeclaration(node.declaration.ref); + const info = this.computeSemanticTokenInfoForDeclaration(node.declaration?.ref); if (info) { acceptor({ node, diff --git a/src/language/scoping/safe-ds-scope-provider.ts b/src/language/scoping/safe-ds-scope-provider.ts index c4a698944..3fed19dfd 100644 --- a/src/language/scoping/safe-ds-scope-provider.ts +++ b/src/language/scoping/safe-ds-scope-provider.ts @@ -52,16 +52,16 @@ import { isContainedIn } from '../helpers/astUtils.js'; import { getAbstractResults, getAssignees, - getMatchingClassMembers, getEnumVariants, getImportedDeclarations, getImports, - isStatic, + getMatchingClassMembers, getPackageName, getParameters, getResults, getStatements, getTypeParameters, + isStatic, } from '../helpers/nodeProperties.js'; import { SafeDsServices } from '../safe-ds-module.js'; import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js'; @@ -165,7 +165,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { */ private getUniqueReferencedDeclarationForType(node: SdsType): SdsNamedTypeDeclaration | undefined { if (isSdsNamedType(node)) { - return node.declaration.ref; + return node.declaration?.ref; } else if (isSdsMemberType(node)) { return node.member?.declaration?.ref; } else { @@ -335,7 +335,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { return EMPTY_SCOPE; } - const namedTypeDeclaration = containingNamedType.declaration.ref; + const namedTypeDeclaration = containingNamedType.declaration?.ref; if (isSdsClass(namedTypeDeclaration)) { const typeParameters = getTypeParameters(namedTypeDeclaration.typeParameterList); return this.createScopeForNodes(typeParameters); @@ -403,7 +403,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { for (const imp of imports) { if (isSdsQualifiedImport(imp)) { for (const importedDeclaration of getImportedDeclarations(imp)) { - const description = importedDeclaration.declaration.$nodeDescription; + const description = importedDeclaration.declaration?.$nodeDescription; if (!description || !this.astReflection.isSubtype(description.type, referenceType)) { continue; } diff --git a/src/language/typing/safe-ds-type-checker.ts b/src/language/typing/safe-ds-type-checker.ts index 6b859e6d4..287f60aca 100644 --- a/src/language/typing/safe-ds-type-checker.ts +++ b/src/language/typing/safe-ds-type-checker.ts @@ -198,6 +198,7 @@ export class SafeDsTypeChecker { } private unionTypeIsAssignableTo(type: UnionType, other: Type): boolean { + // return this.possibleTypes.all { it.isSubstitutableFor(other) } return type.equals(other); } } diff --git a/src/language/typing/safe-ds-type-computer.ts b/src/language/typing/safe-ds-type-computer.ts index 840c04ef2..5ea333c3c 100644 --- a/src/language/typing/safe-ds-type-computer.ts +++ b/src/language/typing/safe-ds-type-computer.ts @@ -75,11 +75,11 @@ import { import { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js'; import { getAssignees, - streamBlockLambdaResults, getLiterals, getParameters, getResults, getTypeArguments, + streamBlockLambdaResults, } from '../helpers/nodeProperties.js'; import { SafeDsPartialEvaluator } from '../partialEvaluation/safe-ds-partial-evaluator.js'; import { Constant, isConstant } from '../partialEvaluation/model.js'; @@ -453,7 +453,7 @@ export class SafeDsTypeComputer { } else if (isSdsMemberType(node)) { return this.computeType(node.member); } else if (isSdsNamedType(node)) { - return this.computeType(node.declaration.ref).copyWithNullability(node.isNullable); + return this.computeType(node.declaration?.ref).copyWithNullability(node.isNullable); } else if (isSdsUnionType(node)) { const typeArguments = getTypeArguments(node.typeArgumentList); return new UnionType(typeArguments.map((typeArgument) => this.computeType(typeArgument.value))); diff --git a/src/language/typing/typeConformance.ts b/src/language/typing/typeConformance.ts deleted file mode 100644 index 35f42f6dc..000000000 --- a/src/language/typing/typeConformance.ts +++ /dev/null @@ -1,141 +0,0 @@ -// package com.larsreimann.safeds.staticAnalysis.typing -// -// import com.larsreimann.safeds.constant.kind -// import com.larsreimann.safeds.emf.variantsOrEmpty -// import com.larsreimann.safeds.naming.qualifiedNameOrNull -// import com.larsreimann.safeds.staticAnalysis.classHierarchy.isSubtypeOf -// import com.larsreimann.safeds.stdlibAccess.StdlibClasses -// import com.larsreimann.safeds.utils.ExperimentalSdsApi -// -// fun Type.isSubstitutableFor(other: Type, resultIfUnresolved: Boolean = false): Boolean { -// if (other == UnresolvedType) { -// return resultIfUnresolved -// } -// -// return when (this) { -// is CallableType -> this.isSubstitutableFor(other) -// is ClassType -> this.isSubstitutableFor(other) -// is EnumType -> this.isSubstitutableFor(other) -// is EnumVariantType -> this.isSubstitutableFor(other) -// is ParameterisedType -> this.isSubstitutableFor(other) -// is UnionType -> this.isSubstitutableFor(other) -// is VariadicType -> this.isSubstitutableFor(other) -// is RecordType -> false -// UnresolvedType -> resultIfUnresolved -// } -// } -// -// private fun CallableType.isSubstitutableFor(other: Type): Boolean { -// return when (val unwrappedOther = unwrapVariadicType(other)) { -// is CallableType -> { -// // TODO: We need to compare names of parameters & results and can allow additional optional parameters -// -// // Sizes must match (too strict requirement -> should be loosened later) -// if (this.parameters.size != unwrappedOther.parameters.size || this.results.size != this.results.size) { -// return false -// } -// -// // Actual parameters must be supertypes of expected parameters (contravariance) -// this.parameters.zip(unwrappedOther.parameters).forEach { (thisParameter, otherParameter) -> -// if (!otherParameter.isSubstitutableFor(thisParameter)) { -// return false -// } -// } -// -// // Expected results must be subtypes of expected results (covariance) -// this.results.zip(unwrappedOther.results).forEach { (thisResult, otherResult) -> -// if (!thisResult.isSubstitutableFor(otherResult)) { -// return false -// } -// } -// -// true -// } -// is ClassType -> { -// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any -// } -// is UnionType -> { -// unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } -// } -// else -> false -// } -// } -// -// private fun ClassType.isSubstitutableFor(other: Type): Boolean { -// return when (val unwrappedOther = unwrapVariadicType(other)) { -// is ClassType -> { -// (!this.isNullable || unwrappedOther.isNullable) && this.sdsClass.isSubtypeOf(unwrappedOther.sdsClass) -// } -// is UnionType -> { -// unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } -// } -// else -> false -// } -// } -// -// private fun EnumType.isSubstitutableFor(other: Type): Boolean { -// return when (val unwrappedOther = unwrapVariadicType(other)) { -// is ClassType -> { -// (!this.isNullable || unwrappedOther.isNullable) && -// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any -// } -// is EnumType -> { -// (!this.isNullable || unwrappedOther.isNullable) && this.sdsEnum == unwrappedOther.sdsEnum -// } -// is UnionType -> { -// unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } -// } -// else -> false -// } -// } -// -// private fun EnumVariantType.isSubstitutableFor(other: Type): Boolean { -// return when (val unwrappedOther = unwrapVariadicType(other)) { -// is ClassType -> { -// (!this.isNullable || unwrappedOther.isNullable) && -// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any -// } -// is EnumType -> { -// (!this.isNullable || unwrappedOther.isNullable) && -// this.sdsEnumVariant in unwrappedOther.sdsEnum.variantsOrEmpty() -// } -// is EnumVariantType -> { -// (!this.isNullable || unwrappedOther.isNullable) && this.sdsEnumVariant == unwrappedOther.sdsEnumVariant -// } -// is UnionType -> unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } -// else -> false -// } -// } -// -// @OptIn(ExperimentalSdsApi::class) -// private fun ParameterisedType.isSubstitutableFor(other: Type): Boolean { -// return when (other) { -// is ParameterisedType -> (!this.isNullable || other.isNullable) && this.kind() == other.kind() -// else -> false -// } -// } -// -// /** -// * A [UnionType] can be substituted for another type of all its possible types can be substituted for the other type. -// */ -// private fun UnionType.isSubstitutableFor(other: Type): Boolean { -// return this.possibleTypes.all { it.isSubstitutableFor(other) } -// } -// -// private fun VariadicType.isSubstitutableFor(other: Type): Boolean { -// return when (other) { -// is ClassType -> other.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any -// is VariadicType -> this.elementType.isSubstitutableFor(other) -// else -> false -// } -// } -// -// /** -// * Returns the elementType of a [VariadicType] or, otherwise, the type itself. -// */ -// private fun unwrapVariadicType(type: Type): Type { -// return when (type) { -// is VariadicType -> type.elementType -// else -> type -// } -// } diff --git a/src/language/validation/builtins/deprecated.ts b/src/language/validation/builtins/deprecated.ts index 7527a49ad..7c0cb97db 100644 --- a/src/language/validation/builtins/deprecated.ts +++ b/src/language/validation/builtins/deprecated.ts @@ -76,7 +76,7 @@ export const argumentCorrespondingParameterShouldNotBeDeprecated = export const namedTypeDeclarationShouldNotBeDeprecated = (services: SafeDsServices) => (node: SdsNamedType, accept: ValidationAcceptor) => { - const declaration = node.declaration.ref; + const declaration = node.declaration?.ref; if (!declaration) { return; } diff --git a/src/language/validation/builtins/experimental.ts b/src/language/validation/builtins/experimental.ts index 39633c20b..73fa5524e 100644 --- a/src/language/validation/builtins/experimental.ts +++ b/src/language/validation/builtins/experimental.ts @@ -68,7 +68,7 @@ export const argumentCorrespondingParameterShouldNotBeExperimental = export const namedTypeDeclarationShouldNotBeExperimental = (services: SafeDsServices) => (node: SdsNamedType, accept: ValidationAcceptor) => { - const declaration = node.declaration.ref; + const declaration = node.declaration?.ref; if (!declaration) { return; } diff --git a/src/language/validation/experimentalLanguageFeatures.ts b/src/language/validation/experimentalLanguageFeatures.ts index 21daa0681..94ecfe389 100644 --- a/src/language/validation/experimentalLanguageFeatures.ts +++ b/src/language/validation/experimentalLanguageFeatures.ts @@ -25,6 +25,7 @@ export const indexedAccessesShouldBeUsedWithCaution = (node: SdsIndexedAccess, a export const literalTypesShouldBeUsedWithCaution = (node: SdsLiteralType, accept: ValidationAcceptor): void => { accept('warning', 'Literal types are experimental and may change without prior notice.', { node, + keyword: 'literal', code: CODE_EXPERIMENTAL_LANGUAGE_FEATURE, }); }; @@ -47,6 +48,7 @@ export const unionTypesShouldBeUsedWithCaution = (node: SdsUnionType, accept: Va accept('warning', 'Union types are experimental and may change without prior notice.', { node, + keyword: 'union', code: CODE_EXPERIMENTAL_LANGUAGE_FEATURE, }); }; diff --git a/src/language/validation/names.ts b/src/language/validation/names.ts index a263d193e..d6873ee45 100644 --- a/src/language/validation/names.ts +++ b/src/language/validation/names.ts @@ -305,7 +305,7 @@ export const moduleMustContainUniqueNames = (node: SdsModule, accept: Validation }; const importedDeclarationName = (node: SdsImportedDeclaration | undefined): string | undefined => { - return node?.alias?.alias ?? node?.declaration.ref?.name; + return node?.alias?.alias ?? node?.declaration?.ref?.name; }; export const pipelineMustContainUniqueNames = (node: SdsPipeline, accept: ValidationAcceptor): void => { diff --git a/src/language/validation/other/declarations/typeParameterConstraints.ts b/src/language/validation/other/declarations/typeParameterConstraints.ts index a6a837f1c..cb96c3732 100644 --- a/src/language/validation/other/declarations/typeParameterConstraints.ts +++ b/src/language/validation/other/declarations/typeParameterConstraints.ts @@ -7,7 +7,7 @@ export const typeParameterConstraintLeftOperandMustBeOwnTypeParameter = ( node: SdsTypeParameterConstraint, accept: ValidationAcceptor, ) => { - const typeParameter = node.leftOperand.ref; + const typeParameter = node.leftOperand?.ref; if (!typeParameter) { return; } diff --git a/src/language/validation/other/declarations/typeParameters.ts b/src/language/validation/other/declarations/typeParameters.ts new file mode 100644 index 000000000..26a738147 --- /dev/null +++ b/src/language/validation/other/declarations/typeParameters.ts @@ -0,0 +1,47 @@ +import { + isSdsCallable, + isSdsClass, + isSdsParameterList, + isSdsUnionType, + SdsTypeParameter, +} from '../../../generated/ast.js'; +import { findLocalReferences, getContainerOfType, hasContainerOfType, ValidationAcceptor } from 'langium'; + +export const CODE_TYPE_PARAMETER_INSUFFICIENT_CONTEXT = 'type-parameter/insufficient-context'; + +export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, accept: ValidationAcceptor) => { + const containingCallable = getContainerOfType(node, isSdsCallable); + /* c8 ignore start */ + if (!containingCallable) { + return; + } + /* c8 ignore stop */ + + // Classes without constructor can only be used as named types, where type arguments are manifest + if (isSdsClass(containingCallable) && !containingCallable.parameterList) { + return; + } + + // A type parameter must be referenced in the parameter list of the containing callable... + let typeParameterHasInsufficientContext = + !containingCallable.parameterList || + findLocalReferences(node, containingCallable.parameterList) + // ...but references in a union type or in the parameter list of a callable type don't count + .filter((reference) => { + const referenceNode = reference.$refNode?.astNode; + const containingParameterList = getContainerOfType(referenceNode, isSdsParameterList); + + return ( + !hasContainerOfType(referenceNode, isSdsUnionType) && + containingParameterList === containingCallable.parameterList + ); + }) + .isEmpty(); + + if (typeParameterHasInsufficientContext) { + accept('error', 'Insufficient context to infer this type parameter.', { + node, + code: CODE_TYPE_PARAMETER_INSUFFICIENT_CONTEXT, + }); + } +}; diff --git a/src/language/validation/other/types/namedTypes.ts b/src/language/validation/other/types/namedTypes.ts index 47da7589c..0e9d5c6f0 100644 --- a/src/language/validation/other/types/namedTypes.ts +++ b/src/language/validation/other/types/namedTypes.ts @@ -51,11 +51,12 @@ export const namedTypeTypeArgumentListMustNotHavePositionalArgumentsAfterNamedAr export const namedTypeMustNotHaveTooManyTypeArguments = (node: SdsNamedType, accept: ValidationAcceptor): void => { // If the declaration is unresolved, we already show another error - if (!node.declaration.ref) { + const namedTypeDeclaration = node.declaration?.ref; + if (!namedTypeDeclaration) { return; } - const typeParameters = getTypeParameters(node.declaration.ref); + const typeParameters = getTypeParameters(namedTypeDeclaration); const typeArguments = getTypeArguments(node.typeArgumentList); if (typeArguments.length > typeParameters.length) { diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 91bb31926..3d3646808 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -140,6 +140,7 @@ import { } from './other/types/literalTypes.js'; import { annotationCallMustHaveCorrectTarget, targetShouldNotHaveDuplicateEntries } from './builtins/target.js'; import { pythonCallMustOnlyContainValidTemplateExpressions } from './builtins/pythonCall.js'; +import { typeParameterMustHaveSufficientContext } from './other/declarations/typeParameters.js'; /** * Register custom validation checks. @@ -283,6 +284,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { segmentResultListShouldNotBeEmpty, ], SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts], + SdsTypeParameter: [typeParameterMustHaveSufficientContext], SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter], SdsTypeParameterList: [typeParameterListShouldNotBeEmpty], SdsUnionType: [ diff --git a/src/language/validation/style.ts b/src/language/validation/style.ts index 58d674206..239944844 100644 --- a/src/language/validation/style.ts +++ b/src/language/validation/style.ts @@ -19,7 +19,7 @@ import { SdsUnionType, } from '../generated/ast.js'; import { ValidationAcceptor } from 'langium'; -import { isRequiredParameter, getParameters, getTypeParameters } from '../helpers/nodeProperties.js'; +import { getParameters, getTypeParameters, isRequiredParameter } from '../helpers/nodeProperties.js'; import { SafeDsServices } from '../safe-ds-module.js'; import { UnknownType } from '../typing/model.js'; import { NullConstant } from '../partialEvaluation/model.js'; @@ -218,7 +218,7 @@ export const importedDeclarationAliasShouldDifferFromDeclarationName = ( node: SdsImportedDeclaration, accept: ValidationAcceptor, ) => { - if (node.alias && node.alias.alias === node.declaration.$refText) { + if (node.alias && node.alias.alias === node.declaration?.$refText) { accept('info', 'This alias can be removed.', { node, property: 'alias', diff --git a/src/language/validation/types.ts b/src/language/validation/types.ts index a6e1df531..974359d1a 100644 --- a/src/language/validation/types.ts +++ b/src/language/validation/types.ts @@ -67,7 +67,7 @@ export const callReceiverMustBeCallable = (services: SafeDsServices) => { export const namedTypeMustSetAllTypeParameters = (services: SafeDsServices) => (node: SdsNamedType, accept: ValidationAcceptor): void => { - const expectedTypeParameters = getTypeParameters(node.declaration.ref); + const expectedTypeParameters = getTypeParameters(node.declaration?.ref); if (isEmpty(expectedTypeParameters)) { return; } @@ -91,7 +91,7 @@ export const namedTypeMustSetAllTypeParameters = } else { accept( 'error', - `The type '${node.declaration.$refText}' is parameterized, so a type argument list must be added.`, + `The type '${node.declaration?.$refText}' is parameterized, so a type argument list must be added.`, { node, code: CODE_TYPE_MISSING_TYPE_ARGUMENTS, diff --git a/tests/resources/validation/experimental language feature/literal types/main.sdstest b/tests/resources/validation/experimental language feature/literal types/main.sdstest index 56b12ff46..369e22d58 100644 --- a/tests/resources/validation/experimental language feature/literal types/main.sdstest +++ b/tests/resources/validation/experimental language feature/literal types/main.sdstest @@ -2,5 +2,5 @@ package tests.validation.experimentalLanguageFeature.literalTypes fun myFunction( // $TEST$ warning "Literal types are experimental and may change without prior notice." - p: »literal<>« + p: »literal«<> ) diff --git a/tests/resources/validation/experimental language feature/union types/main.sdstest b/tests/resources/validation/experimental language feature/union types/main.sdstest index fec09d29d..2db26e877 100644 --- a/tests/resources/validation/experimental language feature/union types/main.sdstest +++ b/tests/resources/validation/experimental language feature/union types/main.sdstest @@ -2,12 +2,12 @@ package tests.validation.experimentalLanguageFeature.unionTypes fun myFunction( // $TEST$ warning "Union types are experimental and may change without prior notice." - p: »union«, + p: »union«, // $TEST$ no warning "Union types are experimental and may change without prior notice." - q: union<»union«, Int>, + q: union<»union«, Int>, // $TEST$ no warning "Union types are experimental and may change without prior notice." // $TEST$ no warning "Union types are experimental and may change without prior notice." - r: union<(p: »union«) -> (r: »union«), Int>, + r: union<(p: »union«) -> (r: »union«), Int>, ) diff --git a/tests/resources/validation/other/declarations/type parameters/insufficient context/main.sdstest b/tests/resources/validation/other/declarations/type parameters/insufficient context/main.sdstest new file mode 100644 index 000000000..ab3e2fd72 --- /dev/null +++ b/tests/resources/validation/other/declarations/type parameters/insufficient context/main.sdstest @@ -0,0 +1,66 @@ +package tests.validation.other.declarations.typeParameters.insufficientContext + +// $TEST$ no error "Insufficient context to infer this type parameter." +class MyClass1<»T«> +// $TEST$ error "Insufficient context to infer this type parameter." +class MyClass2<»T«>() +// $TEST$ error "Insufficient context to infer this type parameter." +class MyClass3<»T«>() { + attr myAttr: T +} +// $TEST$ no error "Insufficient context to infer this type parameter." +class MyClass4<»T«>(param: T) +// $TEST$ no error "Insufficient context to infer this type parameter." +class MyClass5<»T«>(param: MyClass4?) +// $TEST$ no error "Insufficient context to infer this type parameter." +class MyClass6<»T«>(param: MyEnum.MyEnumVariant1) +// $TEST$ no error "Insufficient context to infer this type parameter." +class MyClass7<»T«>(param: () -> (r: T)) +// $TEST$ error "Insufficient context to infer this type parameter." +class MyClass8<»T«>(param: (p: T) -> ()) +// $TEST$ error "Insufficient context to infer this type parameter." +class MyClass9<»T«>(param: () -> (r: (p: T) -> ())) +// $TEST$ error "Insufficient context to infer this type parameter." +class MyClass10<»T«>(param: union) +// $TEST$ error "Insufficient context to infer this type parameter." +class MyClass11<»T«>(param: union) + +enum MyEnum { + // $TEST$ error "Insufficient context to infer this type parameter." + MyEnumVariant1<»T«> + // $TEST$ no error "Insufficient context to infer this type parameter." + MyEnumVariant2<»T«>(param: T) + // $TEST$ no error "Insufficient context to infer this type parameter." + MyEnumVariant3<»T«>(param: MyClass4?) + // $TEST$ no error "Insufficient context to infer this type parameter." + MyEnumVariant4<»T«>(param: MyEnum.MyEnumVariant1) + // $TEST$ no error "Insufficient context to infer this type parameter." + MyEnumVariant5<»T«>(param: () -> (r: T)) + // $TEST$ error "Insufficient context to infer this type parameter." + MyEnumVariant6<»T«>(param: (p: T) -> ()) + // $TEST$ error "Insufficient context to infer this type parameter." + MyEnumVariant7<»T«>(param: () -> (r: (p: T) -> ())) + // $TEST$ error "Insufficient context to infer this type parameter." + MyEnumVariant8<»T«>(param: union) + // $TEST$ error "Insufficient context to infer this type parameter." + MyEnumVariant9<»T«>(param: union) +} + +// $TEST$ error "Insufficient context to infer this type parameter." +fun myFunction1<»T«>() +// $TEST$ no error "Insufficient context to infer this type parameter." +fun myFunction2<»T«>(param: T) +// $TEST$ no error "Insufficient context to infer this type parameter." +fun myFunction3<»T«>(param: MyClass4?) +// $TEST$ no error "Insufficient context to infer this type parameter." +fun myFunction4<»T«>(param: MyEnum.MyEnumVariant1) +// $TEST$ no error "Insufficient context to infer this type parameter." +fun myFunction5<»T«>(param: () -> (r: T)) +// $TEST$ error "Insufficient context to infer this type parameter." +fun myFunction6<»T«>(param: (p: T) -> ()) +// $TEST$ error "Insufficient context to infer this type parameter." +fun myFunction7<»T«>(param: () -> (r: (p: T) -> ())) +// $TEST$ error "Insufficient context to infer this type parameter." +fun myFunction8<»T«>(param: union) +// $TEST$ error "Insufficient context to infer this type parameter." +fun myFunction9<»T«>(param: union)