Skip to content

Commit 6e17f1a

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 1b69e55 commit 6e17f1a

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
@@ -3626,6 +3626,19 @@ class ClassDecl final : public NominalTypeDecl {
36263626
Bits.ClassDecl.Circularity = static_cast<unsigned>(circularity);
36273627
}
36283628

3629+
/// Walk this class and all of the superclasses of this class, transitively,
3630+
/// invoking the callback function for each class.
3631+
///
3632+
/// \param fn The callback function that will be invoked for each superclass.
3633+
/// It can return \c Continue to continue the traversal. Returning
3634+
/// \c SkipChildren halts the search and returns \c false, while returning
3635+
/// \c Stop halts the search and returns \c true.
3636+
///
3637+
/// \returns \c true if \c fn returned \c Stop for any class, \c false
3638+
/// otherwise.
3639+
bool walkSuperclasses(
3640+
llvm::function_ref<TypeWalker::Action(ClassDecl *)> fn) const;
3641+
36293642
//// Whether this class requires all of its stored properties to
36303643
//// have initializers in the class definition.
36313644
bool requiresStoredPropertyInits() const {
@@ -3959,8 +3972,8 @@ class ProtocolDecl final : public NominalTypeDecl {
39593972
/// a protocol having nested types (ObjC protocols).
39603973
llvm::TinyPtrVector<AssociatedTypeDecl *> getAssociatedTypeMembers() const;
39613974

3962-
/// Walk all of the protocols inherited by this protocol, transitively,
3963-
/// invoking the callback function for each protocol.
3975+
/// Walk this protocol and all of the protocols inherited by this protocol,
3976+
/// transitively, invoking the callback function for each protocol.
39643977
///
39653978
/// \param fn The callback function that will be invoked for each inherited
39663979
/// 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
@@ -3768,6 +3768,27 @@ ClassDecl::findImplementingMethod(const AbstractFunctionDecl *Method) const {
37683768
return nullptr;
37693769
}
37703770

3771+
bool ClassDecl::walkSuperclasses(
3772+
llvm::function_ref<TypeWalker::Action(ClassDecl *)> fn) const {
3773+
3774+
SmallPtrSet<ClassDecl *, 8> seen;
3775+
auto *cls = const_cast<ClassDecl *>(this);
3776+
3777+
while (cls && seen.insert(cls).second) {
3778+
switch (fn(cls)) {
3779+
case TypeWalker::Action::Stop:
3780+
return true;
3781+
case TypeWalker::Action::SkipChildren:
3782+
return false;
3783+
case TypeWalker::Action::Continue:
3784+
cls = cls->getSuperclassDecl();
3785+
continue;
3786+
}
3787+
}
3788+
3789+
return false;
3790+
}
3791+
37713792
EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc,
37723793
ArrayRef<EnumElementDecl *> Elements,
37733794
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)