Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/okikio/aiscode.
Something went wrong. Try again.
aiscode laravel vendor phpunit php-code-coverage src StaticAnalysis ExecutableLinesFindingVisitor.php
12 kB · 402 lines
PHP
at astro
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403<?php declare(strict_types=1);/* * This file is part of phpunit/php-code-coverage. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
use function array_diff_key;use function assert;use function count;use function current;use function end;use function explode;use function max;use function preg_match;use function preg_quote;use function range;use function reset;use function sprintf;use PhpParser\Node;use PhpParser\NodeVisitorAbstract;
/** * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage * * @psalm-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser */final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract{ private int $nextBranch = 0; private readonly string $source;
/** * @psalm-var LinesType */ private array $executableLinesGroupedByBranch = [];
/** * @psalm-var array<int, bool> */ private array $unsets = [];
/** * @psalm-var array<int, string> */ private array $commentsToCheckForUnset = [];
public function __construct(string $source) { $this->source = $source; }
public function enterNode(Node $node): void { foreach ($node->getComments() as $comment) { $commentLine = $comment->getStartLine();
if (!isset($this->executableLinesGroupedByBranch[$commentLine])) { continue; }
foreach (explode("\n", $comment->getText()) as $text) { $this->commentsToCheckForUnset[$commentLine] = $text; $commentLine++; } }
if ($node instanceof Node\Scalar\String_ || $node instanceof Node\Scalar\EncapsedStringPart) { $startLine = $node->getStartLine() + 1; $endLine = $node->getEndLine() - 1;
if ($startLine <= $endLine) { foreach (range($startLine, $endLine) as $line) { unset($this->executableLinesGroupedByBranch[$line]); } }
return; }
if ($node instanceof Node\Stmt\Interface_) { foreach (range($node->getStartLine(), $node->getEndLine()) as $line) { $this->unsets[$line] = true; }
return; }
if ($node instanceof Node\Stmt\Declare_ || $node instanceof Node\Stmt\DeclareDeclare || $node instanceof Node\Stmt\Else_ || $node instanceof Node\Stmt\EnumCase || $node instanceof Node\Stmt\Finally_ || $node instanceof Node\Stmt\GroupUse || $node instanceof Node\Stmt\Label || $node instanceof Node\Stmt\Namespace_ || $node instanceof Node\Stmt\Nop || $node instanceof Node\Stmt\Switch_ || $node instanceof Node\Stmt\TryCatch || $node instanceof Node\Stmt\Use_ || $node instanceof Node\Stmt\UseUse || $node instanceof Node\Expr\ConstFetch || $node instanceof Node\Expr\Match_ || $node instanceof Node\Expr\Variable || $node instanceof Node\Expr\Throw_ || $node instanceof Node\ComplexType || $node instanceof Node\Const_ || $node instanceof Node\Identifier || $node instanceof Node\Name || $node instanceof Node\Param || $node instanceof Node\Scalar) { return; }
/* * nikic/php-parser ^4.18 represents <code>throw</code> statements * as <code>Stmt\Throw_</code> objects */ if ($node instanceof Node\Stmt\Throw_) { $this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch);
return; }
/* * nikic/php-parser ^5 represents <code>throw</code> statements * as <code>Stmt\Expression</code> objects that contain an * <code>Expr\Throw_</code> object */ if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Node\Expr\Throw_) { $this->setLineBranch($node->expr->expr->getEndLine(), $node->expr->expr->getEndLine(), ++$this->nextBranch);
return; }
if ($node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\ClassMethod || $node instanceof Node\Expr\Closure || $node instanceof Node\Stmt\Trait_) { if ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassMethod) { $unsets = [];
foreach ($node->getParams() as $param) { foreach (range($param->getStartLine(), $param->getEndLine()) as $line) { $unsets[$line] = true; } }
unset($unsets[$node->getEndLine()]);
$this->unsets += $unsets; }
$isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_;
if (null !== $node->stmts) { foreach ($node->stmts as $stmt) { if ($stmt instanceof Node\Stmt\Nop) { continue; }
foreach (range($stmt->getStartLine(), $stmt->getEndLine()) as $line) { unset($this->executableLinesGroupedByBranch[$line]);
if ( $isConcreteClassLike && !$stmt instanceof Node\Stmt\ClassMethod ) { $this->unsets[$line] = true; } } } }
if ($isConcreteClassLike) { return; }
$hasEmptyBody = [] === $node->stmts || null === $node->stmts || ( 1 === count($node->stmts) && $node->stmts[0] instanceof Node\Stmt\Nop );
if ($hasEmptyBody) { if ($node->getEndLine() === $node->getStartLine() && isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) { return; }
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
return; }
return; }
if ($node instanceof Node\Expr\ArrowFunction) { $startLine = max( $node->getStartLine() + 1, $node->expr->getStartLine(), );
$endLine = $node->expr->getEndLine();
if ($endLine < $startLine) { return; }
$this->setLineBranch($startLine, $endLine, ++$this->nextBranch);
return; }
if ($node instanceof Node\Expr\Ternary) { if (null !== $node->if && $node->getStartLine() !== $node->if->getEndLine()) { $this->setLineBranch($node->if->getStartLine(), $node->if->getEndLine(), ++$this->nextBranch); }
if ($node->getStartLine() !== $node->else->getEndLine()) { $this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch); }
return; }
if ($node instanceof Node\Expr\BinaryOp\Coalesce) { if ($node->getStartLine() !== $node->getEndLine()) { $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch); }
return; }
if ($node instanceof Node\Stmt\If_ || $node instanceof Node\Stmt\ElseIf_ || $node instanceof Node\Stmt\Case_) { if (null === $node->cond) { return; }
$this->setLineBranch( $node->cond->getStartLine(), $node->cond->getStartLine(), ++$this->nextBranch, );
return; }
if ($node instanceof Node\Stmt\For_) { $startLine = null; $endLine = null;
if ([] !== $node->init) { $startLine = $node->init[0]->getStartLine();
end($node->init);
$endLine = current($node->init)->getEndLine();
reset($node->init); }
if ([] !== $node->cond) { if (null === $startLine) { $startLine = $node->cond[0]->getStartLine(); }
end($node->cond);
$endLine = current($node->cond)->getEndLine();
reset($node->cond); }
if ([] !== $node->loop) { if (null === $startLine) { $startLine = $node->loop[0]->getStartLine(); }
end($node->loop);
$endLine = current($node->loop)->getEndLine();
reset($node->loop); }
if (null === $startLine || null === $endLine) { return; }
$this->setLineBranch( $startLine, $endLine, ++$this->nextBranch, );
return; }
if ($node instanceof Node\Stmt\Foreach_) { $this->setLineBranch( $node->expr->getStartLine(), $node->valueVar->getEndLine(), ++$this->nextBranch, );
return; }
if ($node instanceof Node\Stmt\While_ || $node instanceof Node\Stmt\Do_) { $this->setLineBranch( $node->cond->getStartLine(), $node->cond->getEndLine(), ++$this->nextBranch, );
return; }
if ($node instanceof Node\Stmt\Catch_) { assert([] !== $node->types); $startLine = $node->types[0]->getStartLine(); end($node->types); $endLine = current($node->types)->getEndLine();
$this->setLineBranch( $startLine, $endLine, ++$this->nextBranch, );
return; }
if ($node instanceof Node\Expr\CallLike) { if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) { $branch = $this->executableLinesGroupedByBranch[$node->getStartLine()]; } else { $branch = ++$this->nextBranch; }
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch);
return; }
if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) { return; }
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch); }
public function afterTraverse(array $nodes): void { $lines = explode("\n", $this->source);
foreach ($lines as $lineNumber => $line) { $lineNumber++;
if (1 === preg_match('/^\s*$/', $line) || ( isset($this->commentsToCheckForUnset[$lineNumber]) && 1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line) )) { unset($this->executableLinesGroupedByBranch[$lineNumber]); } }
$this->executableLinesGroupedByBranch = array_diff_key( $this->executableLinesGroupedByBranch, $this->unsets, ); }
/** * @psalm-return LinesType */ public function executableLinesGroupedByBranch(): array { return $this->executableLinesGroupedByBranch; }
private function setLineBranch(int $start, int $end, int $branch): void { foreach (range($start, $end) as $line) { $this->executableLinesGroupedByBranch[$line] = $branch; } }}