From 45a3f6b0c423de4ffcf530a7220b2f60b2d158ea Mon Sep 17 00:00:00 2001
From: Lars Reimann <mail@larsreimann.com>
Date: Thu, 21 Sep 2023 11:48:01 +0200
Subject: [PATCH 1/4] refactor: attach checks for emptiness directly to bodies
 of classes and enums

---
 src/language/validation/safe-ds-validator.ts |  5 +++--
 src/language/validation/style.ts             | 13 ++++++-------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts
index e4afd988b..8a38fc9bd 100644
--- a/src/language/validation/safe-ds-validator.ts
+++ b/src/language/validation/safe-ds-validator.ts
@@ -30,9 +30,10 @@ export const registerValidationChecks = function (services: SafeDsServices) {
         SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees],
         SdsAnnotation: [annotationParameterListShouldNotBeEmpty],
         SdsAttribute: [attributeMustHaveTypeHint],
-        SdsClass: [classBodyShouldNotBeEmpty, classTypeParameterListShouldNotBeEmpty],
+        SdsClass: [classTypeParameterListShouldNotBeEmpty],
+        SdsClassBody: [classBodyShouldNotBeEmpty],
         SdsDeclaration: [nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing],
-        SdsEnum: [enumBodyShouldNotBeEmpty],
+        SdsEnumBody: [enumBodyShouldNotBeEmpty],
         SdsEnumVariant: [enumVariantParameterListShouldNotBeEmpty, enumVariantTypeParameterListShouldNotBeEmpty],
         SdsFunction: [functionResultListShouldNotBeEmpty, functionTypeParameterListShouldNotBeEmpty],
         SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage],
