diff --git a/ets2panda/linter/src/lib/CookBookMsg.ts b/ets2panda/linter/src/lib/CookBookMsg.ts index b021201ab0e03bb23043a5c1510b3169b3237f2f..bfe6c7024c62ece582abd28446faef1135e99b90 100644 --- a/ets2panda/linter/src/lib/CookBookMsg.ts +++ b/ets2panda/linter/src/lib/CookBookMsg.ts @@ -310,7 +310,7 @@ cookBookTag[274] = cookBookTag[275] = 'The Custom component with custom layout capability needs to add the "@CustomLayout" decorator (arkui-custom-layout-need-add-decorator)'; cookBookTag[276] = - 'ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)'; + 'ArkTS 1.2 should implement all optional fields from the interface in the class (arkts-no-class-omit-interface-optional-prop)'; cookBookTag[277] = 'Creating local classes is not supported (arkts-no-local-class)'; cookBookTag[281] = '"@Prop" decorator is not supported (arkui-no-prop-decorator)'; cookBookTag[282] = '"@StorageProp" decorator is not supported (arkui-no-storageprop-decorator)'; diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index c4edcff3b3de35c964bfe1d8844226e382898b06..c518b28b9f3a05bc152e5e5d0c7e833da176e31c 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -8786,13 +8786,15 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } /** - * Ensures classes fully implement all properties from their interfaces. + * Ensures classes explicitly implement all optional properties from their interfaces. + * Required fields are ignored because the it's already enforced on ArkTS1.1. */ private handleInterfaceFieldImplementation(clause: ts.HeritageClause): void { // Only process implements clauses if (clause.token !== ts.SyntaxKind.ImplementsKeyword) { return; } + const classDecl = clause.parent as ts.ClassDeclaration; if (!ts.isClassDeclaration(classDecl) || !classDecl.name) { return; @@ -8808,8 +8810,10 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { if (!interfaceDecl) { continue; } + // Gather all inherited interfaces const allInterfaces = this.getAllInheritedInterfaces(interfaceDecl); + // If the class fails to implement any member, report once and exit if (!this.classImplementsAllMembers(classDecl, allInterfaces)) { this.incrementCounters(classDecl.name, FaultID.InterfaceFieldNotImplemented); @@ -8851,24 +8855,32 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } /** - * Returns true if the class declaration declares every property or method + * Returns true if the class declaration declares every optional property or method * signature from the provided list of interface declarations. */ private classImplementsAllMembers(classDecl: ts.ClassDeclaration, interfaces: ts.InterfaceDeclaration[]): boolean { void this; + // Check optional members only for (const intf of interfaces) { for (const member of intf.members) { - if ((ts.isPropertySignature(member) || ts.isMethodSignature(member)) && ts.isIdentifier(member.name)) { - const name = member.name.text; - const found = classDecl.members.some((m) => { + if ( + (ts.isPropertySignature(member) || ts.isMethodSignature(member)) && + ts.isIdentifier(member.name) && + member.questionToken + ) { + const propName = member.name.text; + + // does derived class have this member? + const hasImpl = classDecl.members.some((m) => { return ( (ts.isPropertyDeclaration(m) || ts.isMethodDeclaration(m)) && ts.isIdentifier(m.name) && - m.name.text === name + m.name.text === propName ); }); - if (!found) { + + if (!hasImpl) { return false; } } diff --git a/ets2panda/linter/test/builtin/builtin_array_negative.ets.arkts2.json b/ets2panda/linter/test/builtin/builtin_array_negative.ets.arkts2.json index bc4681ba7423cd3121bb98b68d138be7e9abc979..7a708cf5aa78607c14fe9a1dd1ada1e31c6c507c 100755 --- a/ets2panda/linter/test/builtin/builtin_array_negative.ets.arkts2.json +++ b/ets2panda/linter/test/builtin/builtin_array_negative.ets.arkts2.json @@ -224,16 +224,6 @@ "rule": "Extends or implements expression are not supported(arkts-no-extends-expression)", "severity": "ERROR" }, - { - "line": 47, - "column": 7, - "endLine": 47, - "endColumn": 11, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 59, "column": 3, diff --git a/ets2panda/linter/test/builtin/builtin_class.ets.arkts2.json b/ets2panda/linter/test/builtin/builtin_class.ets.arkts2.json index 11d39cae875aa581d22ce969828f85b9be9c6996..d4f92e186a552f92744e71c8977aeb014209ae57 100755 --- a/ets2panda/linter/test/builtin/builtin_class.ets.arkts2.json +++ b/ets2panda/linter/test/builtin/builtin_class.ets.arkts2.json @@ -24,16 +24,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 16, - "column": 7, - "endLine": 16, - "endColumn": 17, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 16, "column": 47, @@ -64,16 +54,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 19, - "column": 7, - "endLine": 19, - "endColumn": 16, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 19, "column": 43, @@ -104,16 +84,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 28, - "column": 7, - "endLine": 28, - "endColumn": 18, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 28, "column": 48, @@ -144,16 +114,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 31, - "column": 7, - "endLine": 31, - "endColumn": 20, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 31, "column": 51, @@ -204,16 +164,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 40, - "column": 7, - "endLine": 40, - "endColumn": 20, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 40, "column": 32, @@ -244,16 +194,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 43, - "column": 7, - "endLine": 43, - "endColumn": 23, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 43, "column": 57, @@ -281,7 +221,7 @@ "endColumn": 20, "problem": "InterfaceFieldNotImplemented", "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", + "rule": "ArkTS 1.2 should implement all optional fields from the interface in the class (arkts-no-class-omit-interface-optional-prop)", "severity": "ERROR" }, { @@ -344,16 +284,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 56, - "column": 7, - "endLine": 56, - "endColumn": 20, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 56, "column": 53, @@ -404,16 +334,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 60, - "column": 7, - "endLine": 60, - "endColumn": 13, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 60, "column": 67, @@ -474,16 +394,6 @@ "rule": "API is not support use class in this API (arkts-builtin-final-class)", "severity": "ERROR" }, - { - "line": 65, - "column": 7, - "endLine": 65, - "endColumn": 15, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 65, "column": 43, diff --git a/ets2panda/linter/test/builtin/builtin_object_negative.ets.arkts2.json b/ets2panda/linter/test/builtin/builtin_object_negative.ets.arkts2.json index 533a742ee1653ab2d1e42e8c26062193c05c9da9..29902703e0120f3d0107057e2d0e9716281a789d 100755 --- a/ets2panda/linter/test/builtin/builtin_object_negative.ets.arkts2.json +++ b/ets2panda/linter/test/builtin/builtin_object_negative.ets.arkts2.json @@ -194,16 +194,6 @@ "rule": "API is not support ctor signature and func (arkts-builtin-cotr)", "severity": "ERROR" }, - { - "line": 48, - "column": 7, - "endLine": 48, - "endColumn": 11, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 48, "column": 23, @@ -374,16 +364,6 @@ "rule": "Using narrowing of types is not allowed in this API (arkts-builtin-narrow-types)", "severity": "ERROR" }, - { - "line": 92, - "column": 7, - "endLine": 92, - "endColumn": 11, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 93, "column": 3, @@ -525,4 +505,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/builtin/builtin_object_positive.ets.arkts2.json b/ets2panda/linter/test/builtin/builtin_object_positive.ets.arkts2.json index 86f288bc70ec99db34200378bc3c3c6f2079ebab..7e7f373314ddd372af50cd4c50f48ba36d49e769 100755 --- a/ets2panda/linter/test/builtin/builtin_object_positive.ets.arkts2.json +++ b/ets2panda/linter/test/builtin/builtin_object_positive.ets.arkts2.json @@ -14,16 +14,6 @@ "limitations under the License." ], "result": [ - { - "line": 18, - "column": 7, - "endLine": 18, - "endColumn": 11, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 18, "column": 23, diff --git a/ets2panda/linter/test/deprecatedapi/swiper_api.ets.arkts2.json b/ets2panda/linter/test/deprecatedapi/swiper_api.ets.arkts2.json index 3799eb68a19549246cf75b3ad5d5c2d6a045b7a9..0d7d48b7bd59d68dded16567af1fbab103cd08c2 100755 --- a/ets2panda/linter/test/deprecatedapi/swiper_api.ets.arkts2.json +++ b/ets2panda/linter/test/deprecatedapi/swiper_api.ets.arkts2.json @@ -21,7 +21,7 @@ "endColumn": 11, "problem": "InterfaceFieldNotImplemented", "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", + "rule": "ArkTS 1.2 should implement all optional fields from the interface in the class (arkts-no-class-omit-interface-optional-prop)", "severity": "ERROR" }, { diff --git a/ets2panda/linter/test/main/no_class_omit_interface_optional.ets b/ets2panda/linter/test/main/no_class_omit_interface_optional.ets index 6ea84551998b2c75382e88a6425f999647c37f8b..875ef361b3472e323ebf4138622eecd8395bf0c3 100644 --- a/ets2panda/linter/test/main/no_class_omit_interface_optional.ets +++ b/ets2panda/linter/test/main/no_class_omit_interface_optional.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -// 1) All fields implemented — no error +// 1) Required Fields interface I1 { a: number; b: string; @@ -23,21 +23,11 @@ class C1 implements I1 { // ✅ no error b: string = ''; } -// 2) One field missing — error -interface I2 { - x: boolean; - y: number; - cb(): void; -} -class C2 implements I2 { // ❌ Error: missing `y` - x: boolean = true; -} -class C21 implements I2 { // ❌ Error: missing `cb()` - x: boolean = true; - y: number = 5.0; +class C2 implements I1 { // ✅ no error - Required fields are already handled on ArkTS 1.1 + a: number = 0.0; } -// 3) Optional fields are treated as required — error +// 2) Optional fields are treated as required interface I3 { foo?: string; bar?: number; @@ -45,18 +35,17 @@ interface I3 { class C3 implements I3 { // ❌ Error: missing both `foo` and `bar` (first missing stops checking) } -// 4) Optional field implemented class C4 implements I3 { // ✅ no error foo?: string; bar?: number; } -// 5) Interface extends another +// 3) Interface extends another interface A5 { - p: string; + p?: string; } interface B5 extends A5 { - q: string; + q?: string; } class C5 implements B5 { // ✅ no error p: string = 'hello'; @@ -69,9 +58,9 @@ class C61 implements B5 { // ❌ Error: missing `p` q: string = 'hello'; } -// 6) Multiple interfaces at once -interface I6a { u: number } -interface I6b { v: boolean } +// 4) Multiple interfaces at once +interface I6a { u?: number } +interface I6b { v?: boolean } class C7 implements I6a, I6b { // ✅ no error u: number = 42.0; v: boolean = false; diff --git a/ets2panda/linter/test/main/no_class_omit_interface_optional.ets.arkts2.json b/ets2panda/linter/test/main/no_class_omit_interface_optional.ets.arkts2.json index 6281cdd59ea5dcab7b10fcf2a9cb9ecb3c3dc31e..5b06edf9f560f71c2bde4dbe84d29868c63a2267 100644 --- a/ets2panda/linter/test/main/no_class_omit_interface_optional.ets.arkts2.json +++ b/ets2panda/linter/test/main/no_class_omit_interface_optional.ets.arkts2.json @@ -14,65 +14,45 @@ "limitations under the License." ], "result": [ - { - "line": 32, - "column": 7, - "endLine": 32, - "endColumn": 9, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, { "line": 35, "column": 7, "endLine": 35, - "endColumn": 10, - "problem": "InterfaceFieldNotImplemented", - "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", - "severity": "ERROR" - }, - { - "line": 45, - "column": 7, - "endLine": 45, "endColumn": 9, "problem": "InterfaceFieldNotImplemented", "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", + "rule": "ArkTS 1.2 should implement all optional fields from the interface in the class (arkts-no-class-omit-interface-optional-prop)", "severity": "ERROR" }, { - "line": 65, + "line": 54, "column": 7, - "endLine": 65, + "endLine": 54, "endColumn": 9, "problem": "InterfaceFieldNotImplemented", "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", + "rule": "ArkTS 1.2 should implement all optional fields from the interface in the class (arkts-no-class-omit-interface-optional-prop)", "severity": "ERROR" }, { - "line": 68, + "line": 57, "column": 7, - "endLine": 68, + "endLine": 57, "endColumn": 10, "problem": "InterfaceFieldNotImplemented", "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", + "rule": "ArkTS 1.2 should implement all optional fields from the interface in the class (arkts-no-class-omit-interface-optional-prop)", "severity": "ERROR" }, { - "line": 79, + "line": 68, "column": 7, - "endLine": 79, + "endLine": 68, "endColumn": 9, "problem": "InterfaceFieldNotImplemented", "suggest": "", - "rule": "ArkTS 1.2 should implement all fields in the interface in the class (arkts-no-class-omit-interface-optional-prop)", + "rule": "ArkTS 1.2 should implement all optional fields from the interface in the class (arkts-no-class-omit-interface-optional-prop)", "severity": "ERROR" } ] -} +} \ No newline at end of file