已合并
instanceof+interface unexpected narrows to never #11175
instanceof+interface unexpected narrows to never #11175
已合并
ajq257257创建于 14 天前
8 个文件变更+223-5
@@ -1176,7 +1176,7 @@ static void ReportIfInstanceofTrivialKnown(ETSChecker *checker, Type *lhs, Type
1176 checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN,1176 checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN,
1177 util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_TRUE_WORD}, pos);1177 util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_TRUE_WORD}, pos);
1178 }1178 }
1179- } else if (!HasInterfaceConstituent(lhs) || !HasInterfaceConstituent(rhs)) {1179+ } else if (!HasInterfaceConstituent(lhs) && !HasInterfaceConstituent(rhs)) {
1180 checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN,1180 checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN,
1181 util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_FALSE_WORD}, pos);1181 util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_FALSE_WORD}, pos);
1182 }1182 }
@@ -1508,6 +1508,12 @@ std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSObjectType *te
1508 return {testedType, GetGlobalTypesHolder()->GlobalETSNeverType()};1508 return {testedType, GetGlobalTypesHolder()->GlobalETSNeverType()};
1509 }1509 }
1510 1510 
1511+ // When neither type is a supertype of the other, instanceof can still succeed if
1512+ // at least one type is an interface.
1513+ if (testedType->IsInterface() || objectType->IsInterface()) {
1514+ return {testedType, actualType};
1515+ }
1516+ 
1511 return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType};1517 return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType};
1512 }1518 }
1513 1519 
@@ -0,0 +1,83 @@
1+/**
2+ * Copyright (c) 2026 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+ 
16+// Test that instanceof between an interface-typed variable and an unrelated
17+// class does NOT produce "always false" warning (W1001506), because a class
18+// implementing the interface may also extend the tested class.
19+ 
20+interface I {
21+ foo(): void
22+}
23+ 
24+class A {
25+ bar(): void {}
26+}
27+ 
28+class C extends A implements I {
29+ foo(): void {}
30+}
31+ 
32+class D {}
33+ 
34+// interface variable instanceof unrelated class — no warning
35+function ifaceToClass(i: I): boolean {
36+ return i instanceof A
37+}
38+ 
39+// interface variable instanceof unrelated interface — no warning
40+function ifaceToIface(i: I): boolean {
41+ return i instanceof D
42+}
43+ 
44+// class variable instanceof unrelated interface — no warning
45+function classToIface(a: A): boolean {
46+ return a instanceof I
47+}
48+ 
49+// class variable instanceof unrelated class — SHOULD warn "always false"
50+function classToClass(a: A): boolean {
51+ return a instanceof D
52+}
53+ 
54+// Smart narrowing: after if (i instanceof A), i is narrowed to A,
55+// so i instanceof A is "always true"
56+function smartNarrowTrue(i: I): boolean {
57+ if (i instanceof A) {
58+ return i instanceof A
59+ }
60+ return false
61+}
62+ 
63+// Smart narrowing: after if (i instanceof A), i is narrowed to A,
64+// then i instanceof D (unrelated class to A) is "always false"
65+function smartNarrowFalse(i: I): boolean {
66+ if (i instanceof A) {
67+ return i instanceof D
68+ }
69+ return false
70+}
71+ 
72+// Smart narrowing: after if (i instanceof A), i is narrowed to A,
73+// then i instanceof I is NOT "always true"
74+function smartNarrowBackToIface(i: I): boolean {
75+ if (i instanceof A) {
76+ return i instanceof I
77+ }
78+ return false
79+}
80+ 
81+/* @@? 51:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */
82+/* @@? 58:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
83+/* @@? 67:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */
@@ -26,6 +26,5 @@ function qux(x: A): boolean {
26 return false26 return false
27}27}
28 28 
29-/* @@? 21:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
30/* @@? 22:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */29/* @@? 22:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
31/* @@? 23:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */30/* @@? 23:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
@@ -48,5 +48,4 @@ function quz(x: C): boolean {
48 48 
49/* @@? 30:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */49/* @@? 30:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
50/* @@? 34:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */50/* @@? 34:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */
51-/* @@? 43:21 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
52/* @@? 44:21 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */51/* @@? 44:21 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
@@ -61,7 +61,6 @@ function quux(x: string | undefined): number {
61 return x.length + (t1 ? 1 : 0) + (t2 ? 1 : 0)61 return x.length + (t1 ? 1 : 0) + (t2 ? 1 : 0)
62}62}
63 63 
64-/* @@? 31:9 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */
65/* @@? 32:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */64/* @@? 32:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
66/* @@? 39:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */65/* @@? 39:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */
67/* @@? 46:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */66/* @@? 46:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */
@@ -62,7 +62,6 @@ class C<T> {
62}62}
63 63 
64 64 
65-/* @@? 20:8 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */
66/* @@? 20:28 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */65/* @@? 20:28 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */
67/* @@? 36:9 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */66/* @@? 36:9 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */
68/* @@? 36:22 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */67/* @@? 36:22 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */
@@ -0,0 +1,133 @@
1+/*
2+ * Copyright (c) 2026 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+ 
16+interface IBase {
17+ getVal(): int
18+}
19+ 
20+interface IUnrelated {}
21+ 
22+class Base {
23+ field: int = 0
24+}
25+ 
26+class Derived extends Base implements IBase {
27+ constructor(v: int) {
28+ super()
29+ this.field = v
30+ }
31+ getVal(): int {
32+ return this.field
33+ }
34+}
35+ 
36+class Other {}
37+ 
38+// Case 1: interface variable holding a Derived instance, instanceof a Base class
39+function testInterfaceToClass(): int {
40+ let i: IBase = new Derived(42)
41+ let b: Base
42+ try {
43+ if (i instanceof Base) {
44+ b = i as Base
45+ return b.field
46+ }
47+ } catch (e) {
48+ return -1
49+ }
50+ return -2
51+}
52+ 
53+// Case 2: interface variable, instanceof an unrelated interface
54+function testInterfaceToInterface(): int {
55+ let i: IBase = new Derived(10)
56+ try {
57+ if (i instanceof IUnrelated) {
58+ return -1
59+ }
60+ return i.getVal()
61+ } catch (e) {
62+ return -2
63+ }
64+}
65+ 
66+// Case 3: class variable, instanceof an interface
67+function testClassToInterface(): int {
68+ let b: Base = new Derived(99)
69+ try {
70+ if (b instanceof IBase) {
71+ let i: IBase = b as IBase
72+ return i.getVal()
73+ }
74+ } catch (e) {
75+ return -1
76+ }
77+ return -2
78+}
79+ 
80+// Case 4: interface variable, instanceof truly unrelated class
81+function testInterfaceToUnrelatedClass(): int {
82+ let i: IBase = new Derived(5)
83+ if (i instanceof Other) {
84+ return -1
85+ }
86+ return i.getVal()
87+}
88+ 
89+// Case 5: Diamond-like — two interfaces, one class implementing both
90+interface IA {
91+ a(): int
92+}
93+interface IB {
94+ b(): int
95+}
96+class Diamond implements IA, IB {
97+ a(): int { return 1 }
98+ b(): int { return 2 }
99+}
100+ 
101+function testDiamondInterfaceNarrowing(): int {
102+ let x: IA = new Diamond()
103+ try {
104+ if (x instanceof IB) {
105+ let y: IB = x as IB
106+ return y.b()
107+ }
108+ } catch (e) {
109+ return -1
110+ }
111+ return -2
112+}
113+ 
114+// Case 6: instanceof in else-if chain with interface narrowing
115+function testElseIfChain(): int {
116+ let x: Object = new Derived(77)
117+ if (x instanceof IBase) {
118+ let i: IBase = x as IBase
119+ return i.getVal()
120+ } else if (x instanceof Other) {
121+ return -1
122+ }
123+ return -2
124+}
125+ 
126+function main(): void {
127+ arktest.assertEQ(testInterfaceToClass(), 42)
128+ arktest.assertEQ(testInterfaceToInterface(), 10)
129+ arktest.assertEQ(testClassToInterface(), 99)
130+ arktest.assertEQ(testInterfaceToUnrelatedClass(), 5)
131+ arktest.assertEQ(testDiamondInterfaceNarrowing(), 2)
132+ arktest.assertEQ(testElseIfChain(), 77)
133+}