diff --git a/src/language/validation/style.ts b/src/language/validation/style.ts
index 442d0aa92..b034e88f9 100644
--- a/src/language/validation/style.ts
+++ b/src/language/validation/style.ts
@@ -3,7 +3,8 @@ import {
     SdsAnnotation,
     SdsAssignment,
     SdsClass,
-    SdsEnum,
+    SdsClassBody,
+    SdsEnumBody,
     SdsEnumVariant,
     SdsFunction,
     SdsSegment,
@@ -44,21 +45,19 @@ export const assignmentShouldHaveMoreThanWildcardsAsAssignees = (
 // Unnecessary bodies
 // -----------------------------------------------------------------------------
 
-export const classBodyShouldNotBeEmpty = (node: SdsClass, accept: ValidationAcceptor) => {
-    if (node.body && isEmpty(node.body.members)) {
+export const classBodyShouldNotBeEmpty = (node: SdsClassBody, accept: ValidationAcceptor) => {
+    if (isEmpty(node.members)) {
         accept('info', 'This body can be removed.', {
             node,
-            property: 'body',
             code: CODE_STYLE_UNNECESSARY_BODY,
         });
     }
 };
 
-export const enumBodyShouldNotBeEmpty = (node: SdsEnum, accept: ValidationAcceptor) => {
-    if (node.body && isEmpty(node.body.variants)) {
+export const enumBodyShouldNotBeEmpty = (node: SdsEnumBody, accept: ValidationAcceptor) => {
+    if (isEmpty(node.variants)) {
         accept('info', 'This body can be removed.', {
             node,
-            property: 'body',
             code: CODE_STYLE_UNNECESSARY_BODY,
         });
     }

From f26da6ee367bafc139391e0ea46ac2d83ebc4e65 Mon Sep 17 00:00:00 2001
From: Lars Reimann <mail@larsreimann.com>
Date: Thu, 21 Sep 2023 11:49:15 +0200
Subject: [PATCH 2/4] refactor: attach checks for emptiness directly to type
 parameter lists

---
 src/language/validation/safe-ds-validator.ts | 10 +++-----
 src/language/validation/style.ts             | 27 +++-----------------
 2 files changed, 7 insertions(+), 30 deletions(-)

diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts
index 8a38fc9bd..3c069e2e4 100644
--- a/src/language/validation/safe-ds-validator.ts
+++ b/src/language/validation/safe-ds-validator.ts
@@ -6,13 +6,11 @@ import {
     annotationParameterListShouldNotBeEmpty,
     assignmentShouldHaveMoreThanWildcardsAsAssignees,
     classBodyShouldNotBeEmpty,
-    classTypeParameterListShouldNotBeEmpty,
     enumBodyShouldNotBeEmpty,
     enumVariantParameterListShouldNotBeEmpty,
-    enumVariantTypeParameterListShouldNotBeEmpty,
     functionResultListShouldNotBeEmpty,
-    functionTypeParameterListShouldNotBeEmpty,
     segmentResultListShouldNotBeEmpty,
+    typeParameterListShouldNotBeEmpty,
     unionTypeShouldNotHaveASingularTypeArgument,
 } from './style.js';
 import { templateStringMustHaveExpressionBetweenTwoStringParts } from './other/expressions/templateStrings.js';
@@ -30,17 +28,17 @@ export const registerValidationChecks = function (services: SafeDsServices) {
         SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees],
         SdsAnnotation: [annotationParameterListShouldNotBeEmpty],
         SdsAttribute: [attributeMustHaveTypeHint],
-        SdsClass: [classTypeParameterListShouldNotBeEmpty],
         SdsClassBody: [classBodyShouldNotBeEmpty],
         SdsDeclaration: [nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing],
         SdsEnumBody: [enumBodyShouldNotBeEmpty],
-        SdsEnumVariant: [enumVariantParameterListShouldNotBeEmpty, enumVariantTypeParameterListShouldNotBeEmpty],
-        SdsFunction: [functionResultListShouldNotBeEmpty, functionTypeParameterListShouldNotBeEmpty],
+        SdsEnumVariant: [enumVariantParameterListShouldNotBeEmpty],
+        SdsFunction: [functionResultListShouldNotBeEmpty],
         SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage],
         SdsParameter: [parameterMustHaveTypeHint],
         SdsResult: [resultMustHaveTypeHint],
         SdsSegment: [segmentResultListShouldNotBeEmpty],
         SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts],
+        SdsTypeParameterList: [typeParameterListShouldNotBeEmpty],
         SdsUnionType: [unionTypeShouldNotHaveASingularTypeArgument],
         SdsYield: [yieldMustNotBeUsedInPipeline],
     };
diff --git a/src/language/validation/style.ts b/src/language/validation/style.ts
index b034e88f9..e2b0d293b 100644
--- a/src/language/validation/style.ts
+++ b/src/language/validation/style.ts
@@ -2,12 +2,12 @@ import {
     isSdsWildcard,
     SdsAnnotation,
     SdsAssignment,
-    SdsClass,
     SdsClassBody,
     SdsEnumBody,
     SdsEnumVariant,
     SdsFunction,
     SdsSegment,
+    SdsTypeParameterList,
     SdsUnionType,
 } from '../generated/ast.js';
 import { ValidationAcceptor } from 'langium';
@@ -115,31 +115,10 @@ export const segmentResultListShouldNotBeEmpty = (node: SdsSegment, accept: Vali
 // Unnecessary type parameter lists
 // -----------------------------------------------------------------------------
 
-export const classTypeParameterListShouldNotBeEmpty = (node: SdsClass, accept: ValidationAcceptor) => {
-    if (node.typeParameterList && isEmpty(node.typeParameterList.typeParameters)) {
+export const typeParameterListShouldNotBeEmpty = (node: SdsTypeParameterList, accept: ValidationAcceptor) => {
+    if (isEmpty(node.typeParameters)) {
         accept('info', 'This type parameter list can be removed.', {
             node,
-            property: 'typeParameterList',
-            code: CODE_STYLE_UNNECESSARY_TYPE_PARAMETER_LIST,
-        });
-    }
-};
-
-export const enumVariantTypeParameterListShouldNotBeEmpty = (node: SdsEnumVariant, accept: ValidationAcceptor) => {
-    if (node.typeParameterList && isEmpty(node.typeParameterList.typeParameters)) {
-        accept('info', 'This type parameter list can be removed.', {
-            node,
-            property: 'typeParameterList',
-            code: CODE_STYLE_UNNECESSARY_TYPE_PARAMETER_LIST,
-        });
-    }
-};
-
-export const functionTypeParameterListShouldNotBeEmpty = (node: SdsFunction, accept: ValidationAcceptor) => {
-    if (node.typeParameterList && isEmpty(node.typeParameterList.typeParameters)) {
-        accept('info', 'This type parameter list can be removed.', {
-            node,
-            property: 'typeParameterList',
             code: CODE_STYLE_UNNECESSARY_TYPE_PARAMETER_LIST,
         });
     }

From 9fef7016b78dcc5a599630a17b7e2f2bf8bdd631 Mon Sep 17 00:00:00 2001
From: Lars Reimann <mail@larsreimann.com>
Date: Thu, 21 Sep 2023 11:56:50 +0200
Subject: [PATCH 3/4] feat: show info that empty constraint lists can be
 removed

---
 src/language/validation/safe-ds-validator.ts  | 26 +++++++++++--------
 src/language/validation/style.ts              | 15 +++++++++++
 .../main.sdstest                              |  9 +++++++
 .../main.sdstest                              |  9 +++++++
 .../main.sdstest                              | 11 ++++++++
 .../main.sdstest                              |  9 +++++++
 6 files changed, 68 insertions(+), 11 deletions(-)
 create mode 100644 tests/resources/validation/style/unnecessary constraint list in annotation/main.sdstest
 create mode 100644 tests/resources/validation/style/unnecessary constraint list in class/main.sdstest
 create mode 100644 tests/resources/validation/style/unnecessary constraint list in enum variant/main.sdstest
 create mode 100644 tests/resources/validation/style/unnecessary constraint list in function/main.sdstest

diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts
index 34a672cb2..febd5f52f 100644
--- a/src/language/validation/safe-ds-validator.ts
+++ b/src/language/validation/safe-ds-validator.ts
@@ -1,11 +1,11 @@
-import { ValidationChecks } from 'langium';
-import { SafeDsAstType } from '../generated/ast.js';
-import type { SafeDsServices } from '../safe-ds-module.js';
-import { nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing } from './names.js';
+import {ValidationChecks} from 'langium';
+import {SafeDsAstType} from '../generated/ast.js';
+import type {SafeDsServices} from '../safe-ds-module.js';
+import {nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing} from './names.js';
 import {
     annotationParameterListShouldNotBeEmpty,
     assignmentShouldHaveMoreThanWildcardsAsAssignees,
-    classBodyShouldNotBeEmpty,
+    classBodyShouldNotBeEmpty, constraintListShouldNotBeEmpty,
     enumBodyShouldNotBeEmpty,
     enumVariantParameterListShouldNotBeEmpty,
     functionResultListShouldNotBeEmpty,
@@ -13,11 +13,13 @@ 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';
 
 /**
  * Register custom validation checks.
@@ -30,6 +32,7 @@ export const registerValidationChecks = function (services: SafeDsServices) {
         SdsAnnotation: [annotationParameterListShouldNotBeEmpty],
         SdsAttribute: [attributeMustHaveTypeHint],
         SdsClassBody: [classBodyShouldNotBeEmpty],
+        SdsConstraintList: [constraintListShouldNotBeEmpty],
         SdsDeclaration: [nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing],
         SdsEnumBody: [enumBodyShouldNotBeEmpty],
         SdsEnumVariant: [enumVariantParameterListShouldNotBeEmpty],
@@ -50,4 +53,5 @@ export const registerValidationChecks = function (services: SafeDsServices) {
 /**
  * Implementation of custom validations.
  */
-export class SafeDsValidator {}
+export class SafeDsValidator {
+}
diff --git a/src/language/validation/style.ts b/src/language/validation/style.ts
index e2b0d293b..2df07a734 100644
--- a/src/language/validation/style.ts
+++ b/src/language/validation/style.ts
@@ -3,6 +3,7 @@ import {
     SdsAnnotation,
     SdsAssignment,
     SdsClassBody,
+    SdsConstraintList,
     SdsEnumBody,
     SdsEnumVariant,
     SdsFunction,
@@ -16,6 +17,7 @@ import { isEmpty } from 'radash';
 export const CODE_STYLE_UNNECESSARY_ASSIGNMENT = 'style/unnecessary-assignment';
 export const CODE_STYLE_UNNECESSARY_ARGUMENT_LIST = 'style/unnecessary-argument-list';
 export const CODE_STYLE_UNNECESSARY_BODY = 'style/unnecessary-body';
+export const CODE_STYLE_UNNECESSARY_CONSTRAINT_LIST = 'style/unnecessary-constraint-list';
 export const CODE_STYLE_UNNECESSARY_ELVIS_OPERATOR = 'style/unnecessary-elvis-operator';
 export const CODE_STYLE_UNNECESSARY_SAFE_ACCESS = 'style/unnecessary-safe-access';
 export const CODE_STYLE_UNNECESSARY_PARAMETER_LIST = 'style/unnecessary-parameter-list';
@@ -63,6 +65,19 @@ export const enumBodyShouldNotBeEmpty = (node: SdsEnumBody, accept: ValidationAc
     }
 };
 
+// -----------------------------------------------------------------------------
+// Unnecessary constraint list
+// -----------------------------------------------------------------------------
+
+export const constraintListShouldNotBeEmpty = (node: SdsConstraintList, accept: ValidationAcceptor) => {
+    if (isEmpty(node.constraints)) {
+        accept('info', 'This constraint list can be removed.', {
+            node,
+            code: CODE_STYLE_UNNECESSARY_CONSTRAINT_LIST,
+        });
+    }
+};
+
 // -----------------------------------------------------------------------------
 // Unnecessary parameter lists
 // -----------------------------------------------------------------------------
diff --git a/tests/resources/validation/style/unnecessary constraint list in annotation/main.sdstest b/tests/resources/validation/style/unnecessary constraint list in annotation/main.sdstest
new file mode 100644
index 000000000..19fb92a9d
--- /dev/null
+++ b/tests/resources/validation/style/unnecessary constraint list in annotation/main.sdstest	
@@ -0,0 +1,9 @@
+package tests.validation.style.unnecessaryConstraintListInAnnotation
+
+// $TEST$ info "This constraint list can be removed."
+annotation MyAnnotation1 »where {}«
+
+// $TEST$ no info "This constraint list can be removed."
+annotation MyAnnotation2 »where {
+    T sub Int
+}«
diff --git a/tests/resources/validation/style/unnecessary constraint list in class/main.sdstest b/tests/resources/validation/style/unnecessary constraint list in class/main.sdstest
new file mode 100644
index 000000000..254eeb421
--- /dev/null
+++ b/tests/resources/validation/style/unnecessary constraint list in class/main.sdstest	
@@ -0,0 +1,9 @@
+package tests.validation.style.unnecessaryConstraintListInClass
+
+// $TEST$ info "This constraint list can be removed."
+class MyClass1 »where {}«
+
+// $TEST$ no info "This constraint list can be removed."
+class MyClass2<T> »where {
+    T sub Int
+}«
diff --git a/tests/resources/validation/style/unnecessary constraint list in enum variant/main.sdstest b/tests/resources/validation/style/unnecessary constraint list in enum variant/main.sdstest
new file mode 100644
index 000000000..4c058c694
--- /dev/null
+++ b/tests/resources/validation/style/unnecessary constraint list in enum variant/main.sdstest	
@@ -0,0 +1,11 @@
+package tests.validation.style.unnecessaryConstraintInEnumVariant
+
+enum MyEnum {
+    // $TEST$ info "This constraint list can be removed."
+    MyVariant1 »where {}«
+
+    // $TEST$ no info "This constraint list can be removed."
+    MyVariant2<T>() »where {
+        T sub Int
+    }«
+}
diff --git a/tests/resources/validation/style/unnecessary constraint list in function/main.sdstest b/tests/resources/validation/style/unnecessary constraint list in function/main.sdstest
new file mode 100644
index 000000000..fa82757b9
--- /dev/null
+++ b/tests/resources/validation/style/unnecessary constraint list in function/main.sdstest	
@@ -0,0 +1,9 @@
+package tests.validation.style.unnecessaryConstraintListInFunction
+
+// $TEST$ info "This constraint list can be removed."
+fun myFunction1() »where {}«
+
+// $TEST$ no info "This constraint list can be removed."
+fun myFunction2<T>() »where {
+    T sub Int
+}«

From aab559fed780c22d226de76198732a8085eb18a1 Mon Sep 17 00:00:00 2001
From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
Date: Thu, 21 Sep 2023 09:59:13 +0000
Subject: [PATCH 4/4] style: apply automated linter fixes

---
 src/language/validation/safe-ds-validator.ts | 26 +++++++++-----------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts
index febd5f52f..633cad68e 100644
--- a/src/language/validation/safe-ds-validator.ts
+++ b/src/language/validation/safe-ds-validator.ts
@@ -1,11 +1,12 @@
-import {ValidationChecks} from 'langium';
-import {SafeDsAstType} from '../generated/ast.js';
-import type {SafeDsServices} from '../safe-ds-module.js';
-import {nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing} from './names.js';
+import { ValidationChecks } from 'langium';
+import { SafeDsAstType } from '../generated/ast.js';
+import type { SafeDsServices } from '../safe-ds-module.js';
+import { nameMustNotStartWithBlockLambdaPrefix, nameShouldHaveCorrectCasing } from './names.js';
 import {
     annotationParameterListShouldNotBeEmpty,
     assignmentShouldHaveMoreThanWildcardsAsAssignees,
-    classBodyShouldNotBeEmpty, constraintListShouldNotBeEmpty,
+    classBodyShouldNotBeEmpty,
+    constraintListShouldNotBeEmpty,
     enumBodyShouldNotBeEmpty,
     enumVariantParameterListShouldNotBeEmpty,
     functionResultListShouldNotBeEmpty,
@@ -13,13 +14,11 @@ 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';
 
 /**
  * Register custom validation checks.
@@ -53,5 +52,4 @@ export const registerValidationChecks = function (services: SafeDsServices) {
 /**
  * Implementation of custom validations.
  */
-export class SafeDsValidator {
-}
+export class SafeDsValidator {}