From f7db99d130f8eceef1e9d8c90105e287fca7ece9 Mon Sep 17 00:00:00 2001 From: Dylan McGannon Date: Fri, 31 Aug 2018 22:28:37 +1000 Subject: [PATCH 1/2] fix(indexing): Files with classes that declare itself to be it's parent can cause an infinite loop when indexing. #669 --- fixtures/self_referencing_class.php | 4 ++++ src/DefinitionResolver.php | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 fixtures/self_referencing_class.php diff --git a/fixtures/self_referencing_class.php b/fixtures/self_referencing_class.php new file mode 100644 index 00000000..287fc252 --- /dev/null +++ b/fixtures/self_referencing_class.php @@ -0,0 +1,4 @@ +undef_prop = 1; \ No newline at end of file diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 990c1965..163000f9 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -452,7 +452,9 @@ private function resolveMemberAccessExpressionNodeToFqn(Node\Expression\MemberAc // Repeat for parent class if ($implementorDef->extends) { foreach ($implementorDef->extends as $extends) { - $implementorFqns[] = $extends; + if ($extends !== $implementorFqn) { + $implementorFqns[] = $extends; + } } } } From 2cb85bda1bcf38b2d9f190531cace3c13b0b7f58 Mon Sep 17 00:00:00 2001 From: Dylan McGannon Date: Sat, 1 Sep 2018 10:30:09 +1000 Subject: [PATCH 2/2] Maintain a list of visited FQNs while following extends to avoid loops. --- fixtures/self_referencing_class.php | 22 +++++++++++++++++++--- src/DefinitionResolver.php | 6 +++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/fixtures/self_referencing_class.php b/fixtures/self_referencing_class.php index 287fc252..06dc8f0f 100644 --- a/fixtures/self_referencing_class.php +++ b/fixtures/self_referencing_class.php @@ -1,4 +1,20 @@ undef_prop = 1; \ No newline at end of file +namespace RecursiveTest; + +class A extends A {} + +class B extends C {} +class C extends B {} + +class D extends E {} +class E extends F {} +class F extends D {} + +$a = new A; +$a->undef_prop = 1; + +$b = new B; +$b->undef_prop = 1; + +$d = new D; +$d->undef_prop = 1; diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 163000f9..08b82aa4 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -437,6 +437,7 @@ private function resolveMemberAccessExpressionNodeToFqn(Node\Expression\MemberAc // Find the right class that implements the member $implementorFqns = [$classFqn]; + $visitedFqns = []; while ($implementorFqn = array_shift($implementorFqns)) { // If the member FQN exists, return it @@ -449,10 +450,13 @@ private function resolveMemberAccessExpressionNodeToFqn(Node\Expression\MemberAc if ($implementorDef === null) { break; } + // Note the FQN as visited + $visitedFqns[] = $implementorFqn; // Repeat for parent class if ($implementorDef->extends) { foreach ($implementorDef->extends as $extends) { - if ($extends !== $implementorFqn) { + // Don't add the parent FQN if it's already been visited + if (!\in_array($extends, $visitedFqns)) { $implementorFqns[] = $extends; } }