Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow usage of covariant type parameters in own constructor #854

Merged
merged 2 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions packages/safe-ds-lang/src/language/helpers/nodeProperties.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AstNode, getContainerOfType, Stream, stream } from 'langium';
import {
isSdsAnnotation,
isSdsArgument,
isSdsArgumentList,
isSdsAssignment,
isSdsAttribute,
Expand Down Expand Up @@ -78,29 +79,29 @@ export const hasAnnotationCallOf = (
});
};

export const isInternal = (node: SdsDeclaration): boolean => {
export const isInternal = (node: SdsDeclaration | undefined): boolean => {
return isSdsSegment(node) && node.visibility === 'internal';
};

export namespace Argument {
export const isNamed = (node: SdsArgument): boolean => {
return Boolean(node.parameter);
export const isNamed = (node: SdsArgument | undefined): boolean => {
return Boolean(node?.parameter);
};

export const isPositional = (node: SdsArgument): boolean => {
return !node.parameter;
export const isPositional = (node: SdsArgument | undefined): boolean => {
return isSdsArgument(node) && !node.parameter;
};
}

export namespace Enum {
export const isConstant = (node: SdsEnum): boolean => {
return getEnumVariants(node).every((it) => EnumVariant.isConstant(it));
export const isConstant = (node: SdsEnum | undefined): boolean => {
return Boolean(node) && getEnumVariants(node).every((it) => EnumVariant.isConstant(it));
};
}

export namespace EnumVariant {
export const isConstant = (node: SdsEnumVariant): boolean => {
return getParameters(node).every((it) => Parameter.isConstant(it));
export const isConstant = (node: SdsEnumVariant | undefined): boolean => {
return Boolean(node) && getParameters(node).every((it) => Parameter.isConstant(it));
};
}

Expand Down Expand Up @@ -129,7 +130,7 @@ export namespace Parameter {
};
}

export const isStatic = (node: SdsClassMember): boolean => {
export const isStatic = (node: SdsClassMember | undefined): boolean => {
if (isSdsClass(node) || isSdsEnum(node)) {
return true;
} else if (isSdsAttribute(node)) {
Expand All @@ -143,8 +144,8 @@ export const isStatic = (node: SdsClassMember): boolean => {
};

export namespace TypeArgument {
export const isNamed = (node: SdsTypeArgument): boolean => {
return Boolean(node.typeParameter);
export const isNamed = (node: SdsTypeArgument | undefined): boolean => {
return Boolean(node?.typeParameter);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,17 @@ export const typeParameterMustBeUsedInCorrectPosition = (services: SafeDsService
node: reference,
code: CODE_TYPE_PARAMETER_USAGE,
});
return; // Don't show other errors for this reference
}

// Usages in the **own constructor** are always correct. This check must come after the previous one, since
// that one filters out usages in **constructors of nested classes**.
if (isInConstructor(reference)) {
return;
}

// Check usage of variant type parameters
else if (TypeParameter.isContravariant(node)) {
if (TypeParameter.isContravariant(node)) {
const position = getTypePosition(nodeMapper, declarationWithTypeParameter, reference);

if (position !== 'contravariant') {
Expand All @@ -113,6 +120,11 @@ export const typeParameterMustBeUsedInCorrectPosition = (services: SafeDsService
};
};

const isInConstructor = (node: AstNode) => {
const parameterList = getContainerOfType(node, isSdsParameterList);
return isSdsClass(parameterList?.$container);
};

const classTypeParameterIsUsedInCorrectPosition = (classWithTypeParameter: SdsClass, reference: AstNode) => {
const containingClassMember = getContainerOfType(reference, isSdsClassMember);

Expand All @@ -121,7 +133,7 @@ const classTypeParameterIsUsedInCorrectPosition = (classWithTypeParameter: SdsCl
return true;
}

// Handle usage in static context
// Handle usage in static member
if (isStatic(containingClassMember)) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package tests.validation.other.declarations.typeParameters.usageOfVariantTypeParameters

// $TEST$ error "A covariant type parameter cannot be used in contravariant position."
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
class MyClass2<out Covariant>(p1: »Covariant«) {
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
attr a1: »Covariant«
Expand Down
Loading