Skip to content

Commit 2ea6c05

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 801c8a6 commit 2ea6c05

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
@@ -3631,6 +3631,19 @@ class ClassDecl final : public NominalTypeDecl {
36313631
Bits.ClassDecl.Circularity = static_cast<unsigned>(circularity);
36323632
}
36333633

3634+
/// Walk this class and all of the superclasses of this class, transitively,
3635+
/// invoking the callback function for each class.
3636+
///
3637+
/// \param fn The callback function that will be invoked for each superclass.
3638+
/// It can return \c Continue to continue the traversal. Returning
3639+
/// \c SkipChildren halts the search and returns \c false, while returning
3640+
/// \c Stop halts the search and returns \c true.
3641+
///
3642+
/// \returns \c true if \c fn returned \c Stop for any class, \c false
3643+
/// otherwise.
3644+
bool walkSuperclasses(
3645+
llvm::function_ref<TypeWalker::Action(ClassDecl *)> fn) const;
3646+
36343647
//// Whether this class requires all of its stored properties to
36353648
//// have initializers in the class definition.
36363649
bool requiresStoredPropertyInits() const {
@@ -3956,8 +3969,8 @@ class ProtocolDecl final : public NominalTypeDecl {
39563969
/// a protocol having nested types (ObjC protocols).
39573970
llvm::TinyPtrVector<AssociatedTypeDecl *> getAssociatedTypeMembers() const;
39583971

3959-
/// Walk all of the protocols inherited by this protocol, transitively,
3960-
/// invoking the callback function for each protocol.
3972+
/// Walk this protocol and all of the protocols inherited by this protocol,
3973+
/// transitively, invoking the callback function for each protocol.
39613974
///
39623975
/// \param fn The callback function that will be invoked for each inherited
39633976
/// 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
@@ -3715,6 +3715,27 @@ ClassDecl::findImplementingMethod(const AbstractFunctionDecl *Method) const {
37153715
return nullptr;
37163716
}
37173717

3718+
bool ClassDecl::walkSuperclasses(
3719+
llvm::function_ref<TypeWalker::Action(ClassDecl *)> fn) const {
3720+
3721+
SmallPtrSet<ClassDecl *, 8> seen;
3722+
auto *cls = const_cast<ClassDecl *>(this);
3723+
3724+
while (cls && seen.insert(cls).second) {
3725+
switch (fn(cls)) {
3726+
case TypeWalker::Action::Stop:
3727+
return true;
3728+
case TypeWalker::Action::SkipChildren:
3729+
return false;
3730+
case TypeWalker::Action::Continue:
3731+
cls = cls->getSuperclassDecl();
3732+
continue;
3733+
}
3734+
}
3735+
3736+
return false;
3737+
}
3738+
37183739
EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc,
37193740
ArrayRef<EnumElementDecl *> Elements,
37203741
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)