Skip to content

Commit

Permalink
feat: error if type parameter is used in nested named type declaration (
Browse files Browse the repository at this point in the history
#750)

Closes #748

### Summary of Changes

Show an error if a type parameter of a class is used inside a nested
named type declaration.
  • Loading branch information
lars-reimann authored Nov 10, 2023
1 parent 6510555 commit 52374aa
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 5 deletions.
2 changes: 1 addition & 1 deletion docs/language/common/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ A declaration with an _enum type_ must be one of the [variants][variants] of the

```sds
enum SomeEnum {
SomeEnumVariant,
SomeEnumVariant
SomeOtherEnumVariant(count: Int)
}
```
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { findLocalReferences, getContainerOfType, hasContainerOfType, ValidationAcceptor } from 'langium';
import {
isSdsCallable,
isSdsClass,
isSdsDeclaration,
isSdsNamedTypeDeclaration,
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 CODE_TYPE_PARAMETER_USAGE = 'type-parameter/usage';

export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, accept: ValidationAcceptor) => {
const containingCallable = getContainerOfType(node, isSdsCallable);
Expand Down Expand Up @@ -45,3 +48,29 @@ export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, a
});
}
};

export const typeParameterMustNotBeUsedInNestedNamedTypeDeclarations = (
node: SdsTypeParameter,
accept: ValidationAcceptor,
) => {
// Only classes can have nested named type declarations
const declarationWithTypeParameter = getContainerOfType(node.$container, isSdsDeclaration);
if (!isSdsClass(declarationWithTypeParameter)) {
return;
}

findLocalReferences(node).forEach((it) => {
const reference = it.$refNode?.astNode;
const containingNamedTypeDeclaration = getContainerOfType(reference, isSdsNamedTypeDeclaration);
if (
reference &&
containingNamedTypeDeclaration &&
containingNamedTypeDeclaration !== declarationWithTypeParameter
) {
accept('error', 'Type parameters cannot be used in nested named type declarations.', {
node: reference,
code: CODE_TYPE_PARAMETER_USAGE,
});
}
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ import {
segmentShouldBeUsed,
} from './other/declarations/segments.js';
import { typeParameterConstraintLeftOperandMustBeOwnTypeParameter } from './other/declarations/typeParameterConstraints.js';
import { typeParameterMustHaveSufficientContext } from './other/declarations/typeParameters.js';
import {
typeParameterMustHaveSufficientContext,
typeParameterMustNotBeUsedInNestedNamedTypeDeclarations,
} from './other/declarations/typeParameters.js';
import { callArgumentsMustBeConstantIfParameterIsConstant } from './other/expressions/calls.js';
import { divisionDivisorMustNotBeZero } from './other/expressions/infixOperations.js';
import {
Expand Down Expand Up @@ -309,7 +312,10 @@ export const registerValidationChecks = function (services: SafeDsServices) {
segmentShouldBeUsed(services),
],
SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts],
SdsTypeParameter: [typeParameterMustHaveSufficientContext],
SdsTypeParameter: [
typeParameterMustHaveSufficientContext,
typeParameterMustNotBeUsedInNestedNamedTypeDeclarations,
],
SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter],
SdsTypeParameterList: [typeParameterListShouldNotBeEmpty],
SdsUnionType: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tests.validation.other.declarations.typeParameterConstraints.typeParameterOnContainer
package tests.validation.other.declarations.constraints.typeParameterConstraints.typeParameterOnContainer

annotation MyAnnotation where {
// $TEST$ no error "The left operand must refer to a type parameter of the declaration with the constraint."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package tests.validation.other.declarations.typeParameters.usage

// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
class MyClass<T>(p: »T«) {
// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
attr a: »T«

// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
fun f<S>(p1: »T«, p2: »S«) -> (r1: »T«, r2: »S«)

// $TEST$ error "Type parameters cannot be used in nested named type declarations."
// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
class MyInnerClass<S>(p1: »T«, p2: »S«) {
// $TEST$ error "Type parameters cannot be used in nested named type declarations."
attr a: »T«

// $TEST$ error "Type parameters cannot be used in nested named type declarations."
fun f(p: »T«)
}

enum MyInnerEnum {
// $TEST$ error "Type parameters cannot be used in nested named type declarations."
// $TEST$ no error "Type parameters cannot be used in nested named type declarations."
MyEnumVariant<S>(p1: »T«, p2: »S«)
}
}

0 comments on commit 52374aa

Please sign in to comment.