From 593361d4a2105f858ef73ccd802855ada49c54c0 Mon Sep 17 00:00:00 2001 From: wangjiahui Date: Tue, 9 Sep 2025 10:18:42 +0800 Subject: [PATCH] 0702 sync 0728: Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICX1GY Signed-off-by: wangjiahui --- .../linter/arkanalyzer/src/core/base/Expr.ts | 7 +- .../checker/migration/NumericSemanticCheck.ts | 247 +++++-- .../homecheck/src/utils/common/SDKUtils.ts | 5 + ets2panda/linter/rule-config.json | 46 +- ets2panda/linter/src/lib/CookBookMsg.ts | 2 + ets2panda/linter/src/lib/FaultAttrs.ts | 1 + ets2panda/linter/src/lib/FaultDesc.ts | 3 + ets2panda/linter/src/lib/Problems.ts | 1 + ets2panda/linter/src/lib/TypeScriptLinter.ts | 164 ++++- .../linter/src/lib/autofixes/Autofixer.ts | 197 ++++-- ets2panda/linter/src/lib/utils/TsUtils.ts | 18 + .../src/lib/utils/consts/ArkuiConstants.ts | 5 +- .../src/lib/utils/consts/LifecycleMonitor.ts | 17 + .../linter/test/main/entry_componentv2_1.ets | 38 ++ .../main/entry_componentv2_1.ets.args.json | 21 + .../main/entry_componentv2_1.ets.arkts2.json | 178 +++++ .../main/entry_componentv2_1.ets.autofix.json | 332 ++++++++++ .../test/main/entry_componentv2_1.ets.json | 17 + .../main/entry_componentv2_1.ets.migrate.ets | 45 ++ .../main/entry_componentv2_1.ets.migrate.json | 17 + .../linter/test/main/entry_componentv2_2.ets | 46 ++ .../main/entry_componentv2_2.ets.args.json | 21 + .../main/entry_componentv2_2.ets.arkts2.json | 208 ++++++ .../main/entry_componentv2_2.ets.autofix.json | 395 ++++++++++++ .../test/main/entry_componentv2_2.ets.json | 17 + .../main/entry_componentv2_2.ets.migrate.ets | 53 ++ .../main/entry_componentv2_2.ets.migrate.json | 17 + .../linter/test/main/parameter_properties.ets | 48 ++ .../main/parameter_properties.ets.arkts2.json | 100 +++ .../parameter_properties.ets.autofix.json | 608 ++++++++++++++++-- .../main/parameter_properties.ets.migrate.ets | 124 +++- .../parameter_properties.ets.migrate.json | 4 +- .../main/private_identifiers.ets.autofix.json | 373 +++++++++-- .../main/private_identifiers.ets.migrate.ets | 4 +- .../sdk_ability_asynchronous_lifecycle.ets | 9 +- ...ity_asynchronous_lifecycle.ets.arkts2.json | 10 + .../main/sdk_ability_lifecycle_monitor.ets | 1 + ..._ability_lifecycle_monitor.ets.arkts2.json | 36 +- .../sdk_ability_lifecycle_monitor.ets.json | 4 +- .../linter/test/rules/rule25.ets.autofix.json | 130 ++-- .../linter/test/rules/rule25.ets.migrate.ets | 20 +- 41 files changed, 3177 insertions(+), 412 deletions(-) create mode 100644 ets2panda/linter/src/lib/utils/consts/LifecycleMonitor.ts create mode 100644 ets2panda/linter/test/main/entry_componentv2_1.ets create mode 100644 ets2panda/linter/test/main/entry_componentv2_1.ets.args.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_1.ets.arkts2.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_1.ets.autofix.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_1.ets.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.ets create mode 100644 ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_2.ets create mode 100644 ets2panda/linter/test/main/entry_componentv2_2.ets.args.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_2.ets.arkts2.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_2.ets.autofix.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_2.ets.json create mode 100644 ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.ets create mode 100644 ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.json diff --git a/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts b/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts index 9e41e6f201..226d644ca5 100644 --- a/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts +++ b/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts @@ -699,11 +699,10 @@ export abstract class AbstractBinopExpr extends AbstractExpr { case '/': case '%': case '**': - if (op1Type === NumberType.getInstance() && op2Type === NumberType.getInstance()) { - type = NumberType.getInstance(); - } - if (op1Type === BigIntType.getInstance() && op2Type === BigIntType.getInstance()) { + if (op1Type === BigIntType.getInstance() || op2Type === BigIntType.getInstance()) { type = BigIntType.getInstance(); + } else { + type = NumberType.getInstance(); } break; case '!=': diff --git a/ets2panda/linter/homecheck/src/checker/migration/NumericSemanticCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/NumericSemanticCheck.ts index bff6fd3a64..00c9adb29a 100644 --- a/ets2panda/linter/homecheck/src/checker/migration/NumericSemanticCheck.ts +++ b/ets2panda/linter/homecheck/src/checker/migration/NumericSemanticCheck.ts @@ -35,6 +35,7 @@ import { BooleanType, CallGraph, ClassSignature, + classSignatureCompare, ClassType, ClosureFieldRef, CONSTRUCTOR_NAME, @@ -244,6 +245,15 @@ export class NumericSemanticCheck implements BaseChecker { logger.error(`Error checking array index in stmt: ${stmt.toString()}, method: ${target.getSignature().toString()}, error: ${e}`); } } + + // 场景4:async方法检查返回值,与返回值相关的所有变量为number类型,同时替换之前对变量的修复为int/long的告警 + if (target.containsModifier(ModifierType.ASYNC)) { + try { + this.checkAsyncReturnStmts(target.getReturnStmt()); + } catch (e) { + logger.error(`Error checking async method return operands, method: ${target.getSignature().toString()}, error: ${e}`); + } + } } private checkSdkArgsInStmt(stmt: Stmt): void { @@ -576,6 +586,54 @@ export class NumericSemanticCheck implements BaseChecker { }); } + private checkAsyncReturnStmts(stmts: Stmt[]): void { + const res = new Map(); + this.callDepth = 0; + for (const stmt of stmts) { + if (!(stmt instanceof ArkReturnStmt)) { + continue; + } + const returnOp = stmt.getOp(); + if (!Utils.isNearlyNumberType(returnOp.getType())) { + continue; + } + + if (returnOp instanceof NumberConstant && !this.isNumberConstantActuallyFloat(returnOp)) { + // 场景1:直接return整型字面量,需要将整型字面量改为浮点型字面量 + this.addIssueReport(RuleCategory.NumericLiteral, NumberCategory.number, IssueReason.UsedWithOtherType, true, stmt, returnOp); + } else if (returnOp instanceof Local) { + // 场景2:检查return变量以及其生命周期内有关联的其他变量,全部需要定义为number + // 检查入口stmt为local的声明语句,便于查找当前是否已有该变量的issue生成 + const declaringStmt = returnOp.getDeclaringStmt(); + if (declaringStmt === null) { + continue; + } + this.isLocalOnlyUsedAsIntLong(declaringStmt, returnOp, res, NumberCategory.number); + } else { + logger.error( + `Need to handle new return op type, stmt: ${stmt.toString()}, method: ${stmt.getCfg().getDeclaringMethod().getSignature().toString()}` + ); + } + } + res.forEach((issueInfo, local) => { + if (this.shouldIgnoreLocal(local)) { + return; + } + const declaringStmt = local.getDeclaringStmt(); + if (declaringStmt === null) { + return; + } + // 无论local的判定结果是什么,均需要进行自动修复类型注解为int或者number + this.addIssueReport(RuleCategory.NumericLiteral, issueInfo.numberCategory, issueInfo.issueReason, true, declaringStmt, local); + }); + this.classFieldRes.forEach((fieldInfo, field) => { + if (fieldInfo.issueReason === IssueReason.OnlyUsedAsIntLong || fieldInfo.issueReason === IssueReason.UsedWithOtherType) { + // 如果能明确判断出field是int或非int,则添加类型注解int或number,其他找不全的场景不变 + this.addIssueReport(RuleCategory.NumericLiteral, fieldInfo.numberCategory, fieldInfo.issueReason, true, undefined, undefined, field); + } + }); + } + private getFieldRefActualArrayRef(stmt: Stmt): ArkInstanceFieldRef | null { const fieldRef = stmt.getFieldRef(); if (fieldRef === undefined || !(fieldRef instanceof ArkInstanceFieldRef)) { @@ -919,7 +977,7 @@ export class NumericSemanticCheck implements BaseChecker { const ets2ParamType = ets2Params[i].getType(); const ets1ParamType = ets1Params[i].getType(); if ( - ets2ParamType === ets1ParamType || + this.compareTypes(ets1ParamType, ets2ParamType) || (ets1ParamType instanceof NumberType && (this.isIntType(ets2ParamType) || this.isLongType(ets2ParamType))) ) { continue; @@ -937,6 +995,18 @@ export class NumericSemanticCheck implements BaseChecker { return null; } + private compareTypes(param1: Type, param2: Type): boolean { + if (param1 instanceof ClassType && param2 instanceof ClassType) { + const classSign1 = param1.getClassSignature(); + const classSign2 = param2.getClassSignature(); + if (SdkUtils.isClassFromSdk(classSign1) && SdkUtils.isClassFromSdk(classSign2)) { + return classSign1.getClassName() === classSign2.getClassName() + } + return classSignatureCompare(classSign1, classSign2) + } + return param1 === param2 + } + private matchEts1NumberEts2IntLongMethodSig(ets2Sigs: MethodSignature[], ets1Sig: MethodSignature): MethodSignature | null { let intSDKMatched: MethodSignature | null = null; const ets1Params = ets1Sig.getMethodSubSignature().getParameters(); @@ -1098,6 +1168,7 @@ export class NumericSemanticCheck implements BaseChecker { } if (value instanceof StringConstant) { // 存在将‘100%’,‘auto’等赋值给numberType的情况,可能是ArkAnalyzer对左值的推导有错误,左值应该是联合类型 + // TODO: arr[await foo()]语句ArkIR将index表示成‘await %2’,应该表示成ArkAwaitExpr,但都认为是number,仍旧是正确结果 return IssueReason.UsedWithOtherType; } if (value instanceof Local) { @@ -1114,7 +1185,7 @@ export class NumericSemanticCheck implements BaseChecker { } private isNumberConstantActuallyFloat(constant: NumberConstant): boolean { - const valueStr = constant.getValue(); + const valueStr = constant.getValue().toLowerCase(); if (valueStr.includes('.') && !valueStr.includes('e')) { // 数字字面量非科学计数的写法,并且有小数点,则一定是浮点数,1.0也认为是float return true; @@ -1172,7 +1243,10 @@ export class NumericSemanticCheck implements BaseChecker { } return IssueReason.OnlyUsedAsIntLong; } - // 在之前的语句检测中已查找过此local并生成相应的issue,直接根据issue的内容返回结果,如果issue中是int,检查的是long,则结果为long + // 在之前的语句检测中已查找过此local并生成相应的issue,直接根据issue的内容返回结果, + // 1. 如果issue中是int,检查的是long,则结果为long + // 2. 如果issue中是int,检查的是int,则结果为int + // 3. 如果issue中是int,检查的是number,则需要重新检查与此local相关的所有变量,全部为number const currentIssue = this.getLocalIssueFromIssueList(local, stmt); if (currentIssue && currentIssue.fix instanceof RuleFix) { const issueReason = this.getIssueReasonFromDefectInfo(currentIssue.defect); @@ -1182,12 +1256,10 @@ export class NumericSemanticCheck implements BaseChecker { hasChecked.set(local, { issueReason: issueReason, numberCategory: numberCategory }); return issueReason; } - if (numberCategory === NumberCategory.long) { + if (numberCategory === NumberCategory.long || numberCategory === NumberCategory.int) { hasChecked.set(local, { issueReason: issueReason, numberCategory: numberCategory }); - } else { - hasChecked.set(local, { issueReason: issueReason, numberCategory: issueCategory }); + return issueReason; } - return issueReason; } } @@ -1306,8 +1378,14 @@ export class NumericSemanticCheck implements BaseChecker { } return { issueReason: IssueReason.OnlyUsedAsIntLong, numberCategory }; } - if (stmt instanceof ArkReturnStmt || stmt instanceof ArkIfStmt) { - // return语句,local作为返回值,不会影响其值的变化,不会导致int被重新赋值为number使用 + if (stmt instanceof ArkReturnStmt) { + // return语句,local作为返回值,若为同步函数则不会影响其值的变化,不会导致int被重新赋值为number使用,若为异步函数则一定为number + if (stmt.getCfg().getDeclaringMethod().containsModifier(ModifierType.ASYNC)) { + return { issueReason: IssueReason.UsedWithOtherType, numberCategory: NumberCategory.number }; + } + return { issueReason: IssueReason.OnlyUsedAsIntLong, numberCategory }; + } + if (stmt instanceof ArkIfStmt) { // 条件判断语句,local作为condition expr的op1或op2,进行二元条件判断,不会影响其值的变化,不会导致int被重新赋值为number使用 return { issueReason: IssueReason.OnlyUsedAsIntLong, numberCategory }; } @@ -1454,15 +1532,25 @@ export class NumericSemanticCheck implements BaseChecker { if (expr.getOperator() === NormalBinaryOperator.Division) { const op1 = expr.getOp1(); const op2 = expr.getOp2(); + let fixedEnumOp2Number = false; if (op1 instanceof NumberConstant && !this.isNumberConstantActuallyFloat(op1)) { this.addIssueReport(RuleCategory.NumericLiteral, NumberCategory.number, IssueReason.UsedWithOtherType, true, stmt, op1); } else if (op1 instanceof Local) { hasChecked.set(op1, { issueReason: IssueReason.UsedWithOtherType, numberCategory: NumberCategory.number }); + // 对于 enum.A / enum.B的场景,需要将第一个enum.A修复成enum.A.valueOf().toDouble() + if (op1.getName().startsWith(TEMP_LOCAL_PREFIX) && op1.getType() instanceof EnumValueType) { + this.addIssueReport(RuleCategory.NumericLiteral, NumberCategory.number, IssueReason.UsedWithOtherType, true, stmt, op1); + fixedEnumOp2Number = true; + } + this.checkDivisionWithLocal(op1); } if (op2 instanceof NumberConstant && !this.isNumberConstantActuallyFloat(op2)) { this.addIssueReport(RuleCategory.NumericLiteral, NumberCategory.number, IssueReason.UsedWithOtherType, true, stmt, op2); } else if (op2 instanceof Local) { hasChecked.set(op2, { issueReason: IssueReason.UsedWithOtherType, numberCategory: NumberCategory.number }); + if (!fixedEnumOp2Number && op2.getName().startsWith(TEMP_LOCAL_PREFIX) && op2.getType() instanceof EnumValueType) { + this.addIssueReport(RuleCategory.NumericLiteral, NumberCategory.number, IssueReason.UsedWithOtherType, true, stmt, op2); + } } return IssueReason.UsedWithOtherType; } @@ -1514,17 +1602,8 @@ export class NumericSemanticCheck implements BaseChecker { return IssueReason.OnlyUsedAsIntLong; } if (expr instanceof ArkAwaitExpr) { - const promise = expr.getPromise(); - if (promise instanceof Local) { - const declaringStmt = promise.getDeclaringStmt(); - if (declaringStmt === null || !(declaringStmt instanceof ArkAssignStmt)) { - logger.error('Missing or wrong declaringStmt for await promise'); - return IssueReason.CannotFindAll; - } - return this.checkValueOnlyUsedAsIntLong(declaringStmt, declaringStmt.getRightOp(), hasChecked, numberCategory); - } - logger.error(`Need to handle new type of promise: ${promise.getType().toString()}`); - return IssueReason.Other; + // async函数一定返回promise类型,不对其进行Promise的转换 + return IssueReason.UsedWithOtherType; } if (expr instanceof ArkCastExpr) { return this.checkValueOnlyUsedAsIntLong(stmt, expr.getOp(), hasChecked, numberCategory); @@ -1545,6 +1624,57 @@ export class NumericSemanticCheck implements BaseChecker { return IssueReason.Other; } + private isArkAssignStmt(stmt: Stmt): boolean { + return stmt instanceof ArkAssignStmt; + } + + private isArkNormalBinopExpr(value: Value): boolean { + return value instanceof ArkNormalBinopExpr; + } + + private checkDivisionWithLocal(local: Local): void { + if (!local.getName().startsWith(TEMP_LOCAL_PREFIX)) { + return; + } + const decl = local.getDeclaringStmt(); + if (decl === null) { + return; + } + + if (!this.isArkAssignStmt(decl)) { + return; + } + const assignStmt = decl as ArkAssignStmt; + + const rightSide = assignStmt.getRightOp(); + if (!this.isArkNormalBinopExpr(rightSide)) { + return; + } + + const binaryOperation = rightSide as ArkNormalBinopExpr; + const operator = binaryOperation.getOperator(); + switch (operator) { + case NormalBinaryOperator.Division: + case NormalBinaryOperator.Addition: + case NormalBinaryOperator.Multiplication: + case NormalBinaryOperator.Exponentiation: + case NormalBinaryOperator.Subtraction: { + const lhs = binaryOperation.getOp1(); + if (lhs instanceof Local) { + this.checkDivisionWithLocal(lhs); + } + if (lhs instanceof NumberConstant) { + this.addIssueReport(RuleCategory.NumericLiteral, NumberCategory.number, IssueReason.UsedWithOtherType, true, decl, lhs); + return; + } + break; + } + default: { + break; + } + } + } + private isAbstractRefOnlyUsedAsIntLong(stmt: Stmt, ref: AbstractRef, hasChecked: Map, numberCategory: NumberCategory): IssueReason { if (ref instanceof ArkArrayRef) { // 使用数组中某元素进行赋值的场景很复杂,需要判断index的具体值,需要判断数组中的队应元素的全部使用场景,当前不做检查,直接返回false @@ -1985,6 +2115,7 @@ export class NumericSemanticCheck implements BaseChecker { return this.issuesMap.get(mapKey) ?? null; } + // stmt should be the declaring stmt of this local private getLocalIssueFromIssueList(local: Local, stmt: Stmt): IssueReport | null { const filePath = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile().getFilePath(); const position = getLineAndColumn(stmt, local, true); @@ -2333,7 +2464,7 @@ export class NumericSemanticCheck implements BaseChecker { return sourceFile; } - private generateRuleFixForLocalDefine(sourceFile: ts.SourceFile, warnInfo: WarnInfo, numberCategory: NumberCategory): RuleFix | null { + private generateRuleFixForLocalDefine(sourceFile: ts.SourceFile, warnInfo: WarnInfo, numberCategory: NumberCategory, isOptional?: boolean): RuleFix | null { // warnInfo中对于变量声明语句的位置信息只包括变量名,不包括变量声明时的类型注解位置 // 此处先获取变量名后到行尾的字符串信息,判断是替换‘: number’ 或增加 ‘: int’ const localRange = FixUtils.getRangeWithAst(sourceFile, { @@ -2362,7 +2493,7 @@ export class NumericSemanticCheck implements BaseChecker { logger.error('Failed to getting text of the fix range info when generating auto fix info.'); return null; } - ruleFix.text = `${localString}: ${numberCategory}`; + ruleFix.text = isOptional ? `${localString}: ${numberCategory} | undefined` : `${localString}: ${numberCategory}`; return ruleFix; } // 场景2:变量或函数入参,有类型注解的场景,需要将类型注解替换成新的类型,同时考虑可选参数即'?:' @@ -2411,21 +2542,26 @@ export class NumericSemanticCheck implements BaseChecker { // 场景1:对于类属性private a: number 或 private a: number = xxx, fullValueString为private开始到行尾的内容,需要替换为private a: int let match = fullValueString.match(/^([^=;]+:[^=;]+)([\s\S]*)$/); if (match !== null && match.length > 2) { - if (match[1].includes(numberCategory)) { - // 判断field是否已经有正确的类型注解 + ruleFix.range = [fullRange[0], fullRange[0] + match[1].length]; + const localString = FixUtils.getSourceWithRange(sourceFile, ruleFix.range); + if (!localString) { + logger.error('Failed to getting text of the fix range info when generating auto fix info.'); return null; } - ruleFix.range = [fullRange[0], fullRange[0] + match[1].length]; - const originalText = FixUtils.getSourceWithRange(sourceFile, ruleFix.range); - if (!originalText) { + const parts = localString.split(':'); + if (parts.length !== 2) { logger.error('Failed to getting text of the fix range info when generating auto fix info.'); return null; } - if (!originalText.includes(NumberCategory.number)) { + if (parts[1].includes(numberCategory)) { + // 判断field是否已经有正确的类型注解 + return null; + } + if (!parts[1].includes(NumberCategory.number)) { // 原码含有类型注解但是其类型中不含number,无法进行替换 return null; } - ruleFix.text = originalText.replace(NumberCategory.number, numberCategory); + ruleFix.text = `${parts[0].trimEnd()}: ${parts[1].trimStart().replace(NumberCategory.number, numberCategory)}`; return ruleFix; } // 场景2:对于private a = 123,originalText为private开始到行尾的内容,需要替换为private a: int = 123 @@ -2534,7 +2670,22 @@ export class NumericSemanticCheck implements BaseChecker { if (field) { return this.generateRuleFixForFieldDefine(sourceFile, warnInfo, numberCategory); } - return this.generateRuleFixForLocalDefine(sourceFile, warnInfo, numberCategory); + + let isOptionalField: boolean | undefined; + + if (issueStmt instanceof ArkAssignStmt) { + const rightOp = issueStmt.getRightOp(); + if (rightOp instanceof AbstractFieldRef) { + const fieldSig = rightOp.getFieldSignature(); + const declaringSig = fieldSig.getDeclaringSignature(); + if (declaringSig instanceof ClassSignature) { + const baseClass = this.scene.getClass(declaringSig); + const baseField = baseClass?.getField(fieldSig); + isOptionalField = !!baseField?.getQuestionToken(); + } + } + } + return this.generateRuleFixForLocalDefine(sourceFile, warnInfo, numberCategory, isOptionalField); } private generateIntConstantIndexRuleFix(warnInfo: WarnInfo, issueStmt: Stmt, constant: NumberConstant): RuleFix | null { @@ -2579,12 +2730,8 @@ export class NumericSemanticCheck implements BaseChecker { } } - if (value instanceof NumberConstant) { - // 对整型字面量进行自动修复,转成浮点字面量,例如1->1.0 - if (this.isNumberConstantActuallyFloat(value)) { - // 无需修复 - return null; - } + if ((value instanceof Local && value.getName().startsWith(TEMP_LOCAL_PREFIX) && value.getType() instanceof EnumValueType) || + value instanceof NumberConstant) { if (warnInfo.endLine === undefined) { // 按正常流程不应该存在此场景 logger.error('Missing end line info in warnInfo when generating auto fix info.'); @@ -2600,14 +2747,29 @@ export class NumericSemanticCheck implements BaseChecker { logger.error('Failed to getting range info of issue file when generating auto fix info.'); return null; } - - const valueStr = value.getValue(); - const ruleFixText = NumericSemanticCheck.CreateFixTextForIntLiteral(valueStr); const ruleFix = new RuleFix(); ruleFix.range = range; - ruleFix.text = ruleFixText; + + if (value instanceof NumberConstant) { + // 场景1:对整型字面量进行自动修复,转成浮点字面量,例如1->1.0 + if (this.isNumberConstantActuallyFloat(value)) { + // 无需修复 + return null; + } + const valueStr = value.getValue(); + ruleFix.text = NumericSemanticCheck.CreateFixTextForIntLiteral(valueStr);; + } else { + // 场景2:对enum.A这样的枚举类型进行自动修复成enum.A.valueOf().toDouble() + const valueStr = FixUtils.getSourceWithRange(sourceFile, range); + if (valueStr === null) { + logger.error('Failed to getting enum source code with range info.'); + return null; + } + ruleFix.text = NumericSemanticCheck.CreateFixTextForEnumValue(valueStr);; + } return ruleFix; } + // 非整型字面量 // warnInfo中对于变量声明语句的位置信息只包括变量名,不包括变量声明时的类型注解位置,此处获取变量名后到行尾的字符串信息,替换‘: number’ 或增加 ‘: int’ if (issueReason === IssueReason.OnlyUsedAsIntLong) { @@ -2619,10 +2781,13 @@ export class NumericSemanticCheck implements BaseChecker { if (!NumericSemanticCheck.IsNotDecimalNumber(valueStr)) { return valueStr + '.0'; } - return valueStr + '.toDouble()'; } + private static CreateFixTextForEnumValue(valueStr: string): string { + return valueStr + '.valueOf().toDouble()'; + } + private static IsNotDecimalNumber(value: string): boolean { const loweredValue = value.toLowerCase(); return loweredValue.startsWith('0b') || loweredValue.startsWith('0x') || loweredValue.startsWith('0o') || loweredValue.includes('e'); diff --git a/ets2panda/linter/homecheck/src/utils/common/SDKUtils.ts b/ets2panda/linter/homecheck/src/utils/common/SDKUtils.ts index f094b9e132..be2a5afa52 100644 --- a/ets2panda/linter/homecheck/src/utils/common/SDKUtils.ts +++ b/ets2panda/linter/homecheck/src/utils/common/SDKUtils.ts @@ -63,6 +63,11 @@ export class SdkUtils { return projectName === this.OhosSdkName || projectName === this.HmsSdkName; } + static isClassFromSdk(classSignature: ClassSignature): boolean { + const projectName = classSignature.getDeclaringFileSignature().getProjectName(); + return projectName === this.OhosSdkName || projectName === this.HmsSdkName; + } + static getSdkMatchedSignature(ets1SDK: ArkMethod, args: Value[]): MethodSignature | null { const declareSigs = ets1SDK.getDeclareSignatures(); if (declareSigs === null) { diff --git a/ets2panda/linter/rule-config.json b/ets2panda/linter/rule-config.json index 0c613f3a2f..0174894363 100644 --- a/ets2panda/linter/rule-config.json +++ b/ets2panda/linter/rule-config.json @@ -1,33 +1,22 @@ { "ArkTS": [ - "arkts-identifiers-as-prop-names", "arkts-no-ctor-prop-decls", "arkts-no-structural-typing", "arkts-no-inferred-generic-params", "arkts-no-regexp-literals", "arkts-no-props-by-index", "arkts-no-enum-mixed-types", - "arkts-no-definite-assignment", "arkts-no-globalthis", "arkts-no-func-props", "arkts-no-func-bind", - "arkts-no-function-return-this", "arkts-limited-stdlib", - "arkts-no-class-add-super-prop-with-readonly", - "arkts-concurrent-deprecated-apis", "arkts-no-classes-as-obj", "arkts-obj-literal-props", - "arkts-no-template-string-type", - "arkts-obj-literal-key-type", "arkts-optional-methods", - "arkts-use-long-for-large-numeric-literal", "arkts-numeric-semantic", - "arkts-limited-tuple-index-type", "arkts-incompatible-function-types", "arkts-limited-void-type", - "arkts-distinct-infinity-bitwise-inversion", "arkts-no-void-operator", - "arkts-no-local-class", "arkts-no-ts-overload", "arkts-limited-literal-types", "arkts-no-exponent-op", @@ -49,8 +38,6 @@ "arkts-no-tuples-arrays", "arkts-class-static-initialization", "arkts-invalid-identifier", - "arkts-no-import-json-file", - "arkts-no-import-namespace-with-star-as-var", "arkts-no-extends-expression", "arkts-no-ts-like-function-call", "arkts-method-inherit-rule", @@ -59,30 +46,16 @@ "arkts-limited-stdlib-no-setCloneList", "arkts-limited-stdlib-no-setTransferList", "arkts-builtin-object-getOwnPropertyNames", - "arkts-no-class-omit-interface-optional-prop", - "arkts-distinct-abstract-method-default-return-type", - "arkts-class-no-signature-distinct-with-object-public-api", - "arkts-union-assignment-with-obj-literal-ambiguity", "arkts-no-sparse-array", "arkts-no-enum-prop-as-type", "arkts-no-ts-like-smart-type", - "arkts-distinct-unsigned-right-shift-negative-number", "arkts-array-type-immutable", "arkts-primitive-type-normalization", "arkts-no-ts-like-catch-type", "arkts-numeric-bigint-compare", "arkts-only-support-decimal-bigint-literal", "arkts-unsupport-operator", - "arkts-no-duplicate-function-name", - "arkts-require-func-arg-type", - "arkts-subclass-must-call-super-constructor-with-args", - "arkts-no-esobject-support", - "arkts-not-support-tuple-generic-validation", - "arkts-no-optional-tuple-type", - "arkts-no-large-numeric-literal", - "arkts-no-instanceof-func", - "arkts-no-unfixed-len-tuple", - "arkts-no-super-call-in-static-context" + "arkts-no-duplicate-function-name" ], "interop": [ "arkts-interop-js2s-inherit-js-class", @@ -115,8 +88,7 @@ "arkts-interop-js2s-call-js-method", "arkts-interop-js2s-instanceof-js-type", "arkts-interop-js2s-self-addtion-reduction", - "arkts-promise-with-void-type-need-undefined-as-resolve-arg", - "arkts-class-same-type-prop-with-super" + "arkts-promise-with-void-type-need-undefined-as-resolve-arg" ], "ArkUI": [ "arkui-no-!!-bidirectional-data-binding", @@ -146,24 +118,16 @@ "arkui-buildernode-generic-no-tuple", "arkui-buildernode-update-no-literal", "arkui-buildernode-no-nestingbuildersupported", - "arkui-sdk-common-deprecated-api", - "arkui-sdk-common-whitelist-api", - "arkui-sdk-common-behaviorchange-api", "arkui-persistent-prop-serialization", "arkui-persistent-props-serialization", - "arkui-persistencev2-connect-serialization" + "arkui-persistencev2-connect-serialization", + "arkui-entry-invalid-params" ], "builtin": [ "arkts-builtin-thisArgs", "arkts-builtin-symbol-iterator", "arkts-builtin-no-property-descriptor", - "arkts-builtin-cotr", - "arkts-builtin-new-cotr", - "arkts-builtin-uninitialized-element", - "arkts-builtin-final-class", - "arkts-builtin-narrow-types", - "arkts-builtin-disable-api", - "arkts-builtin-iterator-result-value" + "arkts-builtin-cotr" ], "OHMURL": [ "arkts-require-fullpath-name" diff --git a/ets2panda/linter/src/lib/CookBookMsg.ts b/ets2panda/linter/src/lib/CookBookMsg.ts index cfd5fc0a1b..0c004df48d 100644 --- a/ets2panda/linter/src/lib/CookBookMsg.ts +++ b/ets2panda/linter/src/lib/CookBookMsg.ts @@ -424,6 +424,8 @@ cookBookTag[392] = 'The class of the "defaultValue" parameter in the literal passed to the "persistProps" method must be a primitive type or Date type, or implement the "toJson" and "fromJson" methods (arkui-persistent-props-serialization)'; cookBookTag[393] = 'When calling the "globalConnect" and "connect" methods, the parameter list of the methods needs to include "toJson" and "fromJson" (arkui-persistencev2-connect-serialization)'; +cookBookTag[394] = + 'The "@ComponentV2" decorator cannot be used together with the "@Entry" decorator that has "storage" or "useSharedStorage" parameters (arkui-entry-invalid-params)' cookBookTag[399] = 'ArkUI deprecated api check (arkui-no-deprecated-api)'; cookBookTag[400] = 'ArkUI sdk common deprecated api check (arkui-sdk-common-deprecated-api)'; cookBookTag[401] = 'ArkUI sdk common whitelist api check (arkui-sdk-common-whitelist-api)'; diff --git a/ets2panda/linter/src/lib/FaultAttrs.ts b/ets2panda/linter/src/lib/FaultAttrs.ts index 7de7999b99..383491da21 100644 --- a/ets2panda/linter/src/lib/FaultAttrs.ts +++ b/ets2panda/linter/src/lib/FaultAttrs.ts @@ -296,6 +296,7 @@ faultsAttrs[FaultID.PromiseVoidNeedResolveArg] = new FaultAttributes(382); faultsAttrs[FaultID.PersistentPropNeedImplementMethod] = new FaultAttributes(391); faultsAttrs[FaultID.PersistentPropsNeedImplementMethod] = new FaultAttributes(392); faultsAttrs[FaultID.PersistenceV2ConnectNeedAddParam] = new FaultAttributes(393); +faultsAttrs[FaultID.EntryHasInvalidParams] = new FaultAttributes(394); faultsAttrs[FaultID.NoDeprecatedApi] = new FaultAttributes(399); faultsAttrs[FaultID.SdkCommonApiDeprecated] = new FaultAttributes(400); faultsAttrs[FaultID.SdkCommonApiWhiteList] = new FaultAttributes(401); diff --git a/ets2panda/linter/src/lib/FaultDesc.ts b/ets2panda/linter/src/lib/FaultDesc.ts index 5b39e917f8..59ab653299 100644 --- a/ets2panda/linter/src/lib/FaultDesc.ts +++ b/ets2panda/linter/src/lib/FaultDesc.ts @@ -298,4 +298,7 @@ faultDesc[FaultID.BuiltinDisableApi] = 'Disable Api'; faultDesc[FaultID.BuiltinIteratorResultValue] = 'IteratorResult.value is not supported'; faultDesc[FaultID.OptionalTupleType] = 'No optional tuple type'; faultDesc[FaultID.LargeNumericLiteral] = 'Numeric literal exceeds allowed range'; +faultDesc[FaultID.InstanceOfFunction] = 'instanceof with function type'; faultDesc[FaultID.unfixedTuple] = 'No unfixed tuple'; +faultDesc[FaultID.EntryHasInvalidParams] = 'Entry has invalid parameters'; +faultDesc[FaultID.SuperInStaticContext] = '"super" in static context'; diff --git a/ets2panda/linter/src/lib/Problems.ts b/ets2panda/linter/src/lib/Problems.ts index fd32b020c0..ddf7244972 100644 --- a/ets2panda/linter/src/lib/Problems.ts +++ b/ets2panda/linter/src/lib/Problems.ts @@ -300,6 +300,7 @@ export enum FaultID { InstanceOfFunction, unfixedTuple, SuperInStaticContext, + EntryHasInvalidParams, // this should always be last enum LAST_ID } diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index cb56e385e1..9816096043 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -143,7 +143,10 @@ import { GLOBAL_CONNECT_FUNC_NAME, CONNECT_FUNC_NAME, serializationTypeFlags, - serializationTypeName + serializationTypeName, + COMPONENTV2_DECORATOR_NAME, + ENTRY_STORAGE, + ENTRY_USE_SHARED_STORAGE } from './utils/consts/ArkuiConstants'; import { arkuiImportList } from './utils/consts/ArkuiImportList'; import type { IdentifierAndArguments, ForbidenAPICheckResult } from './utils/consts/InteropAPI'; @@ -226,6 +229,7 @@ import { ES_OBJECT } from './utils/consts/ESObject'; import { cookBookMsg } from './CookBookMsg'; import { getCommonApiInfoMap } from './utils/functions/CommonApiInfo'; import { arkuiDecoratorSet } from './utils/consts/ArkuiDecorator'; +import { ABILITY_LIFECYCLE, ABILITY_LIFECYCLE_CALLBACK } from './utils/consts/LifecycleMonitor'; export class TypeScriptLinter extends BaseTypeScriptLinter { supportedStdCallApiChecker: SupportedStdCallApiChecker; @@ -956,6 +960,7 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } this.handleStructDeclarationForLayout(node); this.handleInvalidIdentifier(node); + this.handleStructDeclForEntryAndComponentV2(node); } private handleParameter(node: ts.Node): void { @@ -8567,6 +8572,20 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return; } + const structDecl = node.parent; + if (!ts.isStructDeclaration(structDecl)) { + return; + } + + const decorators = ts.getDecorators(structDecl); + const hasComponentV2 = decorators?.some((decorator) => { + return decorator.expression.getText() === COMPONENTV2_DECORATOR_NAME; + }); + + if (hasComponentV2) { + return; + } + if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression)) { if (node.expression.expression.escapedText !== ENTRY_DECORATOR_NAME || node.expression.arguments.length !== 1) { return; @@ -10404,6 +10423,7 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { if (!ts.isClassDeclaration(cls) || !cls.heritageClauses) { return false; } + return cls.heritageClauses.some((h) => { return ( h.token === ts.SyntaxKind.ExtendsKeyword && @@ -10419,16 +10439,12 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { * and matches the lifecycle method (onDestroy vs onDisconnect). */ private isSupportedAbilityBase(methodName: string, baseExprNode: ts.Expression): boolean { - const sym = this.tsTypeChecker.getSymbolAtLocation(baseExprNode); + const sym = this.getExtendedAsyncLifecycleClass(baseExprNode); if (!sym) { return false; } const baseName = sym.getName(); - if (!ASYNC_LIFECYCLE_SDK_LIST.has(baseName)) { - return false; - } - if (methodName === ON_DISCONNECT && baseName !== SERVICE_EXTENSION_ABILITY) { return false; } @@ -10448,6 +10464,40 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return moduleName === ABILITY_KIT || srcFile.endsWith(`${baseName}.${EXTNAME_D_TS}`); } + /* + * check if this extended class is among the classes we care about + * if not check if that extended class is extending a class in async lifecycle list + * if not again + */ + private getExtendedAsyncLifecycleClass(extendedClass: ts.Expression): undefined | ts.Symbol { + if (!ts.isIdentifier(extendedClass)) { + return undefined; + } + const sym = this.tsTypeChecker.getSymbolAtLocation(extendedClass); + if (!sym) { + return undefined; + } + + if (ASYNC_LIFECYCLE_SDK_LIST.has(sym.getName())) { + return sym; + } + + const decls = sym.getDeclarations(); + if (!decls) { + return undefined; + } + + const decl = decls[0]; + if (!ts.isClassDeclaration(decl) || !decl.heritageClauses) { + return undefined; + } + + const extendedExpression = decl.heritageClauses[0]; + const extendedType = extendedExpression.types[0]; + + return this.getExtendedAsyncLifecycleClass(extendedType.expression); + } + /** * Rule sdk-void-lifecycle-return: * Flags onDestroy/onDisconnect methods in Ability subclasses @@ -10735,35 +10785,49 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return; } - // Guard: exactly two arguments + const decl = this.getDeclarationOfLifecycleEventCallback(callExpr); + + if ( + !decl?.type || + !ts.isTypeReferenceNode(decl.type) || + decl.type.typeName.getText() !== ABILITY_LIFECYCLE_CALLBACK + ) { + return; + } + + // Report the legacy callback usage + this.incrementCounters(callExpr, FaultID.SdkAbilityLifecycleMonitor); + } + + private getDeclarationOfLifecycleEventCallback(callExpr: ts.CallExpression): undefined | ts.VariableDeclaration { const args = callExpr.arguments; if (args.length !== 2) { - return; + return undefined; } // Guard: first arg must be string literal "abilityLifecycle" const eventArg = args[0]; - if (!ts.isStringLiteral(eventArg) || eventArg.text !== 'abilityLifecycle') { - return; + if (!ts.isStringLiteral(eventArg) || eventArg.text !== ABILITY_LIFECYCLE) { + return undefined; } // Guard: second arg must be a variable declared as AbilityLifecycleCallback - const cbArg = args[1]; - if (!ts.isIdentifier(cbArg)) { - return; + let cbArgIdent = args[1]; + while (!ts.isIdentifier(cbArgIdent)) { + if (!ts.isNewExpression(cbArgIdent)) { + return undefined; + } + cbArgIdent = cbArgIdent.expression; } - const varSym = this.tsUtils.trueSymbolAtLocation(cbArg); - const decl = varSym?.declarations?.find(ts.isVariableDeclaration); - if ( - !decl?.type || - !ts.isTypeReferenceNode(decl.type) || - decl.type.typeName.getText() !== 'AbilityLifecycleCallback' - ) { - return; + + if (cbArgIdent.text === ABILITY_LIFECYCLE_CALLBACK) { + this.incrementCounters(callExpr, FaultID.SdkAbilityLifecycleMonitor); + return undefined; } - // Report the legacy callback usage - this.incrementCounters(callExpr, FaultID.SdkAbilityLifecycleMonitor); + const varSym = this.tsUtils.trueSymbolAtLocation(cbArgIdent); + + return varSym?.declarations?.find(ts.isVariableDeclaration); } private isOnMethod(node: ts.CallExpression): boolean { @@ -15642,4 +15706,58 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { this.incrementCounters(node, FaultID.SuperInStaticContext); } } + + private handleStructDeclForEntryAndComponentV2(node: ts.StructDeclaration): void { + if (!this.options.arkts2) { + return; + } + + const decorators = ts.getDecorators(node); + if (!decorators) { + return; + } + + let entryDecorator: ts.Decorator | undefined; + const hasEntry = decorators.some((decorator) => { + if (TsUtils.getDecoratorName(decorator) === ENTRY_DECORATOR_NAME) { + entryDecorator = decorator; + return true; + } + return false; + }); + const hasComponentV2 = decorators.some((decorator) => { + return TsUtils.getDecoratorName(decorator) === COMPONENTV2_DECORATOR_NAME; + }); + + if (!hasEntry || !hasComponentV2 || !entryDecorator) { + return; + } + + const entryDecoratorHasInvalidParams = TypeScriptLinter.checkEntryDecoratorHasInvalidParams(entryDecorator); + if (!entryDecoratorHasInvalidParams) { + return; + } + + const autofix = this.autofixer?.fixEntryAndComponentV2(entryDecorator); + this.incrementCounters(entryDecorator, FaultID.EntryHasInvalidParams, autofix); + } + + private static checkEntryDecoratorHasInvalidParams(entryDecorator: ts.Decorator): boolean { + const callExpr = entryDecorator.expression; + if (!ts.isCallExpression(callExpr)) { + return false; + } + + const arg = callExpr.arguments?.[0]; + if (!ts.isObjectLiteralExpression(arg)) { + return false; + } + + const hasInvalidParams = arg.properties.some((property) => { + const name = property.name?.getText(); + return name === ENTRY_STORAGE || name === ENTRY_USE_SHARED_STORAGE; + }); + + return hasInvalidParams; + } } diff --git a/ets2panda/linter/src/lib/autofixes/Autofixer.ts b/ets2panda/linter/src/lib/autofixes/Autofixer.ts index 1972b3e1e1..374e9466d4 100644 --- a/ets2panda/linter/src/lib/autofixes/Autofixer.ts +++ b/ets2panda/linter/src/lib/autofixes/Autofixer.ts @@ -34,7 +34,8 @@ import { VALUE_IDENTIFIER, INDENT_STEP, ENTRY_DECORATOR_NAME, - ENTRY_STORAGE_PROPERITY, + ENTRY_STORAGE, + ENTRY_USE_SHARED_STORAGE, LOCAL_STORAGE_TYPE_NAME, GET_LOCAL_STORAGE_FUNC_NAME, PROVIDE_DECORATOR_NAME, @@ -111,6 +112,13 @@ interface CreateClassPropertyForObjectLiteralParams { ctorInitProps: ts.PropertyAssignment[]; } +interface ConstructorBodyFixInfo { + pos: number; + needLeadingNewLine: boolean; + needTrailingNewLine: boolean; + indentLastLine: boolean; +} + export interface Autofix { replacementText: string; start: number; @@ -1591,75 +1599,35 @@ export class Autofixer { ctorDecl: ts.ConstructorDeclaration, paramTypes: ts.TypeNode[] | undefined ): Autofix[] | undefined { - if (paramTypes === undefined) { + if (paramTypes === undefined || !ctorDecl.body) { return undefined; } const fieldInitStmts: ts.Statement[] = []; const newFieldPos = ctorDecl.getStart(); const autofixes: Autofix[] = [{ start: newFieldPos, end: newFieldPos, replacementText: '' }]; + const indentStartPos = this.sourceFile.getLineAndCharacterOfPosition(ctorDecl.getStart()).character; for (let i = 0; i < ctorDecl.parameters.length; i++) { - this.fixCtorParameterPropertiesProcessParam(ctorDecl.parameters[i], paramTypes[i], fieldInitStmts, autofixes); - } - - // Note: Bodyless ctors can't have parameter properties. - if (ctorDecl.body) { - const beforeFieldStmts: ts.Statement[] = []; - const afterFieldStmts: ts.Statement[] = []; - const hasSuperExpressionStatement: boolean = this.hasSuperExpression( - ctorDecl.body, - beforeFieldStmts, - afterFieldStmts + this.fixCtorParameterPropertiesProcessParam( + ctorDecl.parameters[i], + paramTypes[i], + fieldInitStmts, + autofixes, + indentStartPos ); - let finalStmts: ts.Statement[] = []; - if (hasSuperExpressionStatement) { - finalStmts = beforeFieldStmts.concat(fieldInitStmts).concat(afterFieldStmts); - } else { - finalStmts = fieldInitStmts.concat(ctorDecl.body.statements); - } - const newBody = ts.factory.createBlock(finalStmts, true); - const newBodyText = this.printer.printNode(ts.EmitHint.Unspecified, newBody, ctorDecl.getSourceFile()); - autofixes.push({ start: ctorDecl.body.getStart(), end: ctorDecl.body.getEnd(), replacementText: newBodyText }); } + autofixes.push(this.fixCtorParameterPropertiesProcessBody(ctorDecl.body, fieldInitStmts, indentStartPos)); return autofixes; } - private hasSuperExpression( - body: ts.Block, - beforeFieldStmts: ts.Statement[], - afterFieldStmts: ts.Statement[] - ): boolean { - void this; - let hasSuperExpressionStatement = false; - ts.forEachChild(body, (node) => { - if (this.isSuperCallStmt(node as ts.Statement)) { - hasSuperExpressionStatement = true; - beforeFieldStmts.push(node as ts.Statement); - } else if (hasSuperExpressionStatement) { - afterFieldStmts.push(node as ts.Statement); - } else { - beforeFieldStmts.push(node as ts.Statement); - } - }); - return hasSuperExpressionStatement; - } - - private isSuperCallStmt(node: ts.Statement): boolean { - void this; - if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression)) { - const expr = node.expression.expression; - return expr.kind === ts.SyntaxKind.SuperKeyword; - } - return false; - } - private fixCtorParameterPropertiesProcessParam( param: ts.ParameterDeclaration, paramType: ts.TypeNode, fieldInitStmts: ts.Statement[], - autofixes: Autofix[] + autofixes: Autofix[], + indentStartPos: number ): void { // Parameter property can not be a destructuring parameter. if (!ts.isIdentifier(param.name)) { @@ -1672,7 +1640,6 @@ export class Autofixer { const paramModifiers = modifiers?.filter((x) => { return x.kind !== ts.SyntaxKind.OverrideKeyword; }); - const newFieldNode = ts.factory.createPropertyDeclaration( paramModifiers, propIdent, @@ -1682,7 +1649,7 @@ export class Autofixer { ); const newFieldText = this.printer.printNode(ts.EmitHint.Unspecified, newFieldNode, param.getSourceFile()) + this.getNewLine(); - autofixes[0].replacementText += newFieldText; + autofixes[0].replacementText += this.adjustIndentation(newFieldText, indentStartPos); const newParamDecl = ts.factory.createParameterDeclaration( undefined, @@ -1706,6 +1673,74 @@ export class Autofixer { } } + private fixCtorParameterPropertiesProcessBody( + ctorBody: ts.Block, + fieldInitStmts: ts.Statement[], + indentStartPos: number + ): Autofix { + let newStmtListText = this.printer.printList( + ts.ListFormat.MultiLine | ts.ListFormat.NoTrailingNewLine, + ts.factory.createNodeArray(fieldInitStmts), + ctorBody.getSourceFile() + ); + + const ctorBodyFixInfo = this.getParamPropertiesCtorBodyFixInfo(ctorBody); + + if (ctorBodyFixInfo.needLeadingNewLine) { + newStmtListText = this.getNewLine() + newStmtListText; + } + if (ctorBodyFixInfo.needTrailingNewLine) { + newStmtListText += this.getNewLine(); + } + newStmtListText = this.adjustIndentation(newStmtListText, indentStartPos, ctorBodyFixInfo.indentLastLine); + return { start: ctorBodyFixInfo.pos, end: ctorBodyFixInfo.pos, replacementText: newStmtListText }; + } + + getParamPropertiesCtorBodyFixInfo(ctorBody: ts.Block): ConstructorBodyFixInfo { + let pos: number; + let needLeadingNewLine = false; + let needTrailingNewLine = false; + let indentLastLine = false; + const statements = ctorBody.statements; + if (statements.length > 0) { + const superCallStmt = statements.find(TsUtils.isSuperCallStmt); + if (superCallStmt) { + // If there's a 'super()' call, then new statements are inserted after it. + const superCallIndex = statements.indexOf(superCallStmt); + if (superCallIndex === statements.length - 1) { + const superCallEndPosition = superCallStmt.getEnd(); + pos = TsUtils.getTrailingCommentRangesEnd(this.sourceFile, superCallEndPosition) ?? superCallEndPosition; + needLeadingNewLine = true; + } else { + pos = statements[superCallIndex + 1].getStart(); + needTrailingNewLine = true; + } + } else { + // If there's no 'super()' call, new statements are inserted at the beginning of ctor body. + needTrailingNewLine = true; + pos = statements[0].getStart(); + } + indentLastLine = true; + } else { + // Ctor body is empty, insert new statements after inner comments if any. + needLeadingNewLine = true; + const bracesOnSameLine = + this.sourceFile.getLineAndCharacterOfPosition(ctorBody.getStart()).line === + this.sourceFile.getLineAndCharacterOfPosition(ctorBody.getEnd()).line; + + if (bracesOnSameLine) { + pos = ctorBody.getEnd() - 1; /* The position of closing brace */ + needTrailingNewLine = true; + } else { + const leadingRangesPos = ctorBody.getStart() + 1; /* End position of opening brace */ + pos = TsUtils.getLeadingCommentRangesEnd(this.sourceFile, leadingRangesPos) ?? leadingRangesPos; + indentLastLine = true; + } + } + + return { pos, needLeadingNewLine, needTrailingNewLine, indentLastLine }; + } + fixPrivateIdentifier(node: ts.PrivateIdentifier): Autofix[] | undefined { const classMember = this.typeChecker.getSymbolAtLocation(node); if (!classMember || (classMember.getFlags() & ts.SymbolFlags.ClassMember) === 0 || !classMember.valueDeclaration) { @@ -2937,23 +2972,22 @@ export class Autofixer { return undefined; } - const parentNode = entryDecorator.parent; const arg = args[0]; - let getLocalStorageStatement: ts.VariableStatement | undefined; + let getLocalStorageStmt: ts.VariableStatement | undefined; if (ts.isIdentifier(arg) || ts.isNewExpression(arg) || ts.isCallExpression(arg)) { - getLocalStorageStatement = Autofixer.createGetLocalStorageLambdaStatement(arg); + getLocalStorageStmt = Autofixer.createGetLocalStorageLambdaStatement(arg); } else if (ts.isObjectLiteralExpression(arg)) { - getLocalStorageStatement = Autofixer.processEntryAnnotationObjectLiteralExpression(arg); + getLocalStorageStmt = Autofixer.processEntryAnnotationObjectLiteralExpression(arg); } - if (getLocalStorageStatement !== undefined) { - let text = this.printer.printNode(ts.EmitHint.Unspecified, getLocalStorageStatement, parentNode.getSourceFile()); + if (getLocalStorageStmt !== undefined) { + let text = this.printer.printNode(ts.EmitHint.Unspecified, getLocalStorageStmt, entryDecorator.getSourceFile()); const fixedEntryDecorator = Autofixer.createFixedEntryDecorator(); const fixedEntryDecoratorText = this.printer.printNode( ts.EmitHint.Unspecified, fixedEntryDecorator, - parentNode.getSourceFile() + entryDecorator.getSourceFile() ); text = text + this.getNewLine() + fixedEntryDecoratorText; return [{ start: entryDecorator.getStart(), end: entryDecorator.getEnd(), replacementText: text }]; @@ -2963,7 +2997,7 @@ export class Autofixer { private static createFixedEntryDecorator(): ts.Decorator { const storageProperty = ts.factory.createPropertyAssignment( - ts.factory.createIdentifier(ENTRY_STORAGE_PROPERITY), + ts.factory.createIdentifier(ENTRY_STORAGE), ts.factory.createStringLiteral(GET_LOCAL_STORAGE_FUNC_NAME) ); const objectLiteralExpr = ts.factory.createObjectLiteralExpression([storageProperty], false); @@ -2985,7 +3019,7 @@ export class Autofixer { return undefined; } if (ts.isIdentifier(objectProperty.name)) { - if (objectProperty.name.escapedText !== ENTRY_STORAGE_PROPERITY) { + if (objectProperty.name.escapedText !== ENTRY_STORAGE) { return undefined; } const properityInitializer = objectProperty.initializer; @@ -3861,7 +3895,7 @@ export class Autofixer { return autofix; } - private adjustIndentation(text: string, startPos: number): string { + private adjustIndentation(text: string, startPos: number, indentLastLine = false): string { const lines = text.split(this.getNewLine()); if (lines.length <= 1) { return text; @@ -3879,7 +3913,7 @@ export class Autofixer { return line; }); - const lastLine = ' '.repeat(startPos) + lines[lines.length - 1]; + const lastLine = ' '.repeat(indentLastLine ? indentBase : startPos) + lines[lines.length - 1]; return [firstLine, ...middleLines, lastLine].join(this.getNewLine()); } @@ -5336,9 +5370,10 @@ export class Autofixer { const methodName = identifierReplacements.get(identifierText); if (methodName) { const accessExpr = Autofixer.createUIContextAccess(methodName); - const newExpression = identifierText === GET_CONTEXT - ? ts.factory.createCallChain(accessExpr, undefined, undefined, []) - : accessExpr; + const newExpression = + identifierText === GET_CONTEXT ? + ts.factory.createCallChain(accessExpr, undefined, undefined, []) : + accessExpr; const start = identifierText === GET_CONTEXT ? callExpr.getStart() : callExpr.expression.getStart(); const end = identifierText === GET_CONTEXT ? callExpr.getEnd() : callExpr.expression.getEnd(); const newText = this.printer.printNode(ts.EmitHint.Unspecified, newExpression, callExpr.getSourceFile()); @@ -5360,7 +5395,10 @@ export class Autofixer { ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier(UI_CONTEXT), ts.factory.createIdentifier(GET_FOCUSED_UI_CONTEXT) - ), undefined, []), + ), + undefined, + [] + ), ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), ts.factory.createIdentifier(methodName) ); @@ -5439,4 +5477,25 @@ export class Autofixer { } ]; } + + fixEntryAndComponentV2(entryDecorator: ts.Decorator): Autofix[] | undefined { + const callExpr = entryDecorator.expression; + if (!ts.isCallExpression(callExpr)) { + return undefined; + } + + const arg = callExpr.arguments?.[0]; + if (!ts.isObjectLiteralExpression(arg)) { + return undefined; + } + + const newProperties = arg.properties.filter((property) => { + const name = property.name?.getText(); + return name !== ENTRY_STORAGE && name !== ENTRY_USE_SHARED_STORAGE; + }); + + const newArg = ts.factory.createObjectLiteralExpression(newProperties, false); + const text = this.printer.printNode(ts.EmitHint.Unspecified, newArg, entryDecorator.getSourceFile()); + return [{ start: arg.getStart(), end: arg.getEnd(), replacementText: text }]; + } } diff --git a/ets2panda/linter/src/lib/utils/TsUtils.ts b/ets2panda/linter/src/lib/utils/TsUtils.ts index 3aa1893629..fc3f35fa26 100644 --- a/ets2panda/linter/src/lib/utils/TsUtils.ts +++ b/ets2panda/linter/src/lib/utils/TsUtils.ts @@ -3951,4 +3951,22 @@ export class TsUtils { }) !== -1 ); } + + static getLeadingCommentRangesEnd(srcFile: ts.SourceFile, pos: number): number | undefined { + const leadingCommentRanges = ts.getLeadingCommentRanges(srcFile.text, pos); + return leadingCommentRanges ? leadingCommentRanges[leadingCommentRanges.length - 1].end : undefined; + } + + static getTrailingCommentRangesEnd(srcFile: ts.SourceFile, pos: number): number | undefined { + const trailingCommentRanges = ts.getTrailingCommentRanges(srcFile.text, pos); + return trailingCommentRanges ? trailingCommentRanges[trailingCommentRanges.length - 1].end : undefined; + } + + static isSuperCallStmt(node: ts.Statement): boolean { + if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression)) { + const expr = node.expression.expression; + return expr.kind === ts.SyntaxKind.SuperKeyword; + } + return false; + } } diff --git a/ets2panda/linter/src/lib/utils/consts/ArkuiConstants.ts b/ets2panda/linter/src/lib/utils/consts/ArkuiConstants.ts index 7165daa561..b16394a134 100644 --- a/ets2panda/linter/src/lib/utils/consts/ArkuiConstants.ts +++ b/ets2panda/linter/src/lib/utils/consts/ArkuiConstants.ts @@ -160,10 +160,13 @@ export const serializationTypeName: Set = new Set([ export const customLayoutFunctionName: Set = new Set(['onMeasureSize', 'onPlaceChildren']); export const ENTRY_DECORATOR_NAME = 'Entry'; -export const ENTRY_STORAGE_PROPERITY = 'storage'; +export const ENTRY_STORAGE = 'storage'; +export const ENTRY_USE_SHARED_STORAGE = 'useSharedStorage'; export const LOCAL_STORAGE_TYPE_NAME = 'LocalStorage'; export const GET_LOCAL_STORAGE_FUNC_NAME = '__get_local_storage__'; +export const COMPONENTV2_DECORATOR_NAME = 'ComponentV2'; + export const PROVIDE_DECORATOR_NAME = 'Provide'; export const PROVIDE_ALIAS_PROPERTY_NAME = 'alias'; export const PROVIDE_ALLOW_OVERRIDE_PROPERTY_NAME = 'allowOverride'; diff --git a/ets2panda/linter/src/lib/utils/consts/LifecycleMonitor.ts b/ets2panda/linter/src/lib/utils/consts/LifecycleMonitor.ts new file mode 100644 index 0000000000..a5b8585938 --- /dev/null +++ b/ets2panda/linter/src/lib/utils/consts/LifecycleMonitor.ts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const ABILITY_LIFECYCLE_CALLBACK: string = 'AbilityLifecycleCallback'; +export const ABILITY_LIFECYCLE: string = 'abilityLifecycle'; diff --git a/ets2panda/linter/test/main/entry_componentv2_1.ets b/ets2panda/linter/test/main/entry_componentv2_1.ets new file mode 100644 index 0000000000..ac140a9514 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_1.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@Entry({ storage: new LocalStorage() }) +@ComponentV2 +struct MyPage1 { + build() { + Text("Hello World") + } +} + +@Entry({ useSharedStorage: true }) +@ComponentV2 +struct MyPage2 { + build() { + Text("Hello World") + } +} + +@Entry({ storage: new LocalStorage(), useSharedStorage: true }) +@ComponentV2 +struct MyPage3 { + build() { + Text("Hello World") + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_1.ets.args.json b/ets2panda/linter/test/main/entry_componentv2_1.ets.args.json new file mode 100644 index 0000000000..ee0734c0fc --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_1.ets.args.json @@ -0,0 +1,21 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "mode": { + "arkts2": "", + "autofix": "--arkts-2", + "migrate": "--arkts-2" + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_1.ets.arkts2.json b/ets2panda/linter/test/main/entry_componentv2_1.ets.arkts2.json new file mode 100644 index 0000000000..6e46e3a973 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_1.ets.arkts2.json @@ -0,0 +1,178 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [ + { + "line": 16, + "column": 1, + "endLine": 16, + "endColumn": 40, + "problem": "EntryHasInvalidParams", + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 23, + "endLine": 16, + "endColumn": 35, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 35, + "problem": "EntryHasInvalidParams", + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 1, + "endLine": 32, + "endColumn": 64, + "problem": "EntryHasInvalidParams", + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 23, + "endLine": 32, + "endColumn": 35, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 2, + "endLine": 16, + "endColumn": 7, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 23, + "endLine": 16, + "endColumn": 35, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 17, + "column": 2, + "endLine": 17, + "endColumn": 13, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 20, + "column": 5, + "endLine": 20, + "endColumn": 9, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 2, + "endLine": 24, + "endColumn": 7, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 25, + "column": 2, + "endLine": 25, + "endColumn": 13, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 28, + "column": 5, + "endLine": 28, + "endColumn": 9, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 2, + "endLine": 32, + "endColumn": 7, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 23, + "endLine": 32, + "endColumn": 35, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 33, + "column": 2, + "endLine": 33, + "endColumn": 13, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_1.ets.autofix.json b/ets2panda/linter/test/main/entry_componentv2_1.ets.autofix.json new file mode 100644 index 0000000000..bb47a5be22 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_1.ets.autofix.json @@ -0,0 +1,332 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [ + { + "line": 16, + "column": 1, + "endLine": 16, + "endColumn": 40, + "problem": "EntryHasInvalidParams", + "autofix": [ + { + "start": 612, + "end": 643, + "replacementText": "{}", + "line": 16, + "column": 1, + "endLine": 16, + "endColumn": 40 + } + ], + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 23, + "endLine": 16, + "endColumn": 35, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 35, + "problem": "EntryHasInvalidParams", + "autofix": [ + { + "start": 725, + "end": 751, + "replacementText": "{}", + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 35 + } + ], + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 1, + "endLine": 32, + "endColumn": 64, + "problem": "EntryHasInvalidParams", + "autofix": [ + { + "start": 833, + "end": 888, + "replacementText": "{}", + "line": 32, + "column": 1, + "endLine": 32, + "endColumn": 64 + } + ], + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 23, + "endLine": 32, + "endColumn": 35, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 2, + "endLine": 16, + "endColumn": 7, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 23, + "endLine": 16, + "endColumn": 35, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 17, + "column": 2, + "endLine": 17, + "endColumn": 13, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 20, + "column": 5, + "endLine": 20, + "endColumn": 9, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 2, + "endLine": 24, + "endColumn": 7, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 25, + "column": 2, + "endLine": 25, + "endColumn": 13, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 28, + "column": 5, + "endLine": 28, + "endColumn": 9, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 2, + "endLine": 32, + "endColumn": 7, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 23, + "endLine": 32, + "endColumn": 35, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 33, + "column": 2, + "endLine": 33, + "endColumn": 13, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_1.ets.json b/ets2panda/linter/test/main/entry_componentv2_1.ets.json new file mode 100644 index 0000000000..ca88f857e9 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_1.ets.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.ets b/ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.ets new file mode 100644 index 0000000000..3f08b0c506 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.ets @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Entry, + LocalStorage, + ComponentV2, + Text, +} from '@kit.ArkUI'; + +@Entry({}) +@ComponentV2 +struct MyPage1 { + build() { + Text("Hello World") + } +} + +@Entry({}) +@ComponentV2 +struct MyPage2 { + build() { + Text("Hello World") + } +} + +@Entry({}) +@ComponentV2 +struct MyPage3 { + build() { + Text("Hello World") + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.json b/ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.json new file mode 100644 index 0000000000..ca88f857e9 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_1.ets.migrate.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_2.ets b/ets2panda/linter/test/main/entry_componentv2_2.ets new file mode 100644 index 0000000000..cf444919a1 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_2.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@Entry({ routeName: "Index", storage: new LocalStorage() }) +@ComponentV2 +struct MyPage1 { + build() { + Text("Hello World") + } +} + +@Entry({ routeName: "Index", useSharedStorage: true }) +@ComponentV2 +struct MyPage2 { + build() { + Text("Hello World") + } +} + +@Entry({ routeName: "Index", storage: new LocalStorage(), useSharedStorage: true }) +@ComponentV2 +struct MyPage3 { + build() { + Text("Hello World") + } +} + +@Entry({ routeName: "Index" }) +@ComponentV2 +struct MyPage3 { + build() { + Text("Hello World") + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_2.ets.args.json b/ets2panda/linter/test/main/entry_componentv2_2.ets.args.json new file mode 100644 index 0000000000..ee0734c0fc --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_2.ets.args.json @@ -0,0 +1,21 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "mode": { + "arkts2": "", + "autofix": "--arkts-2", + "migrate": "--arkts-2" + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_2.ets.arkts2.json b/ets2panda/linter/test/main/entry_componentv2_2.ets.arkts2.json new file mode 100644 index 0000000000..729d674774 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_2.ets.arkts2.json @@ -0,0 +1,208 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [ + { + "line": 16, + "column": 1, + "endLine": 16, + "endColumn": 60, + "problem": "EntryHasInvalidParams", + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 43, + "endLine": 16, + "endColumn": 55, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 55, + "problem": "EntryHasInvalidParams", + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 1, + "endLine": 32, + "endColumn": 84, + "problem": "EntryHasInvalidParams", + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 43, + "endLine": 32, + "endColumn": 55, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 2, + "endLine": 16, + "endColumn": 7, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 43, + "endLine": 16, + "endColumn": 55, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 17, + "column": 2, + "endLine": 17, + "endColumn": 13, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 20, + "column": 5, + "endLine": 20, + "endColumn": 9, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 2, + "endLine": 24, + "endColumn": 7, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 25, + "column": 2, + "endLine": 25, + "endColumn": 13, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 28, + "column": 5, + "endLine": 28, + "endColumn": 9, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 2, + "endLine": 32, + "endColumn": 7, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 43, + "endLine": 32, + "endColumn": 55, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 33, + "column": 2, + "endLine": 33, + "endColumn": 13, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 40, + "column": 2, + "endLine": 40, + "endColumn": 7, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 41, + "column": 2, + "endLine": 41, + "endColumn": 13, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9, + "problem": "UIInterfaceImport", + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_2.ets.autofix.json b/ets2panda/linter/test/main/entry_componentv2_2.ets.autofix.json new file mode 100644 index 0000000000..dbcdb633af --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_2.ets.autofix.json @@ -0,0 +1,395 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [ + { + "line": 16, + "column": 1, + "endLine": 16, + "endColumn": 60, + "problem": "EntryHasInvalidParams", + "autofix": [ + { + "start": 612, + "end": 663, + "replacementText": "{ routeName: \"Index\" }", + "line": 16, + "column": 1, + "endLine": 16, + "endColumn": 60 + } + ], + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 43, + "endLine": 16, + "endColumn": 55, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 55, + "problem": "EntryHasInvalidParams", + "autofix": [ + { + "start": 745, + "end": 791, + "replacementText": "{ routeName: \"Index\" }", + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 55 + } + ], + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 1, + "endLine": 32, + "endColumn": 84, + "problem": "EntryHasInvalidParams", + "autofix": [ + { + "start": 873, + "end": 948, + "replacementText": "{ routeName: \"Index\" }", + "line": 32, + "column": 1, + "endLine": 32, + "endColumn": 84 + } + ], + "suggest": "", + "rule": "The \"@ComponentV2\" decorator cannot be used together with the \"@Entry\" decorator that has \"storage\" or \"useSharedStorage\" parameters (arkui-entry-invalid-params)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 43, + "endLine": 32, + "endColumn": 55, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 2, + "endLine": 16, + "endColumn": 7, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 16, + "column": 43, + "endLine": 16, + "endColumn": 55, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 17, + "column": 2, + "endLine": 17, + "endColumn": 13, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 20, + "column": 5, + "endLine": 20, + "endColumn": 9, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 2, + "endLine": 24, + "endColumn": 7, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 25, + "column": 2, + "endLine": 25, + "endColumn": 13, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 28, + "column": 5, + "endLine": 28, + "endColumn": 9, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 2, + "endLine": 32, + "endColumn": 7, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 32, + "column": 43, + "endLine": 32, + "endColumn": 55, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"LocalStorage\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 33, + "column": 2, + "endLine": 33, + "endColumn": 13, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 36, + "column": 5, + "endLine": 36, + "endColumn": 9, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 40, + "column": 2, + "endLine": 40, + "endColumn": 7, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Entry\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 41, + "column": 2, + "endLine": 41, + "endColumn": 13, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"ComponentV2\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + }, + { + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9, + "problem": "UIInterfaceImport", + "autofix": [ + { + "start": 603, + "end": 603, + "replacementText": "\n\nimport {\n Entry,\n LocalStorage,\n ComponentV2,\n Text,\n} from '@kit.ArkUI';", + "line": 44, + "column": 5, + "endLine": 44, + "endColumn": 9 + } + ], + "suggest": "", + "rule": "The ArkUI interface \"Text\" should be imported before it is used (arkui-modular-interface)", + "severity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_2.ets.json b/ets2panda/linter/test/main/entry_componentv2_2.ets.json new file mode 100644 index 0000000000..ca88f857e9 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_2.ets.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.ets b/ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.ets new file mode 100644 index 0000000000..ee23d5281f --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Entry, + LocalStorage, + ComponentV2, + Text, +} from '@kit.ArkUI'; + +@Entry({ routeName: "Index" }) +@ComponentV2 +struct MyPage1 { + build() { + Text("Hello World") + } +} + +@Entry({ routeName: "Index" }) +@ComponentV2 +struct MyPage2 { + build() { + Text("Hello World") + } +} + +@Entry({ routeName: "Index" }) +@ComponentV2 +struct MyPage3 { + build() { + Text("Hello World") + } +} + +@Entry({ routeName: "Index" }) +@ComponentV2 +struct MyPage3 { + build() { + Text("Hello World") + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.json b/ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.json new file mode 100644 index 0000000000..ca88f857e9 --- /dev/null +++ b/ets2panda/linter/test/main/entry_componentv2_2.ets.migrate.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/parameter_properties.ets b/ets2panda/linter/test/main/parameter_properties.ets index 7706518277..0435b1a277 100644 --- a/ets2panda/linter/test/main/parameter_properties.ets +++ b/ets2panda/linter/test/main/parameter_properties.ets @@ -100,4 +100,52 @@ class G1 { class G2 { constructor(public a?: number, public b?: number) {} +} + +// empty body, braces on same line +class H1 { + constructor( + readonly x: number, + readonly y: number, + ) {} +} + +// empty body with comment, braces on same line +class H2 { + constructor( + readonly x: number, + readonly y: number, + ) { /* Comment */ } +} + +// empty body, braces on different lines +class H3 { + constructor( + readonly x: number, + readonly y: number, + ) { + } +} + +// empty body with comments on multiple lines +class H4 { + constructor( + readonly x: number, + readonly y: number, + ) { + // comment 1 + // comment 2 + } +} + +// body with statements and comments +class H5 { + constructor( + readonly x: number, + readonly y: number, + ) { + // comment at the beginning + console.log('hello'); + // comment at the end + } } \ No newline at end of file diff --git a/ets2panda/linter/test/main/parameter_properties.ets.arkts2.json b/ets2panda/linter/test/main/parameter_properties.ets.arkts2.json index 343ff611b8..fec4a5a5a3 100644 --- a/ets2panda/linter/test/main/parameter_properties.ets.arkts2.json +++ b/ets2panda/linter/test/main/parameter_properties.ets.arkts2.json @@ -233,6 +233,106 @@ "suggest": "", "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", "severity": "ERROR" + }, + { + "line": 108, + "column": 5, + "endLine": 108, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 116, + "column": 5, + "endLine": 116, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 124, + "column": 5, + "endLine": 124, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 133, + "column": 5, + "endLine": 133, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 144, + "column": 5, + "endLine": 144, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23, + "problem": "ParameterProperties", + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/main/parameter_properties.ets.autofix.json b/ets2panda/linter/test/main/parameter_properties.ets.autofix.json index d6e77be9c6..8109c3655f 100644 --- a/ets2panda/linter/test/main/parameter_properties.ets.autofix.json +++ b/ets2panda/linter/test/main/parameter_properties.ets.autofix.json @@ -24,7 +24,7 @@ { "start": 622, "end": 622, - "replacementText": "public readonly x: number;\nprotected y: number;\nprivate z: number;\n", + "replacementText": "public readonly x: number;\n protected y: number;\n private z: number;\n ", "line": 20, "column": 5, "endLine": 20, @@ -58,9 +58,9 @@ "endColumn": 12 }, { - "start": 717, - "end": 719, - "replacementText": "{\n this.x = x;\n this.y = y;\n this.z = z;\n}", + "start": 718, + "end": 718, + "replacementText": "\n this.x = x;\n this.y = y;\n this.z = z;\n ", "line": 20, "column": 5, "endLine": 20, @@ -81,7 +81,7 @@ { "start": 622, "end": 622, - "replacementText": "public readonly x: number;\nprotected y: number;\nprivate z: number;\n", + "replacementText": "public readonly x: number;\n protected y: number;\n private z: number;\n ", "line": 20, "column": 5, "endLine": 20, @@ -115,9 +115,9 @@ "endColumn": 12 }, { - "start": 717, - "end": 719, - "replacementText": "{\n this.x = x;\n this.y = y;\n this.z = z;\n}", + "start": 718, + "end": 718, + "replacementText": "\n this.x = x;\n this.y = y;\n this.z = z;\n ", "line": 20, "column": 5, "endLine": 20, @@ -138,7 +138,7 @@ { "start": 622, "end": 622, - "replacementText": "public readonly x: number;\nprotected y: number;\nprivate z: number;\n", + "replacementText": "public readonly x: number;\n protected y: number;\n private z: number;\n ", "line": 20, "column": 5, "endLine": 20, @@ -172,9 +172,9 @@ "endColumn": 12 }, { - "start": 717, - "end": 719, - "replacementText": "{\n this.x = x;\n this.y = y;\n this.z = z;\n}", + "start": 718, + "end": 718, + "replacementText": "\n this.x = x;\n this.y = y;\n this.z = z;\n ", "line": 20, "column": 5, "endLine": 20, @@ -205,7 +205,7 @@ { "start": 870, "end": 870, - "replacementText": "public w: string;\nprivate readonly r: number[];\n", + "replacementText": "public w: string;\n private readonly r: number[];\n ", "line": 34, "column": 60, "endLine": 34, @@ -230,9 +230,9 @@ "endColumn": 67 }, { - "start": 969, - "end": 1021, - "replacementText": "{\n this.w = w;\n this.r = r;\n console.log(q, this.w, e, this.r, this.f);\n}", + "start": 975, + "end": 975, + "replacementText": "this.w = w;\n this.r = r;\n ", "line": 34, "column": 60, "endLine": 34, @@ -253,7 +253,7 @@ { "start": 870, "end": 870, - "replacementText": "public w: string;\nprivate readonly r: number[];\n", + "replacementText": "public w: string;\n private readonly r: number[];\n ", "line": 34, "column": 60, "endLine": 34, @@ -278,9 +278,9 @@ "endColumn": 67 }, { - "start": 969, - "end": 1021, - "replacementText": "{\n this.w = w;\n this.r = r;\n console.log(q, this.w, e, this.r, this.f);\n}", + "start": 975, + "end": 975, + "replacementText": "this.w = w;\n this.r = r;\n ", "line": 34, "column": 60, "endLine": 34, @@ -371,7 +371,7 @@ { "start": 1275, "end": 1275, - "replacementText": "readonly a: number;\n", + "replacementText": "readonly a: number;\n ", "line": 54, "column": 15, "endLine": 54, @@ -387,9 +387,9 @@ "endColumn": 33 }, { - "start": 1307, - "end": 1309, - "replacementText": "{\n this.a = a;\n}", + "start": 1308, + "end": 1308, + "replacementText": "\n this.a = a;\n ", "line": 54, "column": 15, "endLine": 54, @@ -410,7 +410,7 @@ { "start": 1335, "end": 1335, - "replacementText": "readonly aa: number;\nb: number;\npublic c: number;\n", + "replacementText": "readonly aa: number;\n b: number;\n public c: number;\n ", "line": 61, "column": 5, "endLine": 61, @@ -444,9 +444,9 @@ "endColumn": 11 }, { - "start": 1430, - "end": 1450, - "replacementText": "{\n super(aa);\n this.aa = aa;\n this.b = b;\n this.c = c;\n}", + "start": 1446, + "end": 1446, + "replacementText": "\n this.aa = aa;\n this.b = b;\n this.c = c;", "line": 61, "column": 5, "endLine": 61, @@ -467,7 +467,7 @@ { "start": 1335, "end": 1335, - "replacementText": "readonly aa: number;\nb: number;\npublic c: number;\n", + "replacementText": "readonly aa: number;\n b: number;\n public c: number;\n ", "line": 61, "column": 5, "endLine": 61, @@ -501,9 +501,9 @@ "endColumn": 11 }, { - "start": 1430, - "end": 1450, - "replacementText": "{\n super(aa);\n this.aa = aa;\n this.b = b;\n this.c = c;\n}", + "start": 1446, + "end": 1446, + "replacementText": "\n this.aa = aa;\n this.b = b;\n this.c = c;", "line": 61, "column": 5, "endLine": 61, @@ -524,7 +524,7 @@ { "start": 1335, "end": 1335, - "replacementText": "readonly aa: number;\nb: number;\npublic c: number;\n", + "replacementText": "readonly aa: number;\n b: number;\n public c: number;\n ", "line": 61, "column": 5, "endLine": 61, @@ -558,9 +558,9 @@ "endColumn": 11 }, { - "start": 1430, - "end": 1450, - "replacementText": "{\n super(aa);\n this.aa = aa;\n this.b = b;\n this.c = c;\n}", + "start": 1446, + "end": 1446, + "replacementText": "\n this.aa = aa;\n this.b = b;\n this.c = c;", "line": 61, "column": 5, "endLine": 61, @@ -581,7 +581,7 @@ { "start": 1477, "end": 1477, - "replacementText": "readonly aa: number;\n", + "replacementText": "readonly aa: number;\n ", "line": 68, "column": 15, "endLine": 68, @@ -597,9 +597,9 @@ "endColumn": 34 }, { - "start": 1510, - "end": 1594, - "replacementText": "{\n let f2: number = 1;\n console.log('before super() call');\n super(aa);\n this.aa = aa;\n}", + "start": 1590, + "end": 1590, + "replacementText": "\n this.aa = aa;", "line": 68, "column": 15, "endLine": 68, @@ -620,7 +620,7 @@ { "start": 1621, "end": 1621, - "replacementText": "readonly aa: number;\n", + "replacementText": "readonly aa: number;\n ", "line": 76, "column": 15, "endLine": 76, @@ -636,9 +636,9 @@ "endColumn": 34 }, { - "start": 1654, - "end": 1737, - "replacementText": "{\n super(aa);\n this.aa = aa;\n let f3: number = 1;\n console.log('after super() call');\n}", + "start": 1675, + "end": 1675, + "replacementText": "this.aa = aa;\n ", "line": 76, "column": 15, "endLine": 76, @@ -659,7 +659,7 @@ { "start": 1764, "end": 1764, - "replacementText": "readonly aa: number;\n", + "replacementText": "readonly aa: number;\n ", "line": 84, "column": 15, "endLine": 84, @@ -675,9 +675,9 @@ "endColumn": 34 }, { - "start": 1797, - "end": 1944, - "replacementText": "{\n let f4: number = 1;\n console.log('before super() call');\n super(aa);\n this.aa = aa;\n console.log('after super() call');\n let f5: number = 1;\n}", + "start": 1882, + "end": 1882, + "replacementText": "this.aa = aa;\n ", "line": 84, "column": 15, "endLine": 84, @@ -698,7 +698,7 @@ { "start": 2003, "end": 2003, - "replacementText": "public a?: number;\npublic b: number;\n", + "replacementText": "public a?: number;\n public b: number;\n ", "line": 98, "column": 34, "endLine": 98, @@ -723,9 +723,9 @@ "endColumn": 40 }, { - "start": 2052, - "end": 2054, - "replacementText": "{\n this.a = a;\n this.b = b;\n}", + "start": 2053, + "end": 2053, + "replacementText": "\n this.a = a;\n this.b = b;\n ", "line": 98, "column": 34, "endLine": 98, @@ -746,7 +746,7 @@ { "start": 2003, "end": 2003, - "replacementText": "public a?: number;\npublic b: number;\n", + "replacementText": "public a?: number;\n public b: number;\n ", "line": 98, "column": 34, "endLine": 98, @@ -771,9 +771,9 @@ "endColumn": 40 }, { - "start": 2052, - "end": 2054, - "replacementText": "{\n this.a = a;\n this.b = b;\n}", + "start": 2053, + "end": 2053, + "replacementText": "\n this.a = a;\n this.b = b;\n ", "line": 98, "column": 34, "endLine": 98, @@ -794,7 +794,7 @@ { "start": 2071, "end": 2071, - "replacementText": "public a?: number;\npublic b?: number;\n", + "replacementText": "public a?: number;\n public b?: number;\n ", "line": 102, "column": 34, "endLine": 102, @@ -819,9 +819,9 @@ "endColumn": 40 }, { - "start": 2121, - "end": 2123, - "replacementText": "{\n this.a = a;\n this.b = b;\n}", + "start": 2122, + "end": 2122, + "replacementText": "\n this.a = a;\n this.b = b;\n ", "line": 102, "column": 34, "endLine": 102, @@ -842,7 +842,7 @@ { "start": 2071, "end": 2071, - "replacementText": "public a?: number;\npublic b?: number;\n", + "replacementText": "public a?: number;\n public b?: number;\n ", "line": 102, "column": 34, "endLine": 102, @@ -867,9 +867,9 @@ "endColumn": 40 }, { - "start": 2121, - "end": 2123, - "replacementText": "{\n this.a = a;\n this.b = b;\n}", + "start": 2122, + "end": 2122, + "replacementText": "\n this.a = a;\n this.b = b;\n ", "line": 102, "column": 34, "endLine": 102, @@ -879,6 +879,486 @@ "suggest": "", "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", "severity": "ERROR" + }, + { + "line": 108, + "column": 5, + "endLine": 108, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2175, + "end": 2175, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + }, + { + "start": 2192, + "end": 2210, + "replacementText": "x: number", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + }, + { + "start": 2216, + "end": 2234, + "replacementText": "y: number", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + }, + { + "start": 2241, + "end": 2241, + "replacementText": "\n this.x = x;\n this.y = y;\n ", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2175, + "end": 2175, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + }, + { + "start": 2192, + "end": 2210, + "replacementText": "x: number", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + }, + { + "start": 2216, + "end": 2234, + "replacementText": "y: number", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + }, + { + "start": 2241, + "end": 2241, + "replacementText": "\n this.x = x;\n this.y = y;\n ", + "line": 109, + "column": 5, + "endLine": 109, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 116, + "column": 5, + "endLine": 116, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2307, + "end": 2307, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + }, + { + "start": 2324, + "end": 2342, + "replacementText": "x: number", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + }, + { + "start": 2348, + "end": 2366, + "replacementText": "y: number", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + }, + { + "start": 2388, + "end": 2388, + "replacementText": "\n this.x = x;\n this.y = y;\n ", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2307, + "end": 2307, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + }, + { + "start": 2324, + "end": 2342, + "replacementText": "x: number", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + }, + { + "start": 2348, + "end": 2366, + "replacementText": "y: number", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + }, + { + "start": 2388, + "end": 2388, + "replacementText": "\n this.x = x;\n this.y = y;\n ", + "line": 117, + "column": 5, + "endLine": 117, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 124, + "column": 5, + "endLine": 124, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2447, + "end": 2447, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + }, + { + "start": 2464, + "end": 2482, + "replacementText": "x: number", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + }, + { + "start": 2488, + "end": 2506, + "replacementText": "y: number", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + }, + { + "start": 2513, + "end": 2513, + "replacementText": "\n this.x = x;\n this.y = y;", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2447, + "end": 2447, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + }, + { + "start": 2464, + "end": 2482, + "replacementText": "x: number", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + }, + { + "start": 2488, + "end": 2506, + "replacementText": "y: number", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + }, + { + "start": 2513, + "end": 2513, + "replacementText": "\n this.x = x;\n this.y = y;", + "line": 125, + "column": 5, + "endLine": 125, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 133, + "column": 5, + "endLine": 133, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2580, + "end": 2580, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + }, + { + "start": 2597, + "end": 2615, + "replacementText": "x: number", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + }, + { + "start": 2621, + "end": 2639, + "replacementText": "y: number", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + }, + { + "start": 2680, + "end": 2680, + "replacementText": "\n this.x = x;\n this.y = y;", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2580, + "end": 2580, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + }, + { + "start": 2597, + "end": 2615, + "replacementText": "x: number", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + }, + { + "start": 2621, + "end": 2639, + "replacementText": "y: number", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + }, + { + "start": 2680, + "end": 2680, + "replacementText": "\n this.x = x;\n this.y = y;", + "line": 134, + "column": 5, + "endLine": 134, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 144, + "column": 5, + "endLine": 144, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2738, + "end": 2738, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + }, + { + "start": 2755, + "end": 2773, + "replacementText": "x: number", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + }, + { + "start": 2779, + "end": 2797, + "replacementText": "y: number", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + }, + { + "start": 2841, + "end": 2841, + "replacementText": "this.x = x;\n this.y = y;\n ", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" + }, + { + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23, + "problem": "ParameterProperties", + "autofix": [ + { + "start": 2738, + "end": 2738, + "replacementText": "readonly x: number;\n readonly y: number;\n ", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + }, + { + "start": 2755, + "end": 2773, + "replacementText": "x: number", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + }, + { + "start": 2779, + "end": 2797, + "replacementText": "y: number", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + }, + { + "start": 2841, + "end": 2841, + "replacementText": "this.x = x;\n this.y = y;\n ", + "line": 145, + "column": 5, + "endLine": 145, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", + "severity": "ERROR" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/main/parameter_properties.ets.migrate.ets b/ets2panda/linter/test/main/parameter_properties.ets.migrate.ets index 6a79a113c4..458320d61e 100644 --- a/ets2panda/linter/test/main/parameter_properties.ets.migrate.ets +++ b/ets2panda/linter/test/main/parameter_properties.ets.migrate.ets @@ -15,9 +15,9 @@ class A { public readonly x: number; -protected y: number; -private z: number; -constructor( + protected y: number; + private z: number; + constructor( x: number, y: number, z: number @@ -25,7 +25,7 @@ constructor( this.x = x; this.y = y; this.z = z; -} + } foo(): void { console.log(this.x + this.y + this.z); @@ -39,12 +39,12 @@ class B { public f: number = 10; public w: string; -private readonly r: number[]; -constructor(q: number, w = 'default', e: boolean, r: number[] = [1, 2, 3]) { + private readonly r: number[]; + constructor(q: number, w = 'default', e: boolean, r: number[] = [1, 2, 3]) { this.w = w; this.r = r; console.log(q, this.w, e, this.r, this.f); -} + } } const b = new B(1, '2', true, []); @@ -59,11 +59,11 @@ interface GeneratedTypeLiteralInterface_1 { } class D { public a: number; -private b: GeneratedTypeLiteralInterface_1; -constructor(a: number, b: GeneratedTypeLiteralInterface_1) { + private b: GeneratedTypeLiteralInterface_1; + constructor(a: number, b: GeneratedTypeLiteralInterface_1) { this.a = a; this.b = b; -} // not fixable + } // not fixable } class E { @@ -71,16 +71,16 @@ class E { c: number = 0; readonly a: number; -constructor(a: number) { + constructor(a: number) { this.a = a; -} + } } class F extends E { readonly aa: number; -b: number; -public c: number; -constructor( + b: number; + public c: number; + constructor( aa: number, b: number, c: number @@ -89,39 +89,39 @@ constructor( this.aa = aa; this.b = b; this.c = c; -} + } } class F2 extends E { readonly aa: number; -constructor(aa: number) { + constructor(aa: number) { let f2: number = 1; console.log('before super() call'); super(aa); this.aa = aa; -} + } } class F3 extends E { readonly aa: number; -constructor(aa: number) { + constructor(aa: number) { super(aa); this.aa = aa; let f3: number = 1; console.log('after super() call'); -} + } } class F4 extends E { readonly aa: number; -constructor(aa: number) { + constructor(aa: number) { let f4: number = 1; console.log('before super() call'); super(aa); this.aa = aa; console.log('after super() call'); let f5: number = 1; -} + } } class G { @@ -130,18 +130,88 @@ class G { class G1 { public a?: number; -public b: number; -constructor(a?: number, b: number) { + public b: number; + constructor(a?: number, b: number) { this.a = a; this.b = b; -} + } } class G2 { public a?: number; -public b?: number; -constructor(a?: number, b?: number) { + public b?: number; + constructor(a?: number, b?: number) { this.a = a; this.b = b; + } +} + +// empty body, braces on same line +class H1 { + readonly x: number; + readonly y: number; + constructor( + x: number, + y: number, + ) { + this.x = x; + this.y = y; + } } + +// empty body with comment, braces on same line +class H2 { + readonly x: number; + readonly y: number; + constructor( + x: number, + y: number, + ) { /* Comment */ + this.x = x; + this.y = y; + } +} + +// empty body, braces on different lines +class H3 { + readonly x: number; + readonly y: number; + constructor( + x: number, + y: number, + ) { + this.x = x; + this.y = y; + } +} + +// empty body with comments on multiple lines +class H4 { + readonly x: number; + readonly y: number; + constructor( + x: number, + y: number, + ) { + // comment 1 + // comment 2 + this.x = x; + this.y = y; + } +} + +// body with statements and comments +class H5 { + readonly x: number; + readonly y: number; + constructor( + x: number, + y: number, + ) { + // comment at the beginning + this.x = x; + this.y = y; + console.log('hello'); + // comment at the end + } } \ No newline at end of file diff --git a/ets2panda/linter/test/main/parameter_properties.ets.migrate.json b/ets2panda/linter/test/main/parameter_properties.ets.migrate.json index f5bbbab541..7282b62c14 100644 --- a/ets2panda/linter/test/main/parameter_properties.ets.migrate.json +++ b/ets2panda/linter/test/main/parameter_properties.ets.migrate.json @@ -16,9 +16,9 @@ "result": [ { "line": 43, - "column": 24, + "column": 26, "endLine": 43, - "endColumn": 25, + "endColumn": 27, "problem": "DefaultArgsBehindRequiredArgs", "suggest": "", "rule": "Default parameters must be placed after mandatory parameters (arkts-default-args-behind-required-args)", diff --git a/ets2panda/linter/test/main/private_identifiers.ets.autofix.json b/ets2panda/linter/test/main/private_identifiers.ets.autofix.json index 60af364c02..a05c787677 100644 --- a/ets2panda/linter/test/main/private_identifiers.ets.autofix.json +++ b/ets2panda/linter/test/main/private_identifiers.ets.autofix.json @@ -84,12 +84,20 @@ { "start": 617, "end": 628, - "replacementText": "private p: number;" + "replacementText": "private p: number;", + "line": 44, + "column": 22, + "endLine": 44, + "endColumn": 24 }, { "start": 1121, "end": 1123, - "replacementText": "p" + "replacementText": "p", + "line": 44, + "column": 22, + "endLine": 44, + "endColumn": 24 } ], "suggest": "", @@ -116,12 +124,20 @@ { "start": 677, "end": 689, - "replacementText": "private q?: string;" + "replacementText": "private q?: string;", + "line": 44, + "column": 43, + "endLine": 44, + "endColumn": 45 }, { "start": 1142, "end": 1144, - "replacementText": "q" + "replacementText": "q", + "line": 44, + "column": 43, + "endLine": 44, + "endColumn": 45 } ], "suggest": "", @@ -148,12 +164,20 @@ { "start": 692, "end": 704, - "replacementText": "private e!: string;" + "replacementText": "private e!: string;", + "line": 44, + "column": 53, + "endLine": 44, + "endColumn": 55 }, { "start": 1152, "end": 1154, - "replacementText": "e" + "replacementText": "e", + "line": 44, + "column": 53, + "endLine": 44, + "endColumn": 55 } ], "suggest": "", @@ -170,12 +194,20 @@ { "start": 707, "end": 721, - "replacementText": "private static s = 0;" + "replacementText": "private static s = 0;", + "line": 44, + "column": 60, + "endLine": 44, + "endColumn": 62 }, { "start": 1159, "end": 1161, - "replacementText": "s" + "replacementText": "s", + "line": 44, + "column": 60, + "endLine": 44, + "endColumn": 62 } ], "suggest": "", @@ -192,12 +224,20 @@ { "start": 724, "end": 741, - "replacementText": "private readonly r = 20;" + "replacementText": "private readonly r = 20;", + "line": 44, + "column": 70, + "endLine": 44, + "endColumn": 72 }, { "start": 1169, "end": 1171, - "replacementText": "r" + "replacementText": "r", + "line": 44, + "column": 70, + "endLine": 44, + "endColumn": 72 } ], "suggest": "", @@ -214,12 +254,20 @@ { "start": 744, "end": 768, - "replacementText": "private static readonly sr = 0;" + "replacementText": "private static readonly sr = 0;", + "line": 44, + "column": 77, + "endLine": 44, + "endColumn": 80 }, { "start": 1176, "end": 1179, - "replacementText": "sr" + "replacementText": "sr", + "line": 44, + "column": 77, + "endLine": 44, + "endColumn": 80 } ], "suggest": "", @@ -236,12 +284,20 @@ { "start": 771, "end": 801, - "replacementText": "private static readonly srq?: string;" + "replacementText": "private static readonly srq?: string;", + "line": 44, + "column": 85, + "endLine": 44, + "endColumn": 89 }, { "start": 1184, "end": 1188, - "replacementText": "srq" + "replacementText": "srq", + "line": 44, + "column": 85, + "endLine": 44, + "endColumn": 89 } ], "suggest": "", @@ -258,12 +314,20 @@ { "start": 805, "end": 827, - "replacementText": "private m(x: number): void { }" + "replacementText": "private m(x: number): void { }", + "line": 45, + "column": 10, + "endLine": 45, + "endColumn": 12 }, { "start": 1224, "end": 1226, - "replacementText": "m" + "replacementText": "m", + "line": 45, + "column": 10, + "endLine": 45, + "endColumn": 12 } ], "suggest": "", @@ -300,12 +364,20 @@ { "start": 955, "end": 987, - "replacementText": "private get g1(): number { return 10; }" + "replacementText": "private get g1(): number { return 10; }", + "line": 48, + "column": 18, + "endLine": 48, + "endColumn": 21 }, { "start": 1315, "end": 1318, - "replacementText": "g1" + "replacementText": "g1", + "line": 48, + "column": 18, + "endLine": 48, + "endColumn": 21 } ], "suggest": "", @@ -322,12 +394,20 @@ { "start": 990, "end": 1012, - "replacementText": "private set s1(x: number) { }" + "replacementText": "private set s1(x: number) { }", + "line": 49, + "column": 10, + "endLine": 49, + "endColumn": 13 }, { "start": 1329, "end": 1332, - "replacementText": "s1" + "replacementText": "s1", + "line": 49, + "column": 10, + "endLine": 49, + "endColumn": 13 } ], "suggest": "", @@ -344,12 +424,20 @@ { "start": 1016, "end": 1055, - "replacementText": "private static get g2(): number { return 10; }" + "replacementText": "private static get g2(): number { return 10; }", + "line": 50, + "column": 15, + "endLine": 50, + "endColumn": 18 }, { "start": 1352, "end": 1355, - "replacementText": "g2" + "replacementText": "g2", + "line": 50, + "column": 15, + "endLine": 50, + "endColumn": 18 } ], "suggest": "", @@ -366,12 +454,20 @@ { "start": 1058, "end": 1087, - "replacementText": "private static set s2(x: number) { }" + "replacementText": "private static set s2(x: number) { }", + "line": 51, + "column": 7, + "endLine": 51, + "endColumn": 10 }, { "start": 1363, "end": 1366, - "replacementText": "s2" + "replacementText": "s2", + "line": 51, + "column": 7, + "endLine": 51, + "endColumn": 10 } ], "suggest": "", @@ -388,12 +484,20 @@ { "start": 617, "end": 628, - "replacementText": "private p: number;" + "replacementText": "private p: number;", + "line": 44, + "column": 22, + "endLine": 44, + "endColumn": 24 }, { "start": 1121, "end": 1123, - "replacementText": "p" + "replacementText": "p", + "line": 44, + "column": 22, + "endLine": 44, + "endColumn": 24 } ], "suggest": "", @@ -420,12 +524,20 @@ { "start": 677, "end": 689, - "replacementText": "private q?: string;" + "replacementText": "private q?: string;", + "line": 44, + "column": 43, + "endLine": 44, + "endColumn": 45 }, { "start": 1142, "end": 1144, - "replacementText": "q" + "replacementText": "q", + "line": 44, + "column": 43, + "endLine": 44, + "endColumn": 45 } ], "suggest": "", @@ -442,12 +554,20 @@ { "start": 692, "end": 704, - "replacementText": "private e!: string;" + "replacementText": "private e!: string;", + "line": 44, + "column": 53, + "endLine": 44, + "endColumn": 55 }, { "start": 1152, "end": 1154, - "replacementText": "e" + "replacementText": "e", + "line": 44, + "column": 53, + "endLine": 44, + "endColumn": 55 } ], "suggest": "", @@ -464,12 +584,20 @@ { "start": 707, "end": 721, - "replacementText": "private static s = 0;" + "replacementText": "private static s = 0;", + "line": 44, + "column": 60, + "endLine": 44, + "endColumn": 62 }, { "start": 1159, "end": 1161, - "replacementText": "s" + "replacementText": "s", + "line": 44, + "column": 60, + "endLine": 44, + "endColumn": 62 } ], "suggest": "", @@ -486,12 +614,20 @@ { "start": 724, "end": 741, - "replacementText": "private readonly r = 20;" + "replacementText": "private readonly r = 20;", + "line": 44, + "column": 70, + "endLine": 44, + "endColumn": 72 }, { "start": 1169, "end": 1171, - "replacementText": "r" + "replacementText": "r", + "line": 44, + "column": 70, + "endLine": 44, + "endColumn": 72 } ], "suggest": "", @@ -508,12 +644,20 @@ { "start": 744, "end": 768, - "replacementText": "private static readonly sr = 0;" + "replacementText": "private static readonly sr = 0;", + "line": 44, + "column": 77, + "endLine": 44, + "endColumn": 80 }, { "start": 1176, "end": 1179, - "replacementText": "sr" + "replacementText": "sr", + "line": 44, + "column": 77, + "endLine": 44, + "endColumn": 80 } ], "suggest": "", @@ -530,12 +674,20 @@ { "start": 771, "end": 801, - "replacementText": "private static readonly srq?: string;" + "replacementText": "private static readonly srq?: string;", + "line": 44, + "column": 85, + "endLine": 44, + "endColumn": 89 }, { "start": 1184, "end": 1188, - "replacementText": "srq" + "replacementText": "srq", + "line": 44, + "column": 85, + "endLine": 44, + "endColumn": 89 } ], "suggest": "", @@ -552,12 +704,20 @@ { "start": 805, "end": 827, - "replacementText": "private m(x: number): void { }" + "replacementText": "private m(x: number): void { }", + "line": 45, + "column": 10, + "endLine": 45, + "endColumn": 12 }, { "start": 1224, "end": 1226, - "replacementText": "m" + "replacementText": "m", + "line": 45, + "column": 10, + "endLine": 45, + "endColumn": 12 } ], "suggest": "", @@ -594,12 +754,20 @@ { "start": 955, "end": 987, - "replacementText": "private get g1(): number { return 10; }" + "replacementText": "private get g1(): number { return 10; }", + "line": 48, + "column": 18, + "endLine": 48, + "endColumn": 21 }, { "start": 1315, "end": 1318, - "replacementText": "g1" + "replacementText": "g1", + "line": 48, + "column": 18, + "endLine": 48, + "endColumn": 21 } ], "suggest": "", @@ -616,12 +784,20 @@ { "start": 990, "end": 1012, - "replacementText": "private set s1(x: number) { }" + "replacementText": "private set s1(x: number) { }", + "line": 49, + "column": 10, + "endLine": 49, + "endColumn": 13 }, { "start": 1329, "end": 1332, - "replacementText": "s1" + "replacementText": "s1", + "line": 49, + "column": 10, + "endLine": 49, + "endColumn": 13 } ], "suggest": "", @@ -638,12 +814,20 @@ { "start": 1016, "end": 1055, - "replacementText": "private static get g2(): number { return 10; }" + "replacementText": "private static get g2(): number { return 10; }", + "line": 50, + "column": 15, + "endLine": 50, + "endColumn": 18 }, { "start": 1352, "end": 1355, - "replacementText": "g2" + "replacementText": "g2", + "line": 50, + "column": 15, + "endLine": 50, + "endColumn": 18 } ], "suggest": "", @@ -660,12 +844,20 @@ { "start": 1058, "end": 1087, - "replacementText": "private static set s2(x: number) { }" + "replacementText": "private static set s2(x: number) { }", + "line": 51, + "column": 7, + "endLine": 51, + "endColumn": 10 }, { "start": 1363, "end": 1366, - "replacementText": "s2" + "replacementText": "s2", + "line": 51, + "column": 7, + "endLine": 51, + "endColumn": 10 } ], "suggest": "", @@ -680,8 +872,7 @@ "problem": "DeclWithDuplicateName", "suggest": "", "rule": "Use unique names for types and namespaces. (arkts-unique-names)", - "severity": "ERROR", - "exclusive": "RT" + "severity": "ERROR" }, { "line": 59, @@ -691,8 +882,7 @@ "problem": "DeclWithDuplicateName", "suggest": "", "rule": "Use unique names for types and namespaces. (arkts-unique-names)", - "severity": "ERROR", - "exclusive": "RT" + "severity": "ERROR" }, { "line": 56, @@ -704,12 +894,20 @@ { "start": 1401, "end": 1412, - "replacementText": "private a: string;" + "replacementText": "private a: string;", + "line": 64, + "column": 32, + "endLine": 64, + "endColumn": 34 }, { "start": 1572, "end": 1574, - "replacementText": "a" + "replacementText": "a", + "line": 64, + "column": 32, + "endLine": 64, + "endColumn": 34 } ], "suggest": "", @@ -746,12 +944,20 @@ { "start": 1494, "end": 1526, - "replacementText": "private bar(): string { return 'baz'; }" + "replacementText": "private bar(): string { return 'baz'; }", + "line": 66, + "column": 18, + "endLine": 66, + "endColumn": 22 }, { "start": 1655, "end": 1659, - "replacementText": "bar" + "replacementText": "bar", + "line": 66, + "column": 18, + "endLine": 66, + "endColumn": 22 } ], "suggest": "", @@ -778,12 +984,20 @@ { "start": 1401, "end": 1412, - "replacementText": "private a: string;" + "replacementText": "private a: string;", + "line": 64, + "column": 32, + "endLine": 64, + "endColumn": 34 }, { "start": 1572, "end": 1574, - "replacementText": "a" + "replacementText": "a", + "line": 64, + "column": 32, + "endLine": 64, + "endColumn": 34 } ], "suggest": "", @@ -810,12 +1024,20 @@ { "start": 1494, "end": 1526, - "replacementText": "private bar(): string { return 'baz'; }" + "replacementText": "private bar(): string { return 'baz'; }", + "line": 66, + "column": 18, + "endLine": 66, + "endColumn": 22 }, { "start": 1655, "end": 1659, - "replacementText": "bar" + "replacementText": "bar", + "line": 66, + "column": 18, + "endLine": 66, + "endColumn": 22 } ], "suggest": "", @@ -830,8 +1052,7 @@ "problem": "DeclWithDuplicateName", "suggest": "", "rule": "Use unique names for types and namespaces. (arkts-unique-names)", - "severity": "ERROR", - "exclusive": "RT" + "severity": "ERROR" }, { "line": 71, @@ -843,7 +1064,11 @@ { "start": 1682, "end": 1693, - "replacementText": "private a: number;" + "replacementText": "private a: number;", + "line": 71, + "column": 3, + "endLine": 71, + "endColumn": 5 } ], "suggest": "", @@ -870,17 +1095,29 @@ { "start": 1726, "end": 1726, - "replacementText": "public b: number;\n" + "replacementText": "public b: number;\n ", + "line": 74, + "column": 15, + "endLine": 74, + "endColumn": 21 }, { "start": 1738, "end": 1754, - "replacementText": "b: number" + "replacementText": "b: number", + "line": 74, + "column": 15, + "endLine": 74, + "endColumn": 21 }, { - "start": 1756, - "end": 1758, - "replacementText": "{\n this.b = b;\n}" + "start": 1757, + "end": 1757, + "replacementText": "\n this.b = b;\n ", + "line": 74, + "column": 15, + "endLine": 74, + "endColumn": 21 } ], "suggest": "", diff --git a/ets2panda/linter/test/main/private_identifiers.ets.migrate.ets b/ets2panda/linter/test/main/private_identifiers.ets.migrate.ets index 001beccf21..2fa0c833d2 100644 --- a/ets2panda/linter/test/main/private_identifiers.ets.migrate.ets +++ b/ets2panda/linter/test/main/private_identifiers.ets.migrate.ets @@ -72,7 +72,7 @@ class E { #b: string; // not fixable public b: number; -constructor(b: number) { + constructor(b: number) { this.b = b; -} + } } \ No newline at end of file diff --git a/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets b/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets index b3af1c4b77..a92df18dfc 100644 --- a/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets +++ b/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets @@ -68,4 +68,11 @@ export default class MyUIAbility6 extends UIAbility { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); return sleep1(1000); } -} \ No newline at end of file +} + +export default class MyUIAbility7 extends MyUIAbility1 { + async onDestroy(): Promise { // use UIAbility onDestroy, should report error + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + return sleep(1000); + } +} diff --git a/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets.arkts2.json b/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets.arkts2.json index cfda66c38c..751ef530a4 100644 --- a/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets.arkts2.json +++ b/ets2panda/linter/test/main/sdk_ability_asynchronous_lifecycle.ets.arkts2.json @@ -123,6 +123,16 @@ "suggest": "", "rule": "Type \"void\" has no instances.(sdk-limited-void-type)", "severity": "ERROR" + }, + { + "line": 74, + "column": 9, + "endLine": 74, + "endColumn": 18, + "problem": "SdkAbilityAsynchronousLifecycle", + "suggest": "", + "rule": "1.2 Void cannot be combined. OnDestroy/onDisconnect (The return type of the method is now void | Promise) needs to be split into two interfaces. (sdk-ability-asynchronous-lifecycle)", + "severity": "ERROR" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets b/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets index b3c46a7437..3e32ebb360 100644 --- a/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets +++ b/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets @@ -48,6 +48,7 @@ class MyAbilityStage extends AbilityStage { } let applicationContext = this.context.getApplicationContext(); let lifecycleId = applicationContext.on('abilityLifecycle', abilityLifecycleCallback); // report error normally + let lifecycleId2 = applicationContext.on('abilityLifecycle', new AbilityLifecycleCallback()); // report error normally } } diff --git a/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.arkts2.json b/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.arkts2.json index 2eff60dbc9..2eb1086c7e 100644 --- a/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.arkts2.json +++ b/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.arkts2.json @@ -55,9 +55,29 @@ "severity": "ERROR" }, { - "line": 57, + "line": 51, + "column": 24, + "endLine": 51, + "endColumn": 97, + "problem": "SdkAbilityLifecycleMonitor", + "suggest": "", + "rule": "The UIAbility of 1.2 needs to be listened by the new StaticAbilityLifecycleCallback. The original AbilityLifecycleCallback can only listen to the UIAbility of 1.1 (sdk-ability-lifecycle-monitor)", + "severity": "ERROR" + }, + { + "line": 51, + "column": 70, + "endLine": 51, + "endColumn": 94, + "problem": "DynamicCtorCall", + "suggest": "", + "rule": "\"new\" expression with dynamic constructor type is not supported (arkts-no-dynamic-ctor-call)", + "severity": "ERROR" + }, + { + "line": 58, "column": 7, - "endLine": 58, + "endLine": 59, "endColumn": 8, "problem": "ObjectLiteralProperty", "suggest": "", @@ -65,9 +85,9 @@ "severity": "ERROR" }, { - "line": 57, + "line": 58, "column": 23, - "endLine": 57, + "endLine": 58, "endColumn": 30, "problem": "ParameterType", "suggest": "", @@ -75,9 +95,9 @@ "severity": "ERROR" }, { - "line": 57, + "line": 58, "column": 23, - "endLine": 57, + "endLine": 58, "endColumn": 30, "problem": "AnyType", "suggest": "", @@ -85,9 +105,9 @@ "severity": "ERROR" }, { - "line": 85, + "line": 86, "column": 23, - "endLine": 85, + "endLine": 86, "endColumn": 90, "problem": "SdkAbilityLifecycleMonitor", "suggest": "", diff --git a/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.json b/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.json index 81beed97c6..46f84c7255 100644 --- a/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.json +++ b/ets2panda/linter/test/main/sdk_ability_lifecycle_monitor.ets.json @@ -25,9 +25,9 @@ "severity": "ERROR" }, { - "line": 57, + "line": 58, "column": 23, - "endLine": 57, + "endLine": 58, "endColumn": 30, "problem": "AnyType", "suggest": "", diff --git a/ets2panda/linter/test/rules/rule25.ets.autofix.json b/ets2panda/linter/test/rules/rule25.ets.autofix.json index b69e79be0b..f39859ea12 100644 --- a/ets2panda/linter/test/rules/rule25.ets.autofix.json +++ b/ets2panda/linter/test/rules/rule25.ets.autofix.json @@ -24,27 +24,47 @@ { "start": 624, "end": 624, - "replacementText": "protected ssn: string;\nprivate firstName: string;\nprivate lastName: string;\n" + "replacementText": "protected ssn: string;\n private firstName: string;\n private lastName: string;\n ", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 645, "end": 666, - "replacementText": "ssn: string" + "replacementText": "ssn: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 676, "end": 701, - "replacementText": "firstName: string" + "replacementText": "firstName: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 711, "end": 735, - "replacementText": "lastName: string" + "replacementText": "lastName: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { - "start": 742, - "end": 840, - "replacementText": "{\n this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n}" + "start": 752, + "end": 752, + "replacementText": "this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n ", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 } ], "suggest": "", @@ -61,27 +81,47 @@ { "start": 624, "end": 624, - "replacementText": "protected ssn: string;\nprivate firstName: string;\nprivate lastName: string;\n" + "replacementText": "protected ssn: string;\n private firstName: string;\n private lastName: string;\n ", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 645, "end": 666, - "replacementText": "ssn: string" + "replacementText": "ssn: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 676, "end": 701, - "replacementText": "firstName: string" + "replacementText": "firstName: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 711, "end": 735, - "replacementText": "lastName: string" + "replacementText": "lastName: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { - "start": 742, - "end": 840, - "replacementText": "{\n this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n}" + "start": 752, + "end": 752, + "replacementText": "this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n ", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 } ], "suggest": "", @@ -98,60 +138,52 @@ { "start": 624, "end": 624, - "replacementText": "protected ssn: string;\nprivate firstName: string;\nprivate lastName: string;\n" + "replacementText": "protected ssn: string;\n private firstName: string;\n private lastName: string;\n ", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 645, "end": 666, - "replacementText": "ssn: string" + "replacementText": "ssn: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 676, "end": 701, - "replacementText": "firstName: string" + "replacementText": "firstName: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { "start": 711, "end": 735, - "replacementText": "lastName: string" + "replacementText": "lastName: string", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 }, { - "start": 742, - "end": 840, - "replacementText": "{\n this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n}" + "start": 752, + "end": 752, + "replacementText": "this.ssn = ssn;\n this.firstName = firstName;\n this.lastName = lastName;\n ", + "line": 20, + "column": 9, + "endLine": 20, + "endColumn": 16 } ], "suggest": "", "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", "severity": "ERROR" - }, - { - "line": 49, - "column": 17, - "endLine": 49, - "endColumn": 30, - "problem": "ParameterProperties", - "autofix": [ - { - "start": 1301, - "end": 1301, - "replacementText": "readonly a: A;\n" - }, - { - "start": 1313, - "end": 1326, - "replacementText": "a: A" - }, - { - "start": 1328, - "end": 1353, - "replacementText": "{\n this.a = a;\n this.a = a;\n}" - } - ], - "suggest": "", - "rule": "Declaring fields in \"constructor\" is not supported (arkts-no-ctor-prop-decls)", - "severity": "ERROR", - "exclusive": "SDK" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/rules/rule25.ets.migrate.ets b/ets2panda/linter/test/rules/rule25.ets.migrate.ets index 51395c45d2..94d37a9bf7 100644 --- a/ets2panda/linter/test/rules/rule25.ets.migrate.ets +++ b/ets2panda/linter/test/rules/rule25.ets.migrate.ets @@ -15,20 +15,20 @@ class Person { protected ssn: string; -private firstName: string; -private lastName: string; -constructor( + private firstName: string; + private lastName: string; + constructor( ssn: string, firstName: string, lastName: string ) { - this.ssn = ssn; - this.firstName = firstName; - this.lastName = lastName; - this.ssn = ssn; - this.firstName = firstName; - this.lastName = lastName; -} + this.ssn = ssn; + this.firstName = firstName; + this.lastName = lastName; + this.ssn = ssn + this.firstName = firstName + this.lastName = lastName + } getFullName(): string { return this.firstName + " " + this.lastName -- Gitee