• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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