已合并
Require override for multiple inherited explicit overloads #10640
zhelyapovaleksey创建于 5月7日
Require override for multiple inherited explicit overloads #10640
已合并
共 8 个文件变更+229-7
| @@ -653,6 +653,7 @@ public: | |||
| 653 | void CheckFunctionOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; | 653 | void CheckFunctionOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; |
| 654 | void CheckClassMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; | 654 | void CheckClassMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; |
| 655 | void CheckInterfaceMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; | 655 | void CheckInterfaceMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; |
| 656 | + void CheckInheritedExplicitOverloadRedeclarationRequirement(ETSObjectType *objectType); | ||
| 656 | 657 | ||
| 657 | ETSObjectType *GetRelevantArgumentedTypeFromChild(ETSObjectType *child, ETSObjectType *target); | 658 | ETSObjectType *GetRelevantArgumentedTypeFromChild(ETSObjectType *child, ETSObjectType *target); |
| 658 | util::StringView GetHashFromTypeArguments(const ArenaVector<Type *> &typeArgTypes); | 659 | util::StringView GetHashFromTypeArguments(const ArenaVector<Type *> &typeArgTypes); |
| @@ -959,6 +959,7 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte | |||
| 959 | // and it is possible that the FunctionType interface is not yet created. | 959 | // and it is possible that the FunctionType interface is not yet created. |
| 960 | if (builtinsInitialized) { // NOTE(vpukhov): #31391 | 960 | if (builtinsInitialized) { // NOTE(vpukhov): #31391 |
| 961 | CheckInterfaceFunctions(interfaceType); | 961 | CheckInterfaceFunctions(interfaceType); |
| 962 | + CheckInheritedExplicitOverloadRedeclarationRequirement(interfaceType); | ||
| 962 | } | 963 | } |
| 963 | CheckInterfaceAnnotations(interfaceDecl); | 964 | CheckInterfaceAnnotations(interfaceDecl); |
| 964 | 965 | ||
| @@ -3433,6 +3434,7 @@ void ETSChecker::CheckValidInheritance(ETSObjectType *classType, ir::ClassDefini | |||
| 3433 | 3434 | ||
| 3434 | const auto &allProps = classType->GetAllProperties(); | 3435 | const auto &allProps = classType->GetAllProperties(); |
| 3435 | auto const interfaceList = GetInterfaces(classType); | 3436 | auto const interfaceList = GetInterfaces(classType); |
| 3437 | + CheckInheritedExplicitOverloadRedeclarationRequirement(classType); | ||
| 3436 | auto const instancePropNamesTransitive = CollectInstancePropsTransitive(classType); | 3438 | auto const instancePropNamesTransitive = CollectInstancePropsTransitive(classType); |
| 3437 | 3439 | ||
| 3438 | for (auto *it : allProps) { | 3440 | for (auto *it : allProps) { |
| @@ -1834,6 +1834,50 @@ static std::unordered_set<util::StringView> CollectInstanceOverloadDeclsTransiti | |||
| 1834 | return transitivePropNames; | 1834 | return transitivePropNames; |
| 1835 | } | 1835 | } |
| 1836 | 1836 | ||
| 1837 | +void ETSChecker::CheckInheritedExplicitOverloadRedeclarationRequirement(ETSObjectType *objectType) | ||
| 1838 | +{ | ||
| 1839 | + auto isOverloadDecl = [](const auto prop) { | ||
| 1840 | + auto *decl = prop ? prop->Declaration() : nullptr; | ||
| 1841 | + return decl && decl->Node() && decl->Node()->IsOverloadDeclaration(); | ||
| 1842 | + }; | ||
| 1843 | + | ||
| 1844 | + std::unordered_map<util::StringView, std::vector<ETSObjectType *>> inheritedOverloads; | ||
| 1845 | + | ||
| 1846 | + objectType->EnsureTransitiveSupertypesInitialized(); | ||
| 1847 | + for (auto *superType : objectType->TransitiveSupertypes()) { | ||
| 1848 | + for (auto const [propName, prop] : superType->InstanceDecls()) { | ||
| 1849 | + if (isOverloadDecl(prop)) { | ||
| 1850 | + inheritedOverloads[propName].push_back(superType); | ||
| 1851 | + } | ||
| 1852 | + } | ||
| 1853 | + } | ||
| 1854 | + | ||
| 1855 | + auto const loc = objectType->GetDeclNode() ? objectType->GetDeclNode()->Start() : lexer::SourcePosition {}; | ||
| 1856 | + auto const &localDecls = objectType->InstanceDecls(); | ||
| 1857 | + | ||
| 1858 | + for (auto const &entry : inheritedOverloads) { | ||
| 1859 | + auto const &name = entry.first; | ||
| 1860 | + auto const &types = entry.second; | ||
| 1861 | + | ||
| 1862 | + if (types.size() <= 1) { | ||
| 1863 | + continue; | ||
| 1864 | + } | ||
| 1865 | + | ||
| 1866 | + if (auto it = localDecls.find(name); it != localDecls.end() && isOverloadDecl(it->second)) { | ||
| 1867 | + continue; | ||
| 1868 | + } | ||
| 1869 | + | ||
| 1870 | + auto independentCount = std::count_if(types.begin(), types.end(), [&](ETSObjectType *typeA) { | ||
| 1871 | + return !std::any_of(types.begin(), types.end(), [&](ETSObjectType *typeB) { | ||
| 1872 | + return typeA != typeB && Relation()->IsSupertypeOf(typeA, typeB); | ||
| 1873 | + }); | ||
| 1874 | + }); | ||
| 1875 | + if (independentCount > 1) { | ||
| 1876 | + LogError(diagnostic::NO_EXPLICIT_OVERLOAD_OVERRIDING, {name, objectType->Name()}, loc); | ||
| 1877 | + } | ||
| 1878 | + } | ||
| 1879 | +} | ||
| 1880 | + | ||
| 1837 | static void CheckOverloadDeclInheritance(ETSChecker *checker, ir::OverloadDeclaration *currOverloadDecl, | 1881 | static void CheckOverloadDeclInheritance(ETSChecker *checker, ir::OverloadDeclaration *currOverloadDecl, |
| 1838 | const std::unordered_set<util::StringView> &currOverloadDeclNames) | 1882 | const std::unordered_set<util::StringView> &currOverloadDeclNames) |
| 1839 | { | 1883 | { |
| @@ -435,6 +435,13 @@ public: | |||
| 435 | return propertiesInstantiated_; | 435 | return propertiesInstantiated_; |
| 436 | } | 436 | } |
| 437 | 437 | ||
| 438 | + void EnsureTransitiveSupertypesInitialized() const | ||
| 439 | + { | ||
| 440 | + if (transitiveSupertypes_ == nullptr) { | ||
| 441 | + transitiveSupertypes_ = allocator_->New<ArenaSet<ETSObjectType *>>(allocator_->Adapter()); | ||
| 442 | + } | ||
| 443 | + } | ||
| 444 | + | ||
| 438 | protected: | 445 | protected: |
| 439 | virtual ETSFunctionType *CreateMethodTypeForProp(util::StringView name) const; | 446 | virtual ETSFunctionType *CreateMethodTypeForProp(util::StringView name) const; |
| 440 | 447 | ||
| @@ -502,13 +509,6 @@ private: | |||
| 502 | } | 509 | } |
| 503 | } | 510 | } |
| 504 | 511 | ||
| 505 | - void EnsureTransitiveSupertypesInitialized() const | ||
| 506 | - { | ||
| 507 | - if (transitiveSupertypes_ == nullptr) { | ||
| 508 | - transitiveSupertypes_ = allocator_->New<ArenaSet<ETSObjectType *>>(allocator_->Adapter()); | ||
| 509 | - } | ||
| 510 | - } | ||
| 511 | - | ||
| 512 | bool CastWidening(TypeRelation *relation, Type *target, TypeFlag unboxFlags, TypeFlag wideningFlags); | 512 | bool CastWidening(TypeRelation *relation, Type *target, TypeFlag unboxFlags, TypeFlag wideningFlags); |
| 513 | void IdenticalUptoTypeArguments(TypeRelation *relation, Type *other); | 513 | void IdenticalUptoTypeArguments(TypeRelation *relation, Type *other); |
| 514 | void SubstitutePartialTypes(TypeRelation *relation, Type *other); | 514 | void SubstitutePartialTypes(TypeRelation *relation, Type *other); |
| @@ -0,0 +1,61 @@ | |||
| 1 | +/* | ||
Negative test cases can be added. ![]() ![]() | |||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*--- | ||
| 17 | +desc: >- | ||
| 18 | + A class that implements interfaces with explicit overloads of the same name | ||
| 19 | + remains valid when it declares a new explicit overload containing all | ||
| 20 | + inherited target methods. | ||
| 21 | +---*/ | ||
| 22 | + | ||
| 23 | +interface I1 { | ||
| 24 | + f1(x: int): string | ||
| 25 | + f2(x: boolean): string | ||
| 26 | + overload foo { f1, f2 } | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +interface I2 { | ||
| 30 | + f3(x: number): string | ||
| 31 | + f4(x: string): string | ||
| 32 | + overload foo { f3, f4 } | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +class A implements I1, I2 { | ||
| 36 | + f1(x: int): string { | ||
| 37 | + return "f1" | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + f2(x: boolean): string { | ||
| 41 | + return "f2" | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + f3(x: number): string { | ||
| 45 | + return "f3" | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + f4(x: string): string { | ||
| 49 | + return "f4" | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + overload foo { f2, f4, f1, f3 } | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +function main(): void { | ||
| 56 | + let a: A = new A() | ||
| 57 | + arktest.assertEQ("f1", a.foo(1)) | ||
| 58 | + arktest.assertEQ("f2", a.foo(false)) | ||
| 59 | + arktest.assertEQ("f3", a.foo(1.5)) | ||
| 60 | + arktest.assertEQ("f4", a.foo("ets")) | ||
| 61 | +} | ||
| @@ -0,0 +1,45 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*--- | ||
| 17 | +desc: >- | ||
| 18 | + One inherited explicit interface overload does not require redeclaration in a | ||
| 19 | + subinterface. The inherited overload remains usable through the subinterface. | ||
| 20 | +---*/ | ||
| 21 | + | ||
| 22 | +interface I1 { | ||
| 23 | + fooInt(x: int): string | ||
| 24 | + fooString(x: string): string | ||
| 25 | + overload foo { fooInt, fooString } | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +interface I2 extends I1 { | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +class A implements I2 { | ||
| 32 | + fooInt(x: int): string { | ||
| 33 | + return "int" | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + fooString(x: string): string { | ||
| 37 | + return "string" | ||
| 38 | + } | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +function main(): void { | ||
| 42 | + let a: I2 = new A() | ||
| 43 | + arktest.assertEQ("int", a.foo(1)) | ||
| 44 | + arktest.assertEQ("string", a.foo("ets")) | ||
| 45 | +} | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/*--- | ||
| 17 | +desc: >- | ||
| 18 | + A subinterface that inherits several explicit overload declarations of the | ||
| 19 | + same name may remain valid when it declares a new explicit overload that | ||
| 20 | + includes all inherited target methods, even when the inherited member sets | ||
| 21 | + are disjoint and the new order differs. | ||
| 22 | +---*/ | ||
| 23 | + | ||
| 24 | +interface I1 { | ||
| 25 | + f1(x: int): string | ||
| 26 | + f2(x: boolean): string | ||
| 27 | + overload foo { f1, f2 } | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +interface I2 { | ||
| 31 | + f3(x: number): string | ||
| 32 | + f4(x: string): string | ||
| 33 | + overload foo { f3, f4 } | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +interface I3 extends I1, I2 { | ||
| 37 | + overload foo { f4, f1, f3, f2 } | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +class A implements I3 { | ||
| 41 | + f1(x: int): string { | ||
| 42 | + return "f1" | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + f2(x: boolean): string { | ||
| 46 | + return "f2" | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + f3(x: number): string { | ||
| 50 | + return "f3" | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + f4(x: string): string { | ||
| 54 | + return "f4" | ||
| 55 | + } | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +function main(): void { | ||
| 59 | + let a: I3 = new A() | ||
| 60 | + arktest.assertEQ("f1", a.foo(1)) | ||
| 61 | + arktest.assertEQ("f2", a.foo(true)) | ||
| 62 | + arktest.assertEQ("f3", a.foo(1.5)) | ||
| 63 | + arktest.assertEQ("f4", a.foo("ets")) | ||
| 64 | +} | ||
| @@ -1096,6 +1096,11 @@ semantic: | |||
| 1096 | message: "Type '{}' has no call signatures." | 1096 | message: "Type '{}' has no call signatures." |
| 1097 | code_fix_ids: [FixAddMissingNewOperator, RemoveAccidentalCallParentheses] | 1097 | code_fix_ids: [FixAddMissingNewOperator, RemoveAccidentalCallParentheses] |
| 1098 | 1098 | ||
| 1099 | +- name: NO_EXPLICIT_OVERLOAD_OVERRIDING | ||
| 1100 | + id: 464 | ||
| 1101 | + message: "When multiple explicit overloads with the same name '{}' are inherited, the new explicit overload must be declared | ||
| 1102 | + instead in '{}'." | ||
| 1103 | + | ||
| 1099 | - name: NO_INDEX_ACCESS_METHOD | 1104 | - name: NO_INDEX_ACCESS_METHOD |
| 1100 | id: 250 | 1105 | id: 250 |
| 1101 | message: "Object type doesn't have proper index access method." | 1106 | message: "Object type doesn't have proper index access method." |


why move it to public?