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 +}