Skip to content

Commit 26de7c1

Browse files
committed
[index] Fix infinite loop while looking at superclasses
While checking for superclasses in isUnitTest, we need to handle circular inheritance. For good measure, add tests for protocols as well. The new API is designed to behave the same as walkInheritedProtocols except that is walks over superclasses. rdar://49434989
1 parent f720459 commit 26de7c1

File tree

4 files changed

+109
-8
lines changed

4 files changed

+109
-8
lines changed

include/swift/AST/Decl.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3647,6 +3647,19 @@ class ClassDecl final : public NominalTypeDecl {
36473647
Bits.ClassDecl.Circularity = static_cast<unsigned>(circularity);
36483648
}
36493649

3650+
/// Walk this class and all of the superclasses of this class, transitively,
3651+
/// invoking the callback function for each class.
3652+
///
3653+
/// \param fn The callback function that will be invoked for each superclass.
3654+
/// It can return \c Continue to continue the traversal. Returning
3655+
/// \c SkipChildren halts the search and returns \c false, while returning
3656+
/// \c Stop halts the search and returns \c true.
3657+
///
3658+
/// \returns \c true if \c fn returned \c Stop for any class, \c false
3659+
/// otherwise.
3660+
bool walkSuperclasses(
3661+
llvm::function_ref<TypeWalker::Action(ClassDecl *)> fn) const;
3662+
36503663
//// Whether this class requires all of its stored properties to
36513664
//// have initializers in the class definition.
36523665
bool requiresStoredPropertyInits() const {
@@ -3975,8 +3988,8 @@ class ProtocolDecl final : public NominalTypeDecl {
39753988
/// a protocol having nested types (ObjC protocols).
39763989
llvm::TinyPtrVector<AssociatedTypeDecl *> getAssociatedTypeMembers() const;
39773990

3978-
/// Walk all of the protocols inherited by this protocol, transitively,
3979-
/// invoking the callback function for each protocol.
3991+
/// Walk this protocol and all of the protocols inherited by this protocol,
3992+
/// transitively, invoking the callback function for each protocol.
39803993
///
39813994
/// \param fn The callback function that will be invoked for each inherited
39823995
/// protocol. It can return \c Continue to continue the traversal,

lib/AST/Decl.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3794,6 +3794,27 @@ ClassDecl::findImplementingMethod(const AbstractFunctionDecl *Method) const {
37943794
return nullptr;
37953795
}
37963796

3797+
bool ClassDecl::walkSuperclasses(
3798+
llvm::function_ref<TypeWalker::Action(ClassDecl *)> fn) const {
3799+
3800+
SmallPtrSet<ClassDecl *, 8> seen;
3801+
auto *cls = const_cast<ClassDecl *>(this);
3802+
3803+
while (cls && seen.insert(cls).second) {
3804+
switch (fn(cls)) {
3805+
case TypeWalker::Action::Stop:
3806+
return true;
3807+
case TypeWalker::Action::SkipChildren:
3808+
return false;
3809+
case TypeWalker::Action::Continue:
3810+
cls = cls->getSuperclassDecl();
3811+
continue;
3812+
}
3813+
}
3814+
3815+
return false;
3816+
}
3817+
37973818
EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc,
37983819
ArrayRef<EnumElementDecl *> Elements,
37993820
DeclContext *DC) {

lib/Index/IndexSymbol.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ static NominalTypeDecl *getNominalParent(const ValueDecl *D) {
2828
static bool isUnitTestCase(const ClassDecl *D) {
2929
if (!D)
3030
return false;
31-
while (auto *SuperD = D->getSuperclassDecl()) {
32-
if (SuperD->getNameStr() == "XCTestCase")
33-
return true;
34-
D = SuperD;
35-
}
36-
return false;
31+
32+
return D->walkSuperclasses([D](ClassDecl *SuperD) {
33+
if (SuperD != D && // Do not treate XCTestCase itself as a test.
34+
SuperD->getNameStr() == "XCTestCase")
35+
return TypeWalker::Action::Stop; // Found test; stop and return true.
36+
return TypeWalker::Action::Continue;
37+
});
3738
}
3839

3940
static bool isUnitTest(const ValueDecl *D) {

test/Index/circular.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s
2+
3+
class SelfCycle : SelfCycle {}
4+
// CHECK: [[@LINE-1]]:7 | class/Swift | SelfCycle | {{[^ ]*}} | Def | rel: 0
5+
// CHECK: [[@LINE-2]]:19 | class/Swift | SelfCycle | {{[^ ]*}} | Ref,RelBase | rel: 1
6+
// CHECK: RelBase | class/Swift | SelfCycle | {{\W*}}
7+
8+
class Cycle1_A: Cycle1_B {}
9+
// CHECK: [[@LINE-1]]:7 | class/Swift | Cycle1_A | {{[^ ]*}} | Def | rel: 0
10+
// CHECK: [[@LINE-2]]:17 | class/Swift | Cycle1_B | {{[^ ]*}} | Ref,RelBase | rel: 1
11+
// CHECK: RelBase | class/Swift | Cycle1_A | {{[^ ]*}}
12+
class Cycle1_B: Cycle1_A {}
13+
// CHECK: [[@LINE-1]]:7 | class/Swift | Cycle1_B | {{[^ ]*}} | Def | rel: 0
14+
// CHECK: [[@LINE-2]]:17 | class/Swift | Cycle1_A | {{[^ ]*}} | Ref,RelBase | rel: 1
15+
// CHECK: RelBase | class/Swift | Cycle1_B | {{[^ ]*}}
16+
17+
class Cycle2_A: Cycle2_C {}
18+
// CHECK: [[@LINE-1]]:7 | class/Swift | Cycle2_A | {{[^ ]*}} | Def | rel: 0
19+
// CHECK: [[@LINE-2]]:17 | class/Swift | Cycle2_C | {{[^ ]*}} | Ref,RelBase | rel: 1
20+
// CHECK: RelBase | class/Swift | Cycle2_A | {{[^ ]*}}
21+
class Cycle2_B: Cycle2_A {}
22+
// CHECK: [[@LINE-1]]:7 | class/Swift | Cycle2_B | {{[^ ]*}} | Def | rel: 0
23+
// CHECK: [[@LINE-2]]:17 | class/Swift | Cycle2_A | {{[^ ]*}} | Ref,RelBase | rel: 1
24+
// CHECK: RelBase | class/Swift | Cycle2_B | {{[^ ]*}}
25+
class Cycle2_C: Cycle2_B {}
26+
// CHECK: [[@LINE-1]]:7 | class/Swift | Cycle2_C | {{[^ ]*}} | Def | rel: 0
27+
// CHECK: [[@LINE-2]]:17 | class/Swift | Cycle2_B | {{[^ ]*}} | Ref,RelBase | rel: 1
28+
// CHECK: RelBase | class/Swift | Cycle2_C | {{[^ ]*}}
29+
30+
class TestCase1: XCTestCase {}
31+
// CHECK: [[@LINE-1]]:7 | class(test)/Swift | TestCase1 | {{[^ ]*}} | Def | rel: 0
32+
// CHECK: [[@LINE-2]]:18 | class/Swift | XCTestCase | {{[^ ]*}} | Ref,RelBase | rel: 1
33+
// CHECK: RelBase | class(test)/Swift | TestCase1 | {{[^ ]*}}
34+
class XCTestCase: TestCase1 {}
35+
// CHECK: [[@LINE-1]]:7 | class/Swift | XCTestCase | {{[^ ]*}} | Def | rel: 0
36+
// CHECK: [[@LINE-2]]:19 | class(test)/Swift | TestCase1 | {{[^ ]*}} | Ref,RelBase | rel: 1
37+
// CHECK: RelBase | class/Swift | XCTestCase | {{[^ ]*}}
38+
class TestCase2: TestCase1 {}
39+
// CHECK: [[@LINE-1]]:7 | class(test)/Swift | TestCase2 | {{[^ ]*}} | Def | rel: 0
40+
// CHECK: [[@LINE-2]]:18 | class(test)/Swift | TestCase1 | {{[^ ]*}} | Ref,RelBase | rel: 1
41+
// CHECK: RelBase | class(test)/Swift | TestCase2 | {{[^ ]*}}
42+
43+
protocol SelfCycleP: SelfCycleP {}
44+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | SelfCycleP | {{[^ ]*}} | Def | rel: 0
45+
// CHECK: [[@LINE-2]]:22 | protocol/Swift | SelfCycleP | {{[^ ]*}} | Ref,RelBase | rel: 1
46+
// CHECK: RelBase | protocol/Swift | SelfCycleP | {{[^ ]*}}
47+
protocol Cycle1P_A: Cycle1P_B {}
48+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | Cycle1P_A | {{[^ ]*}} | Def | rel: 0
49+
// CHECK: [[@LINE-2]]:21 | protocol/Swift | Cycle1P_B | {{[^ ]*}} | Ref,RelBase | rel: 1
50+
// CHECK: RelBase | protocol/Swift | Cycle1P_A | {{[^ ]*}}
51+
protocol Cycle1P_B: Cycle1P_A {}
52+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | Cycle1P_B | {{[^ ]*}} | Def | rel: 0
53+
// CHECK: [[@LINE-2]]:21 | protocol/Swift | Cycle1P_A | {{[^ ]*}} | Ref,RelBase | rel: 1
54+
// CHECK: RelBase | protocol/Swift | Cycle1P_B | {{[^ ]*}}
55+
protocol Cycle2P_A: Cycle2P_C {}
56+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | Cycle2P_A | {{[^ ]*}} | Def | rel: 0
57+
// CHECK: [[@LINE-2]]:21 | protocol/Swift | Cycle2P_C | {{[^ ]*}} | Ref,RelBase | rel: 1
58+
// CHECK: RelBase | protocol/Swift | Cycle2P_A | {{[^ ]*}}
59+
protocol Cycle2P_B: Cycle2P_A {}
60+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | Cycle2P_B | {{[^ ]*}} | Def | rel: 0
61+
// CHECK: [[@LINE-2]]:21 | protocol/Swift | Cycle2P_A | {{[^ ]*}} | Ref,RelBase | rel: 1
62+
// CHECK: RelBase | protocol/Swift | Cycle2P_B | {{[^ ]*}}
63+
protocol Cycle2P_C: Cycle2P_B {}
64+
// CHECK: [[@LINE-1]]:10 | protocol/Swift | Cycle2P_C | {{[^ ]*}} | Def | rel: 0
65+
// CHECK: [[@LINE-2]]:21 | protocol/Swift | Cycle2P_B | {{[^ ]*}} | Ref,RelBase | rel: 1
66+
// CHECK: RelBase | protocol/Swift | Cycle2P_C | {{[^ ]*}}

0 commit comments

Comments
 (0)