From fcb5367e4c5e28d9d52a56e6ca19ccb11a21fcf8 Mon Sep 17 00:00:00 2001 From: sefayilmazunal Date: Wed, 13 Aug 2025 15:42:49 +0300 Subject: [PATCH] fix false scan on union-member-access rule Description: false scan issue on arkts-common-union-member-access fixed Issue: ICSU20 Signed-off-by: sefayilmazunal --- ets2panda/linter/src/lib/TypeScriptLinter.ts | 71 ++++++++----------- .../test/main/avoid_using_union_types.ets | 8 ++- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index c2f34353a2..c75fef3a31 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -1828,61 +1828,48 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { if (!this.options.arkts2) { return; } + + // Safeguard: only process the outermost property access, not nested chains + if (ts.isPropertyAccessExpression(propertyAccessNode.parent)) { + return; + } + const baseExprType = this.tsTypeChecker.getTypeAtLocation(propertyAccessNode.expression); const baseExprSym = baseExprType.aliasSymbol || baseExprType.getSymbol(); const symbolName = baseExprSym ? baseExprSym.name : this.tsTypeChecker.typeToString(baseExprType); + if (!baseExprType.isUnion() || COMMON_UNION_MEMBER_ACCESS_WHITELIST.has(symbolName)) { return; } - const allType = baseExprType.types; - const commonPropertyType = allType.filter((type) => { - return this.tsUtils.findProperty(type, propertyAccessNode.name.getText()) !== undefined; + + const allTypes = baseExprType.types; + const propName = propertyAccessNode.name.getText(); + + // Only keep union members that have the property + const typesWithProp = allTypes.filter((type) => { + return this.tsUtils.findProperty(type, propName) !== undefined; }); - const typeMap = new Map(); - if (commonPropertyType.length === allType.length) { - allType.forEach((type) => { - this.handleTypeMember(type, propertyAccessNode.name.getText(), typeMap); - }); - if (typeMap.size > 1) { - this.incrementCounters(propertyAccessNode, FaultID.AvoidUnionTypes); - } - } - } - private handleTypeMember( - type: ts.Type, - memberName: string, - typeMap: Map - ): void { - const propertySymbol = this.tsUtils.findProperty(type, memberName); - if (!propertySymbol?.declarations) { - return; - } - const propertyType = this.tsTypeChecker.getTypeOfSymbolAtLocation(propertySymbol, propertySymbol.declarations[0]); - const symbol = propertySymbol.valueDeclaration; - if (!symbol) { + if (typesWithProp.length !== allTypes.length) { + // Not all members have this property, nothing to check return; } - if (ts.isMethodDeclaration(symbol)) { - const returnType = this.getMethodReturnType(propertySymbol); - typeMap.set(returnType, memberName); - } else { - typeMap.set(propertyType, memberName); - } - } - private getMethodReturnType(symbol: ts.Symbol): string | undefined { - const declaration = symbol.valueDeclaration ?? (symbol.declarations?.[0] as ts.Node | undefined); - if (!declaration) { - return undefined; + // Extract the type of the property for each member + const propTypes: string[] = []; + for (const t of typesWithProp) { + const propSym = this.tsUtils.findProperty(t, propName); + if (propSym) { + const propType = this.tsTypeChecker.getTypeOfSymbolAtLocation(propSym, propertyAccessNode); + propTypes.push(this.tsTypeChecker.typeToString(propType)); + } } - const methodType = this.tsTypeChecker.getTypeOfSymbolAtLocation(symbol, declaration); - const signatures = methodType.getCallSignatures(); - if (signatures.length === 0) { - return 'void'; + + // If there's more than one distinct property type signature, flag it + const distinctPropTypes = new Set(propTypes); + if (distinctPropTypes.size > 1) { + this.incrementCounters(propertyAccessNode, FaultID.AvoidUnionTypes); } - const returnType = signatures[0].getReturnType(); - return this.tsTypeChecker.typeToString(returnType); } private handleLiteralAsPropertyName(node: ts.PropertyDeclaration | ts.PropertySignature): void { diff --git a/ets2panda/linter/test/main/avoid_using_union_types.ets b/ets2panda/linter/test/main/avoid_using_union_types.ets index da1290b868..9b96d9dc4b 100644 --- a/ets2panda/linter/test/main/avoid_using_union_types.ets +++ b/ets2panda/linter/test/main/avoid_using_union_types.ets @@ -44,4 +44,10 @@ class E { tt() {} } let ab: D|E = new D(); -ab.tt(); \ No newline at end of file +ab.tt(); + +declare interface TextPickerResult { + value: string | string[]; +} +const result: TextPickerResult = { value: 'a' }; +result.value.toString() // no error - ok -- Gitee