Skip to content

Commit

Permalink
fix: allow usage of covariant type parameters in own constructor (#854)
Browse files Browse the repository at this point in the history
### Summary of Changes

Previously, an incorrect error was shown when a covariant type parameter
was used in the own constructor of the generic class. This PR removes
this error.
  • Loading branch information
lars-reimann authored Feb 4, 2024
1 parent 0ed9d6e commit 4ebae94
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 15 deletions.
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

0 comments on commit 4ebae94

Please sign in to comment.