From 23b340ed3206791b0fabd7950f683c3d554bc2bd Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 11 Nov 2023 17:09:31 +0100 Subject: [PATCH] feat: info if overriding member is not needed (#759) Closes #25 ### Summary of Changes Show an info if an overriding member is identical to the overridden one. In this case, it can be removed. --- .../src/language/validation/inheritance.ts | 13 ++- .../language/validation/safe-ds-validator.ts | 4 +- .../main.sdstest | 80 +++++++------------ .../main.sdstest | 55 +++++++++++++ 4 files changed, 97 insertions(+), 55 deletions(-) create mode 100644 packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method should differ from overridden method/main.sdstest diff --git a/packages/safe-ds-lang/src/language/validation/inheritance.ts b/packages/safe-ds-lang/src/language/validation/inheritance.ts index a6523bc5e..5a0e924ec 100644 --- a/packages/safe-ds-lang/src/language/validation/inheritance.ts +++ b/packages/safe-ds-lang/src/language/validation/inheritance.ts @@ -7,10 +7,11 @@ import { ClassType, UnknownType } from '../typing/model.js'; export const CODE_INHERITANCE_CYCLE = 'inheritance/cycle'; export const CODE_INHERITANCE_MULTIPLE_INHERITANCE = 'inheritance/multiple-inheritance'; -export const CODE_INHERITANCE_MUST_MATCH_OVERRIDDEN_MEMBER = 'inheritance/must-match-overridden-member'; +export const CODE_INHERITANCE_IDENTICAL_TO_OVERRIDDEN_MEMBER = 'inheritance/identical-to-overridden-member'; +export const CODE_INHERITANCE_INCOMPATIBLE_TO_OVERRIDDEN_MEMBER = 'inheritance/incompatible-to-overridden-member'; export const CODE_INHERITANCE_NOT_A_CLASS = 'inheritance/not-a-class'; -export const classMemberMustMatchOverriddenMember = (services: SafeDsServices) => { +export const classMemberMustMatchOverriddenMemberAndShouldBeNeeded = (services: SafeDsServices) => { const classHierarchy = services.types.ClassHierarchy; const typeChecker = services.types.TypeChecker; const typeComputer = services.types.TypeComputer; @@ -35,9 +36,15 @@ export const classMemberMustMatchOverriddenMember = (services: SafeDsServices) = { node, property: 'name', - code: CODE_INHERITANCE_MUST_MATCH_OVERRIDDEN_MEMBER, + code: CODE_INHERITANCE_INCOMPATIBLE_TO_OVERRIDDEN_MEMBER, }, ); + } else if (typeChecker.isAssignableTo(overriddenMemberType, ownMemberType)) { + accept('info', 'Overriding member is identical to overridden member and can be removed.', { + node, + property: 'name', + code: CODE_INHERITANCE_IDENTICAL_TO_OVERRIDDEN_MEMBER, + }); } }; }; diff --git a/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts b/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts index ec2f87b50..a7be8a0b5 100644 --- a/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts +++ b/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts @@ -36,7 +36,7 @@ import { unionTypesShouldBeUsedWithCaution, } from './experimentalLanguageFeatures.js'; import { - classMemberMustMatchOverriddenMember, + classMemberMustMatchOverriddenMemberAndShouldBeNeeded, classMustNotInheritItself, classMustOnlyInheritASingleClass, } from './inheritance.js'; @@ -228,7 +228,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { classMustNotInheritItself(services), ], SdsClassBody: [classBodyShouldNotBeEmpty], - SdsClassMember: [classMemberMustMatchOverriddenMember(services)], + SdsClassMember: [classMemberMustMatchOverriddenMemberAndShouldBeNeeded(services)], SdsConstraintList: [constraintListsShouldBeUsedWithCaution, constraintListShouldNotBeEmpty], SdsDeclaration: [ nameMustNotStartWithCodegenPrefix, diff --git a/packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method must match overridden method/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method must match overridden method/main.sdstest index 29fe514fc..10cce01f5 100644 --- a/packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method must match overridden method/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method must match overridden method/main.sdstest @@ -1,75 +1,55 @@ package tests.validation.inheritance.overridingMethodMustMatchOverriddenMethod class MySuperClass { - attr myInstanceAttribute: Int - static attr myStaticAttribute: Int + attr myInstanceAttribute: Number + static attr myStaticAttribute: Number - fun myInstanceMethod(a: Int = 0) -> r: Int - static fun myStaticMethod(a: Int = 0) -> r: Int + fun myInstanceMethod(a: Number = 0) -> r: Number + static fun myStaticMethod(a: Number = 0) -> r: Number } class MyClass1 sub MySuperClass { - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" + // $TEST$ no error r"Overriding member does not match the overridden member:.*" attr »myInstanceAttribute«: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" + // $TEST$ no error r"Overriding member does not match the overridden member:.*" static attr »myStaticAttribute«: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - attr »myInstanceAttribute«: String - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static attr »myStaticAttribute«: String + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + attr »myInstanceAttribute«: Any + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + static attr »myStaticAttribute«: Any - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" + // $TEST$ no error r"Overriding member does not match the overridden member:.*" attr »myOwnInstanceAttribute«: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" + // $TEST$ no error r"Overriding member does not match the overridden member:.*" static attr »myOwnStaticAttribute«: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - fun »myInstanceMethod«(a: Int = 0) -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static fun »myStaticMethod«(a: Int = 0) -> r: Int + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + fun »myInstanceMethod«(a: Any = 0) -> r: Int + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + static fun »myStaticMethod«(a: Any = 0) -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" + // $TEST$ no error r"Overriding member does not match the overridden member:.*" fun »myInstanceMethod«() -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" + // $TEST$ no error r"Overriding member does not match the overridden member:.*" static fun »myStaticMethod«() -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - fun »myOwnInstanceMethod«(a: Int = 0) -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static fun »myOwnStaticMethod«(a: Int = 0) -> r: Int + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + fun »myOwnInstanceMethod«(a: Any = 0) -> r: Int + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + static fun »myOwnStaticMethod«(a: Any = 0) -> r: Int } class MyClass2 sub MySuperClass { - // $TEST$ error r"Overriding member does not match the overridden member:[\s\S]*" - attr »myInstanceAttribute«: String - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static attr »myStaticAttribute«: String + // $TEST$ error r"Overriding member does not match the overridden member:.*" + attr »myInstanceAttribute«: Any + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + static attr »myStaticAttribute«: Any - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - attr »myInstanceAttribute«: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static attr »myStaticAttribute«: Int - - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - attr »myOwnInstanceAttribute«: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static attr »myOwnStaticAttribute«: Int - - - // $TEST$ error r"Overriding member does not match the overridden member:[\s\S]*" - fun »myInstanceMethod«() -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static fun »myStaticMethod«() -> r: Int - - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - fun »myInstanceMethod«(a: Int = 0) -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static fun »myStaticMethod«(a: Int = 0) -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - fun »myOwnInstanceMethod«(a: Int = 0) -> r: Int - // $TEST$ no error r"Overriding member does not match the overridden member:[\s\S]*" - static fun »myOwnStaticMethod«(a: Int = 0) -> r: Int + // $TEST$ error r"Overriding member does not match the overridden member:.*" + fun »myInstanceMethod«(a: Number = 0) -> r: Any + // $TEST$ no error r"Overriding member does not match the overridden member:.*" + static fun »myStaticMethod«(a: Number = 0) -> r: Any } diff --git a/packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method should differ from overridden method/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method should differ from overridden method/main.sdstest new file mode 100644 index 000000000..48a92b7c1 --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/validation/inheritance/overriding method should differ from overridden method/main.sdstest @@ -0,0 +1,55 @@ +package tests.validation.inheritance.overridingMethodShouldDifferFromOverriddenMethod + +class MySuperClass { + attr myInstanceAttribute: Number + static attr myStaticAttribute: Number + + fun myInstanceMethod(a: Number = 0) -> r: Number + static fun myStaticMethod(a: Number = 0) -> r: Number +} + +class MyClass1 sub MySuperClass { + // $TEST$ info "Overriding member is identical to overridden member and can be removed." + attr »myInstanceAttribute«: Number + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static attr »myStaticAttribute«: Number + + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + attr »myInstanceAttribute«: Number + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static attr »myStaticAttribute«: Number + + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + attr »myOwnInstanceAttribute«: Number + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static attr »myOwnStaticAttribute«: Number + + + // $TEST$ info "Overriding member is identical to overridden member and can be removed." + fun »myInstanceMethod«(a: Number = 0) -> r: Number + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static fun »myStaticMethod«(a: Number = 0) -> r: Number + + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + fun »myInstanceMethod«() -> r: Number + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static fun »myStaticMethod«() -> r: Number + + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + fun »myOwnInstanceMethod«(a: Number = 0) -> r: Number + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static fun »myOwnStaticMethod«(a: Number = 0) -> r: Number +} + +class MyClass2 sub MySuperClass { + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + attr »myInstanceAttribute«: Int + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static attr »myStaticAttribute«: Int + + + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + fun »myInstanceMethod«(a: Number = 0) -> r: Int + // $TEST$ no info "Overriding member is identical to overridden member and can be removed." + static fun »myStaticMethod«(a: Number = 0) -> r: Int +}