From 1c4f7ab3b838b23afb2ee4dab14acbf75956e952 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 22 Mar 2022 12:24:31 +0300 Subject: * add phpunit as a dev dependency * add some basic tests for UrlHelper::rewrite_relative() * fix UrlHelper::rewrite_relative() to work better on non-absolute relative URL paths --- .../ExecutableLinesFindingVisitor.php | 273 +++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php (limited to 'vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php') 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 @@ + + * + * 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 + */ + private $executableLines = []; + + /** + * @psalm-var array + */ + private $propertyLines = []; + + /** + * @psalm-var array + */ + 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 + */ + 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_; + } +} -- cgit v1.2.3