1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import path from 'path'; 17import { 18 AbstractFieldRef, 19 ArkAssignStmt, 20 ArkCastExpr, 21 ArkField, 22 ArkIfStmt, 23 ArkInstanceFieldRef, 24 ArkInstanceOfExpr, 25 ArkMethod, 26 ArkNamespace, 27 ArkNormalBinopExpr, 28 ArkParameterRef, 29 ArkUnopExpr, 30 BasicBlock, 31 CallGraph, 32 Cfg, 33 ClassSignature, 34 classSignatureCompare, 35 ClassType, 36 DVFGBuilder, 37 FieldSignature, 38 fileSignatureCompare, 39 LineColPosition, 40 Local, 41 NormalBinaryOperator, 42 RelationalBinaryOperator, 43 Scene, 44 Stmt, 45 UnaryOperator, 46 Value, 47} from 'arkanalyzer/lib'; 48import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger'; 49import { BaseChecker, BaseMetaData } from '../BaseChecker'; 50import { Defects, MatcherCallback, Rule } from '../../Index'; 51import { IssueReport } from '../../model/Defects'; 52import { DVFG, DVFGNode } from 'arkanalyzer/lib/VFG/DVFG'; 53import { CALL_DEPTH_LIMIT, getGlobalsDefineInDefaultMethod, getLineAndColumn, GlobalCallGraphHelper } from './Utils'; 54import { ClassCategory } from 'arkanalyzer/lib/core/model/ArkClass'; 55import { Language } from 'arkanalyzer/lib/core/model/ArkFile'; 56import { BooleanConstant, NumberConstant } from 'arkanalyzer/lib/core/base/Constant'; 57import { ArkClass, NumberType } from 'arkanalyzer'; 58 59const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'NoTSLikeAsCheck'); 60const gMetaData: BaseMetaData = { 61 severity: 1, 62 ruleDocPath: '', 63 description: '', 64}; 65 66enum TypeAssuranceCondition { 67 Positive, 68 Negative, 69 NotExist, 70} 71 72export class NoTSLikeAsCheck implements BaseChecker { 73 readonly metaData: BaseMetaData = gMetaData; 74 public rule: Rule; 75 public defects: Defects[] = []; 76 public issues: IssueReport[] = []; 77 private scene: Scene; 78 private cg: CallGraph; 79 private dvfg: DVFG; 80 private dvfgBuilder: DVFGBuilder; 81 private visited: Set<ArkMethod> = new Set(); 82 83 public registerMatchers(): MatcherCallback[] { 84 const matchBuildCb: MatcherCallback = { 85 matcher: undefined, 86 callback: this.check, 87 }; 88 return [matchBuildCb]; 89 } 90 91 public check = (scene: Scene): void => { 92 this.scene = scene; 93 this.cg = GlobalCallGraphHelper.getCGInstance(scene); 94 95 this.dvfg = new DVFG(this.cg); 96 this.dvfgBuilder = new DVFGBuilder(this.dvfg, scene); 97 98 for (let arkFile of scene.getFiles()) { 99 // 此规则仅对arkts1.1和arkts1.2进行检查,typescript在编译阶段会报错,javascript没有类型断言语法 100 if (!(arkFile.getLanguage() === Language.ARKTS1_1 || arkFile.getLanguage() === Language.ARKTS1_2)) { 101 continue; 102 } 103 const defaultMethod = arkFile.getDefaultClass().getDefaultArkMethod(); 104 let globalVarMap: Map<string, Stmt[]> = new Map(); 105 if (defaultMethod) { 106 this.dvfgBuilder.buildForSingleMethod(defaultMethod); 107 globalVarMap = getGlobalsDefineInDefaultMethod(defaultMethod); 108 } 109 for (let clazz of arkFile.getClasses()) { 110 this.processClass(clazz, globalVarMap); 111 } 112 for (let namespace of arkFile.getAllNamespacesUnderThisFile()) { 113 this.processNameSpace(namespace, globalVarMap); 114 } 115 } 116 }; 117 118 public processClass(arkClass: ArkClass, globalVarMap: Map<string, Stmt[]>): void { 119 for (let field of arkClass.getFields()) { 120 this.processClassField(field, globalVarMap); 121 } 122 for (let mtd of arkClass.getMethods()) { 123 this.processArkMethod(mtd, globalVarMap); 124 } 125 } 126 127 public processNameSpace(namespace: ArkNamespace, globalVarMap: Map<string, Stmt[]>): void { 128 for (let ns of namespace.getNamespaces()) { 129 this.processNameSpace(ns, globalVarMap); 130 } 131 for (let clazz of namespace.getClasses()) { 132 this.processClass(clazz, globalVarMap); 133 } 134 } 135 136 public processClassField(field: ArkField, globalVarMap: Map<string, Stmt[]>): void { 137 const instInit = field.getDeclaringArkClass().getInstanceInitMethod(); 138 this.processArkMethod(instInit, globalVarMap); 139 } 140 141 public processArkMethod(target: ArkMethod, globalVarMap: Map<string, Stmt[]>): void { 142 const stmts = target.getBody()?.getCfg().getStmts() ?? []; 143 for (const stmt of stmts) { 144 // cast表达式所在语句为sink点,从该点开始进行逆向数据流分析 145 const castExpr = this.getCastExpr(stmt); 146 if (castExpr === null) { 147 continue; 148 } 149 150 // 判断是否为cast表达式的自增自减运算,属于告警场景之一 151 if (this.isCastExprWithIncrementDecrement(stmt)) { 152 this.addIssueReport(stmt, castExpr, undefined, true); 153 continue; 154 } 155 156 // 判断cast类型断言的类型是否是class,非class的场景不在本规则检查范围内 157 if (!(castExpr.getType() instanceof ClassType)) { 158 continue; 159 } 160 if (this.hasCheckedWithInstanceof(stmt.getCfg(), stmt)) { 161 continue; 162 } 163 if (!this.visited.has(target)) { 164 this.dvfgBuilder.buildForSingleMethod(target); 165 this.visited.add(target); 166 } 167 168 let checkAll = { value: true }; 169 let visited: Set<Stmt> = new Set(); 170 const result = this.checkFromStmt(stmt, globalVarMap, checkAll, visited); 171 if (result !== null) { 172 this.addIssueReport(stmt, castExpr, result); 173 } else { 174 if (!checkAll.value) { 175 this.addIssueReport(stmt, castExpr); 176 } 177 } 178 } 179 } 180 181 private isCastExprWithIncrementDecrement(stmt: Stmt): boolean { 182 if (!(stmt instanceof ArkAssignStmt) || !(stmt.getRightOp() instanceof ArkCastExpr)) { 183 return false; 184 } 185 const castLocal = stmt.getLeftOp(); 186 if (!(castLocal instanceof Local)) { 187 return false; 188 } 189 // 判断是否为自增或自减语句,需要判断used stmt是否至少包含%0 = %0 + 1 和 castExpr = %0两条语句,不新增临时变量 190 // 非自增或自减语句,used stmt中仅包含%1 = %0 + 1 191 const usedStmts = castLocal.getUsedStmts(); 192 if (usedStmts.length !== 2) { 193 return false; 194 } 195 let selfAssignFlag = false; 196 let assignBackFlag = false; 197 for (const usedStmt of usedStmts) { 198 if (!(usedStmt instanceof ArkAssignStmt)) { 199 return false; 200 } 201 const leftOp = usedStmt.getLeftOp(); 202 const rightOp = usedStmt.getRightOp(); 203 if (leftOp instanceof Local) { 204 if (leftOp !== castLocal) { 205 return false; 206 } 207 if (!(rightOp instanceof ArkNormalBinopExpr)) { 208 return false; 209 } 210 const op1 = rightOp.getOp1(); 211 const op2 = rightOp.getOp2(); 212 const operator = rightOp.getOperator(); 213 if (op1 !== castLocal) { 214 return false; 215 } 216 if (operator !== NormalBinaryOperator.Addition && operator !== NormalBinaryOperator.Subtraction) { 217 return false; 218 } 219 if (!(op2 instanceof NumberConstant) || !(op2.getType() instanceof NumberType) || op2.getValue() !== '1') { 220 return false; 221 } 222 selfAssignFlag = true; 223 } 224 if (leftOp instanceof ArkCastExpr) { 225 if (leftOp !== stmt.getRightOp()) { 226 return false; 227 } 228 if (rightOp !== castLocal) { 229 return false; 230 } 231 assignBackFlag = true; 232 } 233 } 234 return selfAssignFlag && assignBackFlag; 235 } 236 237 private hasCheckedWithInstanceof(cfg: Cfg, stmt: Stmt): boolean { 238 const castExpr = this.getCastExpr(stmt); 239 if (castExpr === null) { 240 return false; 241 } 242 for (const block of cfg.getBlocks()) { 243 // 这里仅判断了cast op是否进行了instanceof判断,如果op是由op1赋值,op1进行了instanceof判断,此处不认为是做了有效检查,因为此赋值链可能很长且中途发生类型变化,极易判断错误 244 const checkRes = this.checkTypeAssuranceInBasicBlock(block, castExpr); 245 if (checkRes === TypeAssuranceCondition.NotExist) { 246 continue; 247 } 248 let checkedBB: Set<number> = new Set<number>(); 249 let needCheckBB: number[] = []; 250 checkedBB.add(block.getId()); 251 const allSuccessors = block.getSuccessors(); 252 if (allSuccessors.length > 0 && checkRes === TypeAssuranceCondition.Positive) { 253 needCheckBB.push(allSuccessors[0].getId()); 254 } 255 if (allSuccessors.length > 1 && checkRes === TypeAssuranceCondition.Negative) { 256 needCheckBB.push(allSuccessors[1].getId()); 257 } 258 while (needCheckBB.length > 0) { 259 const bbId = needCheckBB.shift(); 260 if (bbId === undefined) { 261 break; 262 } 263 if (checkedBB.has(bbId)) { 264 continue; 265 } 266 checkedBB.add(bbId); 267 const bb = this.getBlockWithId(bbId, cfg); 268 if (bb === null) { 269 continue; 270 } 271 if (this.isStmtInBlock(stmt, bb)) { 272 return true; 273 } else { 274 bb.getSuccessors().forEach(b => needCheckBB.push(b.getId())); 275 } 276 } 277 } 278 return false; 279 } 280 281 private checkTypeAssuranceInBasicBlock(bb: BasicBlock, castExpr: ArkCastExpr): TypeAssuranceCondition { 282 for (const stmt of bb.getStmts()) { 283 if (!(stmt instanceof ArkIfStmt)) { 284 continue; 285 } 286 const conditionExpr = stmt.getConditionExpr(); 287 const op1 = conditionExpr.getOp1(); 288 const op2 = conditionExpr.getOp2(); 289 const operator = conditionExpr.getOperator(); 290 // 对于if (i instanceof A)这种条件语句,op1总是临时变量,op2总是false,操作符总是!= 291 if (!(op1 instanceof Local && op2 instanceof BooleanConstant && op2.getValue() === 'false' && operator === RelationalBinaryOperator.InEquality)) { 292 break; 293 } 294 return this.checkTypeAssuranceWithLocal(op1, castExpr, stmt.getOriginPositionInfo(), true); 295 } 296 return TypeAssuranceCondition.NotExist; 297 } 298 299 private checkTypeAssuranceWithLocal(operand: Local, castExpr: ArkCastExpr, ifStmtPos: LineColPosition, shouldBe: boolean): TypeAssuranceCondition { 300 const declaringStmt = operand.getDeclaringStmt(); 301 if (declaringStmt === null) { 302 return TypeAssuranceCondition.NotExist; 303 } 304 // if语句中的所有条件遵从三地址码原则拆分成多个语句时,所有语句的位置信息是一致的,不一致时表示是条件语句之前的赋值或声明情况,不在本判断范围内 305 const stmtPos = declaringStmt.getOriginPositionInfo(); 306 if (stmtPos.getLineNo() !== ifStmtPos.getLineNo() || stmtPos.getColNo() !== ifStmtPos.getColNo()) { 307 return TypeAssuranceCondition.NotExist; 308 } 309 if (!(declaringStmt instanceof ArkAssignStmt)) { 310 return TypeAssuranceCondition.NotExist; 311 } 312 const rightOp = declaringStmt.getRightOp(); 313 if (rightOp instanceof ArkInstanceOfExpr) { 314 if (this.isTypeAssuranceMatchCast(rightOp, castExpr)) { 315 if (shouldBe) { 316 return TypeAssuranceCondition.Positive; 317 } else { 318 return TypeAssuranceCondition.Negative; 319 } 320 } 321 return TypeAssuranceCondition.NotExist; 322 } 323 if (rightOp instanceof ArkUnopExpr && rightOp.getOperator() === UnaryOperator.LogicalNot) { 324 const unaryOp = rightOp.getOp(); 325 if (unaryOp instanceof Local) { 326 return this.checkTypeAssuranceWithLocal(unaryOp, castExpr, ifStmtPos, !shouldBe); 327 } 328 return TypeAssuranceCondition.NotExist; 329 } 330 if (rightOp instanceof ArkNormalBinopExpr) { 331 const op1 = rightOp.getOp1(); 332 const op2 = rightOp.getOp2(); 333 const operator = rightOp.getOperator(); 334 // 这里仅判断&&和||两种逻辑运算符的场景,其他场景在包含类型守卫判断的条件语句中不常见,暂不考虑 335 let res: TypeAssuranceCondition; 336 if (operator === NormalBinaryOperator.LogicalAnd) { 337 if (op1 instanceof Local) { 338 res = this.checkTypeAssuranceWithLocal(op1, castExpr, ifStmtPos, shouldBe); 339 if (res !== TypeAssuranceCondition.NotExist) { 340 return res; 341 } 342 } 343 if (op2 instanceof Local) { 344 res = this.checkTypeAssuranceWithLocal(op2, castExpr, ifStmtPos, shouldBe); 345 if (res !== TypeAssuranceCondition.NotExist) { 346 return res; 347 } 348 } 349 return TypeAssuranceCondition.NotExist; 350 } 351 if (operator === NormalBinaryOperator.LogicalOr) { 352 // a or b,不论a或b里是类型守卫判断,均无法保证分支中的类型明确 353 if (shouldBe) { 354 return TypeAssuranceCondition.NotExist; 355 } 356 } 357 } 358 return TypeAssuranceCondition.NotExist; 359 } 360 361 private isTypeAssuranceMatchCast(instanceOfExpr: ArkInstanceOfExpr, castExpr: ArkCastExpr): boolean { 362 const castOp = castExpr.getOp(); 363 const castType = castExpr.getType(); 364 const instanceofType = instanceOfExpr.getCheckType(); 365 if (castType.getTypeString() !== instanceofType.getTypeString()) { 366 return false; 367 } 368 const instanceofOp = instanceOfExpr.getOp(); 369 if (!(castOp instanceof Local && instanceofOp instanceof Local)) { 370 return false; 371 } 372 return castOp.getName() === instanceofOp.getName(); 373 } 374 375 private isStmtInBlock(stmt: Stmt, block: BasicBlock): boolean { 376 for (const s of block.getStmts()) { 377 if (s === stmt) { 378 return true; 379 } 380 } 381 return false; 382 } 383 384 private getBlockWithId(id: number, cfg: Cfg): BasicBlock | null { 385 const blocks = cfg.getBlocks(); 386 for (const bb of blocks) { 387 if (bb.getId() === id) { 388 return bb; 389 } 390 } 391 return null; 392 } 393 394 private checkFromStmt(stmt: Stmt, globalVarMap: Map<string, Stmt[]>, checkAll: { value: boolean }, visited: Set<Stmt>, depth: number = 0): Stmt | null { 395 if (depth > CALL_DEPTH_LIMIT) { 396 checkAll.value = false; 397 return null; 398 } 399 const node = this.dvfg.getOrNewDVFGNode(stmt); 400 let worklist: DVFGNode[] = [node]; 401 while (worklist.length > 0) { 402 const current = worklist.shift()!; 403 const currentStmt = current.getStmt(); 404 if (visited.has(currentStmt)) { 405 continue; 406 } 407 visited.add(currentStmt); 408 if (this.isWithInterfaceAnnotation(currentStmt)) { 409 return currentStmt; 410 } 411 412 const fieldDeclareStmt = this.isCastOpFieldWithInterfaceType(currentStmt); 413 if (fieldDeclareStmt) { 414 return fieldDeclareStmt; 415 } 416 const gv = this.checkIfCastOpIsGlobalVar(currentStmt); 417 if (gv) { 418 const globalDefs = globalVarMap.get(gv.getName()); 419 if (globalDefs === undefined) { 420 const importValue = this.checkIfCastOpIsFromImport(currentStmt); 421 if (importValue && importValue.getDeclaringStmt() !== null) { 422 worklist.push(this.dvfg.getOrNewDVFGNode(importValue.getDeclaringStmt()!)); 423 } 424 } else { 425 globalDefs.forEach(d => worklist.push(this.dvfg.getOrNewDVFGNode(d))); 426 } 427 continue; 428 } 429 430 const callsite = this.cg.getCallSiteByStmt(currentStmt); 431 for (const cs of callsite) { 432 const declaringMtd = this.cg.getArkMethodByFuncID(cs.calleeFuncID); 433 if (!declaringMtd || !declaringMtd.getCfg()) { 434 continue; 435 } 436 if (!this.visited.has(declaringMtd)) { 437 this.dvfgBuilder.buildForSingleMethod(declaringMtd); 438 this.visited.add(declaringMtd); 439 } 440 const returnStmts = declaringMtd.getReturnStmt(); 441 for (const stmt of returnStmts) { 442 const res = this.checkFromStmt(stmt, globalVarMap, checkAll, visited, depth + 1); 443 if (res !== null) { 444 return res; 445 } 446 } 447 } 448 const paramRef = this.isFromParameter(currentStmt); 449 if (paramRef) { 450 const paramIdx = paramRef.getIndex(); 451 const callsites = this.cg.getInvokeStmtByMethod(currentStmt.getCfg().getDeclaringMethod().getSignature()); 452 this.processCallsites(callsites); 453 const argDefs = this.collectArgDefs(paramIdx, callsites); 454 for (const stmt of argDefs) { 455 const res = this.checkFromStmt(stmt, globalVarMap, checkAll, visited, depth + 1); 456 if (res !== null) { 457 return res; 458 } 459 } 460 } 461 current.getIncomingEdge().forEach(e => worklist.push(e.getSrcNode() as DVFGNode)); 462 } 463 return null; 464 } 465 466 private isCastOpFieldWithInterfaceType(stmt: Stmt): Stmt | undefined { 467 const obj = this.getCastOp(stmt); 468 if (obj === null || !(obj instanceof Local)) { 469 return undefined; 470 } 471 const declaringStmt = obj.getDeclaringStmt(); 472 if (declaringStmt === null || !(declaringStmt instanceof ArkAssignStmt)) { 473 return undefined; 474 } 475 const rightOp = declaringStmt.getRightOp(); 476 if (!(rightOp instanceof AbstractFieldRef)) { 477 return undefined; 478 } 479 const fieldDeclaring = rightOp.getFieldSignature().getDeclaringSignature(); 480 if (fieldDeclaring instanceof ClassSignature) { 481 const field = this.scene.getClass(fieldDeclaring)?.getField(rightOp.getFieldSignature()); 482 if (!field) { 483 return undefined; 484 } 485 const fieldInitializer = field.getInitializer(); 486 const lastStmt = fieldInitializer[fieldInitializer.length - 1]; 487 if (this.isWithInterfaceAnnotation(lastStmt)) { 488 return lastStmt; 489 } 490 } 491 return undefined; 492 } 493 494 private checkIfCastOpIsGlobalVar(stmt: Stmt): Local | undefined { 495 const obj = this.getCastOp(stmt); 496 if (obj instanceof Local && !obj.getDeclaringStmt()) { 497 return obj; 498 } 499 return undefined; 500 } 501 502 private checkIfCastOpIsFromImport(stmt: Stmt): Local | undefined { 503 const obj = this.getCastOp(stmt); 504 if (obj === null || !(obj instanceof Local)) { 505 return undefined; 506 } 507 const importInfos = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile().getImportInfos(); 508 for (const importInfo of importInfos) { 509 if (importInfo.getImportClauseName() === obj.getName()) { 510 const exportInfo = importInfo.getLazyExportInfo(); 511 if (exportInfo === null) { 512 return undefined; 513 } 514 const arkExport = exportInfo.getArkExport(); 515 if (arkExport === null || arkExport === undefined) { 516 return undefined; 517 } 518 if (!(arkExport instanceof Local)) { 519 return undefined; 520 } 521 return arkExport; 522 } 523 } 524 return undefined; 525 } 526 527 private processCallsites(callsites: Stmt[]): void { 528 callsites.forEach(cs => { 529 const declaringMtd = cs.getCfg().getDeclaringMethod(); 530 if (!this.visited.has(declaringMtd)) { 531 this.dvfgBuilder.buildForSingleMethod(declaringMtd); 532 this.visited.add(declaringMtd); 533 } 534 }); 535 } 536 537 // 判断语句是否为赋值语句,且左值的类型注解为Interface,右值的类型与左值不一样 538 private isWithInterfaceAnnotation(stmt: Stmt): boolean { 539 if (!(stmt instanceof ArkAssignStmt)) { 540 return false; 541 } 542 const leftOpType = stmt.getLeftOp().getType(); 543 if (!(leftOpType instanceof ClassType)) { 544 return false; 545 } 546 const leftOpTypeclass = this.scene.getClass(leftOpType.getClassSignature()); 547 if (leftOpTypeclass === null) { 548 return false; 549 } 550 if (leftOpTypeclass.getCategory() !== ClassCategory.INTERFACE) { 551 return false; 552 } 553 const rightOpType = stmt.getRightOp().getType(); 554 if (!(rightOpType instanceof ClassType)) { 555 return true; 556 } 557 return !classSignatureCompare(leftOpType.getClassSignature(), rightOpType.getClassSignature()); 558 } 559 560 private isFromParameter(stmt: Stmt): ArkParameterRef | undefined { 561 if (!(stmt instanceof ArkAssignStmt)) { 562 return undefined; 563 } 564 const rightOp = stmt.getRightOp(); 565 if (rightOp instanceof ArkParameterRef) { 566 return rightOp; 567 } 568 return undefined; 569 } 570 571 private getCastOp(stmt: Stmt): Value | null { 572 if (!(stmt instanceof ArkAssignStmt)) { 573 return null; 574 } 575 const rightOp = stmt.getRightOp(); 576 if (!(rightOp instanceof ArkCastExpr)) { 577 return null; 578 } 579 return rightOp.getOp(); 580 } 581 582 private getCastExpr(stmt: Stmt): ArkCastExpr | null { 583 // method中使用as断言的地方可能是body体中,函数调用的实参,返回值,均会表示成ArkAssignStmt 584 if (!(stmt instanceof ArkAssignStmt)) { 585 return null; 586 } 587 const rightOp = stmt.getRightOp(); 588 if (!(rightOp instanceof ArkCastExpr)) { 589 return null; 590 } 591 return rightOp; 592 } 593 594 private collectArgDefs(argIdx: number, callsites: Stmt[]): Stmt[] { 595 const getKey = (v: Value): Value | FieldSignature => { 596 return v instanceof ArkInstanceFieldRef ? v.getFieldSignature() : v; 597 }; 598 return callsites.flatMap(callsite => { 599 const target: Value | FieldSignature = getKey(callsite.getInvokeExpr()!.getArg(argIdx)); 600 return Array.from(this.dvfg.getOrNewDVFGNode(callsite).getIncomingEdge()) 601 .map(e => (e.getSrcNode() as DVFGNode).getStmt()) 602 .filter(s => { 603 return s instanceof ArkAssignStmt && target === getKey(s.getLeftOp()); 604 }); 605 }); 606 } 607 608 private addIssueReport(stmt: Stmt, operand: ArkCastExpr, relatedStmt?: Stmt, incrementCase: boolean = false): void { 609 const severity = this.rule.alert ?? this.metaData.severity; 610 const warnInfo = getLineAndColumn(stmt, operand); 611 const problem = 'As'; 612 const descPrefix = 'The value in type assertion is assigned by value with interface annotation'; 613 let desc = `(${this.rule.ruleId.replace('@migration/', '')})`; 614 615 if (incrementCase) { 616 desc = 'Can not use neither increment nor decrement with cast expression ' + desc; 617 } else if (relatedStmt === undefined) { 618 desc = `Can not check when function call chain depth exceeds ${CALL_DEPTH_LIMIT}, please check it manually ` + desc; 619 } else { 620 const sinkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile(); 621 const relatedFile = relatedStmt.getCfg().getDeclaringMethod().getDeclaringArkFile(); 622 if (fileSignatureCompare(sinkFile.getFileSignature(), relatedFile.getFileSignature())) { 623 desc = `${descPrefix} in Line ${relatedStmt.getOriginPositionInfo().getLineNo()} ` + desc; 624 } else { 625 desc = `${descPrefix} in file ${path.normalize(relatedFile.getName())}: ${relatedStmt.getOriginPositionInfo().getLineNo()} ` + desc; 626 } 627 } 628 629 let defects = new Defects( 630 warnInfo.line, 631 warnInfo.startCol, 632 warnInfo.endCol, 633 problem, 634 desc, 635 severity, 636 this.rule.ruleId, 637 warnInfo.filePath, 638 this.metaData.ruleDocPath, 639 true, 640 false, 641 false 642 ); 643 this.issues.push(new IssueReport(defects, undefined)); 644 } 645} 646