From 648ba9c920775dd836f1bdd6728bbcf7526f80a4 Mon Sep 17 00:00:00 2001 From: Gert de Pagter Date: Fri, 8 Mar 2019 14:18:53 +0100 Subject: [PATCH 1/2] Stop traversal of interfaces and abstract methods --- src/Mutant/Generator/MutationsGenerator.php | 2 + src/Visitor/MutationsCollectorVisitor.php | 15 +- src/Visitor/NotMutableIgnoreVisitor.php | 57 ++++++++ tests/Visitor/NotMutableIgnoreVisitorTest.php | 130 ++++++++++++++++++ 4 files changed, 190 insertions(+), 14 deletions(-) create mode 100644 src/Visitor/NotMutableIgnoreVisitor.php create mode 100644 tests/Visitor/NotMutableIgnoreVisitorTest.php diff --git a/src/Mutant/Generator/MutationsGenerator.php b/src/Mutant/Generator/MutationsGenerator.php index f911a0c0a..0f821680a 100644 --- a/src/Mutant/Generator/MutationsGenerator.php +++ b/src/Mutant/Generator/MutationsGenerator.php @@ -47,6 +47,7 @@ use Infection\Traverser\PriorityNodeTraverser; use Infection\Visitor\FullyQualifiedClassNameVisitor; use Infection\Visitor\MutationsCollectorVisitor; +use Infection\Visitor\NotMutableIgnoreVisitor; use Infection\Visitor\ParentConnectorVisitor; use Infection\Visitor\ReflectionVisitor; use PhpParser\Node; @@ -159,6 +160,7 @@ private function getMutationsFromFile(SplFileInfo $file, bool $onlyCovered, arra $onlyCovered ); + $traverser->addVisitor(new NotMutableIgnoreVisitor(), 50); $traverser->addVisitor(new ParentConnectorVisitor(), 40); $traverser->addVisitor(new FullyQualifiedClassNameVisitor(), 30); $traverser->addVisitor(new ReflectionVisitor(), 20); diff --git a/src/Visitor/MutationsCollectorVisitor.php b/src/Visitor/MutationsCollectorVisitor.php index 2153393e1..cc71dfc7b 100644 --- a/src/Visitor/MutationsCollectorVisitor.php +++ b/src/Visitor/MutationsCollectorVisitor.php @@ -50,7 +50,7 @@ final class MutationsCollectorVisitor extends NodeVisitorAbstract /** * @var Mutator[] */ - private $mutators = []; + private $mutators; /** * @var Mutation[] @@ -109,19 +109,6 @@ public function leaveNode(Node $node): void continue; } - if ($isOnFunctionSignature - && $methodNode = $node->getAttribute(ReflectionVisitor::FUNCTION_SCOPE_KEY) - ) { - /** @var Node\Stmt\ClassMethod|Node\Expr\Closure $methodNode */ - if ($methodNode instanceof Node\Stmt\ClassMethod && $methodNode->isAbstract()) { - continue; - } - - if ($methodNode instanceof Node\Stmt\ClassMethod && $methodNode->getAttribute(ParentConnectorVisitor::PARENT_KEY) instanceof Node\Stmt\Interface_) { - continue; - } - } - $isCoveredByTest = $this->isCoveredByTest($isOnFunctionSignature, $node); if ($this->onlyCovered && !$isCoveredByTest) { diff --git a/src/Visitor/NotMutableIgnoreVisitor.php b/src/Visitor/NotMutableIgnoreVisitor.php new file mode 100644 index 000000000..2ba60e872 --- /dev/null +++ b/src/Visitor/NotMutableIgnoreVisitor.php @@ -0,0 +1,57 @@ +isAbstract()) { + return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + } +} diff --git a/tests/Visitor/NotMutableIgnoreVisitorTest.php b/tests/Visitor/NotMutableIgnoreVisitorTest.php new file mode 100644 index 000000000..afd8c31a9 --- /dev/null +++ b/tests/Visitor/NotMutableIgnoreVisitorTest.php @@ -0,0 +1,130 @@ +spyVisitor = $this->getSpyVisitor(); + } + + public function test_it_does_not_traverse_interface_methods(): void + { + $code = <<<'PHP' +parseAndTraverse($code); + $this->assertSame(0, $this->spyVisitor->getNumberOfClassMethodsVisited()); + } + + public function test_it_does_not_traverse_abstract_methods(): void + { + $code = <<<'PHP' +parseAndTraverse($code); + $this->assertSame(0, $this->spyVisitor->getNumberOfClassMethodsVisited()); + } + + public function test_it_still_traverses_normal_methods_in_abstract_classes(): void + { + $code = <<<'PHP' +parseAndTraverse($code); + $this->assertSame(1, $this->spyVisitor->getNumberOfClassMethodsVisited()); + } + + private function getSpyVisitor() + { + return new class() extends NodeVisitorAbstract { + private $nodesVisitedCount = 0; + + public function leaveNode(Node $node): void + { + if ($node instanceof Node\Stmt\ClassMethod) { + ++$this->nodesVisitedCount; + } + } + + public function getNumberOfClassMethodsVisited(): int + { + return $this->nodesVisitedCount; + } + }; + } + + private function parseAndTraverse(string $code): void + { + $nodes = $this->getNodes($code); + + $traverser = new NodeTraverser(); + + $traverser->addVisitor(new NotMutableIgnoreVisitor()); + $traverser->addVisitor($this->spyVisitor); + + $traverser->traverse($nodes); + } +} From 6593925d995ce2f632acc39e325b6fce876263f6 Mon Sep 17 00:00:00 2001 From: Gert de Pagter Date: Fri, 8 Mar 2019 14:25:54 +0100 Subject: [PATCH 2/2] Remove interface and abstraction checks in public visitor --- .../FunctionSignature/PublicVisibility.php | 11 ---- src/Mutator/Util/InterfaceParentTrait.php | 64 ------------------- tests/Mutator/AbstractMutatorTestCase.php | 2 + 3 files changed, 2 insertions(+), 75 deletions(-) delete mode 100644 src/Mutator/Util/InterfaceParentTrait.php diff --git a/src/Mutator/FunctionSignature/PublicVisibility.php b/src/Mutator/FunctionSignature/PublicVisibility.php index bcd398005..df7c09a33 100644 --- a/src/Mutator/FunctionSignature/PublicVisibility.php +++ b/src/Mutator/FunctionSignature/PublicVisibility.php @@ -35,7 +35,6 @@ namespace Infection\Mutator\FunctionSignature; -use Infection\Mutator\Util\InterfaceParentTrait; use Infection\Mutator\Util\Mutator; use Infection\Visitor\ReflectionVisitor; use PhpParser\Node; @@ -47,8 +46,6 @@ */ final class PublicVisibility extends Mutator { - use InterfaceParentTrait; - /** * Replaces "public function..." with "protected function ..." * @@ -81,18 +78,10 @@ protected function mutatesNode(Node $node): bool return false; } - if ($node->isAbstract()) { - return false; - } - if ($node->isMagic()) { return false; } - if ($this->isBelongsToInterface($node)) { - return false; - } - return !$this->hasSamePublicParentMethod($node); } diff --git a/src/Mutator/Util/InterfaceParentTrait.php b/src/Mutator/Util/InterfaceParentTrait.php deleted file mode 100644 index 94cb9ca1b..000000000 --- a/src/Mutator/Util/InterfaceParentTrait.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ -trait InterfaceParentTrait -{ - private function isBelongsToInterface(Node $node): bool - { - $parentNode = $node->getAttribute(ParentConnectorVisitor::PARENT_KEY); - - if ($parentNode instanceof Node\Stmt\Interface_) { - return true; - } - - if ($parentNode instanceof Node) { - return $this->isBelongsToInterface($parentNode); - } - - return false; - } -} diff --git a/tests/Mutator/AbstractMutatorTestCase.php b/tests/Mutator/AbstractMutatorTestCase.php index 0510faf69..4b53fb02a 100644 --- a/tests/Mutator/AbstractMutatorTestCase.php +++ b/tests/Mutator/AbstractMutatorTestCase.php @@ -42,6 +42,7 @@ use Infection\Tests\Fixtures\SimpleMutatorVisitor; use Infection\Visitor\CloneVisitor; use Infection\Visitor\FullyQualifiedClassNameVisitor; +use Infection\Visitor\NotMutableIgnoreVisitor; use Infection\Visitor\ParentConnectorVisitor; use Infection\Visitor\ReflectionVisitor; use PhpParser\Lexer; @@ -152,6 +153,7 @@ private function getMutationsFromCode(string $code, Parser $parser, array $setti $mutationsCollectorVisitor = new SimpleMutationsCollectorVisitor($this->getMutator($settings), $initialStatements); + $traverser->addVisitor(new NotMutableIgnoreVisitor()); $traverser->addVisitor($mutationsCollectorVisitor); $traverser->addVisitor(new ParentConnectorVisitor()); $traverser->addVisitor(new FullyQualifiedClassNameVisitor());