diff --git a/ets2panda/linter/src/lib/autofixes/Autofixer.ts b/ets2panda/linter/src/lib/autofixes/Autofixer.ts index 1a85f9c1b535134a04c8f44ac101f6af45ae36e2..7d10ca692110050e35049892f828a29e1cdcb3dc 100644 --- a/ets2panda/linter/src/lib/autofixes/Autofixer.ts +++ b/ets2panda/linter/src/lib/autofixes/Autofixer.ts @@ -2059,7 +2059,8 @@ export class Autofixer { private fixObjectLiteralAsClass( objectLiteralExpr: ts.ObjectLiteralExpression, typeDecl: ts.ClassDeclaration | ts.InterfaceDeclaration | undefined, - enclosingStmt: ts.Node + enclosingStmt: ts.Node, + typeNode?: ts.TypeReferenceNode ): Autofix[] | undefined { if (this.utils.nodeCapturesValueFromEnclosingLocalScope(objectLiteralExpr, enclosingStmt)) { return undefined; @@ -2078,9 +2079,9 @@ export class Autofixer { const classDeclAndCtorInitProps = this.createClassDeclForObjectLiteral( objectLiteralExpr, enclosingStmt, - newClassName, - newInitInterfaceName, - typeDecl + { className: newClassName, initInterfaceName: newInitInterfaceName }, + typeDecl, + typeNode ); if (!classDeclAndCtorInitProps) { return undefined; @@ -2109,10 +2110,11 @@ export class Autofixer { private createClassDeclForObjectLiteral( objectLiteralExpr: ts.ObjectLiteralExpression, enclosingStmt: ts.Node, - newClassName: string, - newInitInterfaceName: string, - typeDecl: ts.ClassDeclaration | ts.InterfaceDeclaration | undefined + names: { className: string; initInterfaceName: string }, + typeDecl: ts.ClassDeclaration | ts.InterfaceDeclaration | undefined, + typeNode?: ts.TypeReferenceNode ): { classDecl: ts.ClassDeclaration; ctorInitProps: ts.PropertyAssignment[] } | undefined { + const { className, initInterfaceName } = names; const classFields: ts.PropertyDeclaration[] = []; const classMethods: (ts.MethodDeclaration | ts.AccessorDeclaration)[] = []; const ctorBodyStmts: ts.Statement[] = []; @@ -2142,14 +2144,14 @@ export class Autofixer { const classElements: ts.ClassElement[] = [...classFields]; if (ctorInitProps.length) { - classElements.push(Autofixer.createClassConstructorForObjectLiteral(newInitInterfaceName, ctorBodyStmts)); + classElements.push(Autofixer.createClassConstructorForObjectLiteral(initInterfaceName, ctorBodyStmts)); } classElements.push(...classMethods); - const heritageClauses = Autofixer.createHeritageClausesForObjectLiteralClass(typeDecl); + const heritageClauses = Autofixer.createHeritageClausesForObjectLiteralClass(typeDecl, typeNode); return { - classDecl: ts.factory.createClassDeclaration(undefined, newClassName, undefined, heritageClauses, classElements), + classDecl: ts.factory.createClassDeclaration(undefined, className, undefined, heritageClauses, classElements), ctorInitProps }; } @@ -2208,20 +2210,32 @@ export class Autofixer { } private static createHeritageClausesForObjectLiteralClass( - typeDecl: ts.ClassDeclaration | ts.InterfaceDeclaration | undefined + typeDecl: ts.ClassDeclaration | ts.InterfaceDeclaration | undefined, + typeNode?: ts.TypeReferenceNode ): ts.HeritageClause[] | undefined { if (!typeDecl?.name) { return undefined; } + const heritageTypeExpression = typeNode ? + Autofixer.entityNameToExpression(typeNode.typeName) : + ts.factory.createIdentifier(typeDecl.name.text); + return [ ts.factory.createHeritageClause( ts.isClassDeclaration(typeDecl) ? ts.SyntaxKind.ExtendsKeyword : ts.SyntaxKind.ImplementsKeyword, - [ts.factory.createExpressionWithTypeArguments(typeDecl.name, undefined)] + [ts.factory.createExpressionWithTypeArguments(heritageTypeExpression, undefined)] ) ]; } + private static entityNameToExpression(name: ts.EntityName): ts.Expression { + if (ts.isQualifiedName(name)) { + return ts.factory.createPropertyAccessExpression(Autofixer.entityNameToExpression(name.left), name.right); + } + return ts.factory.createIdentifier(name.text); + } + private static createClassConstructorForObjectLiteral( newInitInterfaceName: string, ctorBodyStmts: ts.Statement[] @@ -2271,7 +2285,7 @@ export class Autofixer { } const typeDecl = TsUtils.getDeclaration(objectLiteralType.getSymbol()); - if (!typeDecl || (!ts.isClassDeclaration(typeDecl) && !ts.isInterfaceDeclaration(typeDecl)) || !typeDecl.name) { + if (!typeDecl || !ts.isClassDeclaration(typeDecl) && !ts.isInterfaceDeclaration(typeDecl) || !typeDecl.name) { return undefined; } @@ -2288,7 +2302,8 @@ export class Autofixer { return undefined; } - return this.fixObjectLiteralAsClass(objectLiteralExpr, typeDecl, enclosingStmt); + const typeNode = (objectLiteralExpr.parent as ts.VariableDeclaration).type as ts.TypeReferenceNode | undefined; + return this.fixObjectLiteralAsClass(objectLiteralExpr, typeDecl, enclosingStmt, typeNode); } private hasMethodOverridingProperty( diff --git a/ets2panda/linter/test/main/object_literals_properties.ets b/ets2panda/linter/test/main/object_literals_properties.ets index f603b4bcfe2475ae269b8188d3be4814789ca7bf..84c1d5db860e0f4e0d5f200b21e71e7b7b8741d0 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets +++ b/ets2panda/linter/test/main/object_literals_properties.ets @@ -255,4 +255,31 @@ class C6 { } let map1:Map = new Map(); -let c6: C6 = {map1}; \ No newline at end of file +let c6: C6 = {map1}; + +// Namespace typed object literals +namespace X { + export class C { + m() { + console.log("C - 1"); + } + } + + export interface I { + m(a: number, b: string): void; + } +} + +function test() { + let c: X.C = { + m() { + console.log("C - 2"); + } + } + + let i: X.I = { + m(): void { + console.log("I"); + } + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json b/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json index ed1d4e6f861191b13e767782e6d9fea0841b2d4d..4f42a0f593f239b67d468925e2630974976a6fc9 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json @@ -1294,6 +1294,46 @@ "rule": "Object literal properties can only contain name-value pairs (arkts-obj-literal-props)", "severity": "ERROR" }, + { + "line": 274, + "column": 16, + "endLine": 274, + "endColumn": 17, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 275, + "column": 5, + "endLine": 277, + "endColumn": 6, + "problem": "ObjectLiteralProperty", + "suggest": "", + "rule": "Object literal properties can only contain name-value pairs (arkts-obj-literal-props)", + "severity": "ERROR" + }, + { + "line": 280, + "column": 16, + "endLine": 280, + "endColumn": 17, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 281, + "column": 5, + "endLine": 283, + "endColumn": 6, + "problem": "ObjectLiteralProperty", + "suggest": "", + "rule": "Object literal properties can only contain name-value pairs (arkts-obj-literal-props)", + "severity": "ERROR" + }, { "line": 187, "column": 3, diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json b/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json index 43d944717dcff1040dd2b731d7a45f1d06a76b41..04eed5c40725e7ae3e575caa83ea8b14ab9a98fc 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json @@ -2308,6 +2308,86 @@ "rule": "Object literal properties can only contain name-value pairs (arkts-obj-literal-props)", "severity": "ERROR" }, + { + "line": 274, + "column": 16, + "endLine": 274, + "endColumn": 17, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 275, + "column": 5, + "endLine": 277, + "endColumn": 6, + "problem": "ObjectLiteralProperty", + "autofix": [ + { + "start": 4958, + "end": 4958, + "replacementText": "class GeneratedObjectLiteralClass_11 extends X.C {\n m() {\n console.log(\"C - 2\");\n }\n}\n\n", + "line": 275, + "column": 5, + "endLine": 277, + "endColumn": 6 + }, + { + "start": 4991, + "end": 5040, + "replacementText": "new GeneratedObjectLiteralClass_11()", + "line": 275, + "column": 5, + "endLine": 277, + "endColumn": 6 + } + ], + "suggest": "", + "rule": "Object literal properties can only contain name-value pairs (arkts-obj-literal-props)", + "severity": "ERROR" + }, + { + "line": 280, + "column": 16, + "endLine": 280, + "endColumn": 17, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 281, + "column": 5, + "endLine": 283, + "endColumn": 6, + "problem": "ObjectLiteralProperty", + "autofix": [ + { + "start": 4958, + "end": 4958, + "replacementText": "class GeneratedObjectLiteralClass_12 implements X.I {\n m(): void {\n console.log(\"I\");\n }\n}\n\n", + "line": 281, + "column": 5, + "endLine": 283, + "endColumn": 6 + }, + { + "start": 5058, + "end": 5109, + "replacementText": "new GeneratedObjectLiteralClass_12()", + "line": 281, + "column": 5, + "endLine": 283, + "endColumn": 6 + } + ], + "suggest": "", + "rule": "Object literal properties can only contain name-value pairs (arkts-obj-literal-props)", + "severity": "ERROR" + }, { "line": 187, "column": 3, diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.json b/ets2panda/linter/test/main/object_literals_properties.ets.json index c8d9bf18dc81b732a67d5bb6570894fd796d4f76..97cb35867cee19acd5d2782fa51f2cc23bdc5f80 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.json @@ -294,6 +294,26 @@ "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", "severity": "ERROR" }, + { + "line": 274, + "column": 16, + "endLine": 274, + "endColumn": 17, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 280, + "column": 16, + "endLine": 280, + "endColumn": 17, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, { "line": 187, "column": 3, diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets index a47d79115e50477b35149732f1a86885fd3e0a36..c2c24296503bfb2c9c06af0882c3e9dd3ea8e123 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets +++ b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets @@ -317,4 +317,35 @@ class C6 { } let map1:Map = new Map(); -let c6: C6 = {map1: map1}; \ No newline at end of file +let c6: C6 = {map1: map1}; + +// Namespace typed object literals +namespace X { + export class C { + m() { + console.log("C - 1"); + } + } + + export interface I { + m(a: number, b: string): void; + } +} + +class GeneratedObjectLiteralClass_11 extends X.C { + m() { + console.log("C - 2"); + } +} + +class GeneratedObjectLiteralClass_12 implements X.I { + m(): void { + console.log("I"); + } +} + +function test() { + let c: X.C = new GeneratedObjectLiteralClass_11() + + let i: X.I = new GeneratedObjectLiteralClass_12() +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json index f7f45413213d057e773dee3e964cb26baae12df2..b652f8984b042e4ff3b80b0521adea79db14aef4 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json @@ -314,6 +314,16 @@ "rule": "Object literal properties can only contain name-value pairs (arkts-obj-literal-props)", "severity": "ERROR" }, + { + "line": 342, + "column": 5, + "endLine": 342, + "endColumn": 6, + "problem": "MethodInheritRule", + "suggest": "", + "rule": "Overridden method parameters and return types must respect type inheritance principles (arkts-method-inherit-rule)", + "severity": "ERROR" + }, { "line": 245, "column": 3,