summaryrefslogtreecommitdiff
path: root/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php')
-rw-r--r--vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php273
1 files changed, 273 insertions, 0 deletions
diff --git a/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php b/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php
new file mode 100644
index 000000000..ae0b08ae7
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php
@@ -0,0 +1,273 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
+
+use PhpParser\Node;
+use PhpParser\Node\Expr\Array_;
+use PhpParser\Node\Expr\ArrayDimFetch;
+use PhpParser\Node\Expr\ArrayItem;
+use PhpParser\Node\Expr\Assign;
+use PhpParser\Node\Expr\BinaryOp;
+use PhpParser\Node\Expr\CallLike;
+use PhpParser\Node\Expr\Cast;
+use PhpParser\Node\Expr\Closure;
+use PhpParser\Node\Expr\Match_;
+use PhpParser\Node\Expr\MethodCall;
+use PhpParser\Node\Expr\NullsafePropertyFetch;
+use PhpParser\Node\Expr\PropertyFetch;
+use PhpParser\Node\Expr\StaticPropertyFetch;
+use PhpParser\Node\Expr\Ternary;
+use PhpParser\Node\MatchArm;
+use PhpParser\Node\Scalar\Encapsed;
+use PhpParser\Node\Stmt\Break_;
+use PhpParser\Node\Stmt\Case_;
+use PhpParser\Node\Stmt\Catch_;
+use PhpParser\Node\Stmt\Class_;
+use PhpParser\Node\Stmt\ClassMethod;
+use PhpParser\Node\Stmt\Continue_;
+use PhpParser\Node\Stmt\Do_;
+use PhpParser\Node\Stmt\Echo_;
+use PhpParser\Node\Stmt\Else_;
+use PhpParser\Node\Stmt\ElseIf_;
+use PhpParser\Node\Stmt\Expression;
+use PhpParser\Node\Stmt\Finally_;
+use PhpParser\Node\Stmt\For_;
+use PhpParser\Node\Stmt\Foreach_;
+use PhpParser\Node\Stmt\Goto_;
+use PhpParser\Node\Stmt\If_;
+use PhpParser\Node\Stmt\Property;
+use PhpParser\Node\Stmt\Return_;
+use PhpParser\Node\Stmt\Switch_;
+use PhpParser\Node\Stmt\Throw_;
+use PhpParser\Node\Stmt\TryCatch;
+use PhpParser\Node\Stmt\Unset_;
+use PhpParser\Node\Stmt\While_;
+use PhpParser\NodeVisitorAbstract;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
+{
+ /**
+ * @psalm-var array<int, int>
+ */
+ private $executableLines = [];
+
+ /**
+ * @psalm-var array<int, int>
+ */
+ private $propertyLines = [];
+
+ /**
+ * @psalm-var array<int, Return_>
+ */
+ private $returns = [];
+
+ public function enterNode(Node $node): void
+ {
+ $this->savePropertyLines($node);
+
+ if (!$this->isExecutable($node)) {
+ return;
+ }
+
+ foreach ($this->getLines($node) as $line) {
+ if (isset($this->propertyLines[$line])) {
+ return;
+ }
+
+ $this->executableLines[$line] = $line;
+ }
+ }
+
+ /**
+ * @psalm-return array<int, int>
+ */
+ public function executableLines(): array
+ {
+ $this->computeReturns();
+
+ sort($this->executableLines);
+
+ return $this->executableLines;
+ }
+
+ private function savePropertyLines(Node $node): void
+ {
+ if (!$node instanceof Property && !$node instanceof Node\Stmt\ClassConst) {
+ return;
+ }
+
+ foreach (range($node->getStartLine(), $node->getEndLine()) as $index) {
+ $this->propertyLines[$index] = $index;
+ }
+ }
+
+ private function computeReturns(): void
+ {
+ foreach ($this->returns as $return) {
+ foreach (range($return->getStartLine(), $return->getEndLine()) as $loc) {
+ if (isset($this->executableLines[$loc])) {
+ continue 2;
+ }
+ }
+
+ $line = $return->getEndLine();
+
+ if ($return->expr !== null) {
+ $line = $return->expr->getStartLine();
+ }
+
+ $this->executableLines[$line] = $line;
+ }
+ }
+
+ /**
+ * @return int[]
+ */
+ private function getLines(Node $node): array
+ {
+ if ($node instanceof Cast ||
+ $node instanceof PropertyFetch ||
+ $node instanceof NullsafePropertyFetch ||
+ $node instanceof StaticPropertyFetch) {
+ return [$node->getEndLine()];
+ }
+
+ if ($node instanceof ArrayDimFetch) {
+ if (null === $node->dim) {
+ return [];
+ }
+
+ return [$node->dim->getStartLine()];
+ }
+
+ if ($node instanceof Array_) {
+ $startLine = $node->getStartLine();
+
+ if (isset($this->executableLines[$startLine])) {
+ return [];
+ }
+
+ if ([] === $node->items) {
+ return [$node->getEndLine()];
+ }
+
+ if ($node->items[0] instanceof ArrayItem) {
+ return [$node->items[0]->getStartLine()];
+ }
+ }
+
+ if ($node instanceof ClassMethod) {
+ if ($node->name->name !== '__construct') {
+ return [];
+ }
+
+ $existsAPromotedProperty = false;
+
+ foreach ($node->getParams() as $param) {
+ if (0 !== ($param->flags & Class_::VISIBILITY_MODIFIER_MASK)) {
+ $existsAPromotedProperty = true;
+
+ break;
+ }
+ }
+
+ if ($existsAPromotedProperty) {
+ // Only the line with `function` keyword should be listed here
+ // but `nikic/php-parser` doesn't provide a way to fetch it
+ return range($node->getStartLine(), $node->name->getEndLine());
+ }
+
+ return [];
+ }
+
+ if ($node instanceof MethodCall) {
+ return [$node->name->getStartLine()];
+ }
+
+ if ($node instanceof Ternary) {
+ $lines = [$node->cond->getStartLine()];
+
+ if (null !== $node->if) {
+ $lines[] = $node->if->getStartLine();
+ }
+
+ $lines[] = $node->else->getStartLine();
+
+ return $lines;
+ }
+
+ if ($node instanceof Match_) {
+ return [$node->cond->getStartLine()];
+ }
+
+ if ($node instanceof MatchArm) {
+ return [$node->body->getStartLine()];
+ }
+
+ if ($node instanceof Expression && (
+ $node->expr instanceof Cast ||
+ $node->expr instanceof Match_ ||
+ $node->expr instanceof MethodCall
+ )) {
+ return [];
+ }
+
+ if ($node instanceof Return_) {
+ $this->returns[] = $node;
+
+ return [];
+ }
+
+ return [$node->getStartLine()];
+ }
+
+ private function isExecutable(Node $node): bool
+ {
+ return $node instanceof Assign ||
+ $node instanceof ArrayDimFetch ||
+ $node instanceof Array_ ||
+ $node instanceof BinaryOp ||
+ $node instanceof Break_ ||
+ $node instanceof CallLike ||
+ $node instanceof Case_ ||
+ $node instanceof Cast ||
+ $node instanceof Catch_ ||
+ $node instanceof ClassMethod ||
+ $node instanceof Closure ||
+ $node instanceof Continue_ ||
+ $node instanceof Do_ ||
+ $node instanceof Echo_ ||
+ $node instanceof ElseIf_ ||
+ $node instanceof Else_ ||
+ $node instanceof Encapsed ||
+ $node instanceof Expression ||
+ $node instanceof Finally_ ||
+ $node instanceof For_ ||
+ $node instanceof Foreach_ ||
+ $node instanceof Goto_ ||
+ $node instanceof If_ ||
+ $node instanceof Match_ ||
+ $node instanceof MatchArm ||
+ $node instanceof MethodCall ||
+ $node instanceof NullsafePropertyFetch ||
+ $node instanceof PropertyFetch ||
+ $node instanceof Return_ ||
+ $node instanceof StaticPropertyFetch ||
+ $node instanceof Switch_ ||
+ $node instanceof Ternary ||
+ $node instanceof Throw_ ||
+ $node instanceof TryCatch ||
+ $node instanceof Unset_ ||
+ $node instanceof While_;
+ }
+}