Skip to content

Commit da010ea

Browse files
authored
Merge pull request #23674 from benlangmuir/infinite-superclass
[index] Fix infinite loop while looking at superclasses
2 parents b75d835 + 26de7c1 commit da010ea

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)