Вход Регистрация
Файл: vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php
Строк: 614
<?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 SebastianBergmannCodeCoverageStaticAnalysis;

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 
PhpParserNode;
use 
PhpParserNodeVisitorAbstract;

/**
 * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
 */
final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
{
    
/**
     * @var int
     */
    
private $nextBranch 0;

    
/**
     * @var string
     */
    
private $source;

    
/**
     * @var array<int, int>
     */
    
private $executableLinesGroupedByBranch = [];

    
/**
     * @var array<int, bool>
     */
    
private $unsets = [];

    
/**
     * @var array<int, string>
     */
    
private $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 NodeScalarString_ ||
            
$node instanceof NodeScalarEncapsedStringPart) {
            
$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 NodeStmtDeclare_ ||
            
$node instanceof NodeStmtDeclareDeclare ||
            
$node instanceof NodeStmtElse_ ||
            
$node instanceof NodeStmtEnumCase ||
            
$node instanceof NodeStmtFinally_ ||
            
$node instanceof NodeStmtInterface_ ||
            
$node instanceof NodeStmtLabel ||
            
$node instanceof NodeStmtNamespace_ ||
            
$node instanceof NodeStmtNop ||
            
$node instanceof NodeStmtSwitch_ ||
            
$node instanceof NodeStmtTryCatch ||
            
$node instanceof NodeStmtUse_ ||
            
$node instanceof NodeStmtUseUse ||
            
$node instanceof NodeExprMatch_ ||
            
$node instanceof NodeExprVariable ||
            
$node instanceof NodeComplexType ||
            
$node instanceof NodeConst_ ||
            
$node instanceof NodeIdentifier ||
            
$node instanceof NodeName ||
            
$node instanceof NodeParam ||
            
$node instanceof NodeScalar) {
            return;
        }

        if (
$node instanceof NodeStmtThrow_) {
            
$this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch);

            return;
        }

        if (
$node instanceof NodeStmtEnum_ ||
            
$node instanceof NodeStmtFunction_ ||
            
$node instanceof NodeStmtClass_ ||
            
$node instanceof NodeStmtClassMethod ||
            
$node instanceof NodeExprClosure ||
            
$node instanceof NodeStmtTrait_) {
            
$isConcreteClassLike $node instanceof NodeStmtEnum_ || $node instanceof NodeStmtClass_ || $node instanceof NodeStmtTrait_;

            if (
null !== $node->stmts) {
                foreach (
$node->stmts as $stmt) {
                    if (
$stmt instanceof NodeStmtNop) {
                        continue;
                    }

                    foreach (
range($stmt->getStartLine(), $stmt->getEndLine()) as $line) {
                        unset(
$this->executableLinesGroupedByBranch[$line]);

                        if (
                            
$isConcreteClassLike &&
                            !
$stmt instanceof NodeStmtClassMethod
                        
) {
                            
$this->unsets[$line] = true;
                        }
                    }
                }
            }

            if (
$isConcreteClassLike) {
                return;
            }

            
$hasEmptyBody = [] === $node->stmts ||
                
null === $node->stmts ||
                (
                    
=== count($node->stmts) &&
                    
$node->stmts[0] instanceof NodeStmtNop
                
);

            if (
$hasEmptyBody) {
                if (
$node->getEndLine() === $node->getStartLine()) {
                    return;
                }

                
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);

                return;
            }

            return;
        }

        if (
$node instanceof NodeExprArrowFunction) {
            
$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 NodeExprTernary) {
            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 NodeExprBinaryOpCoalesce) {
            if (
$node->getStartLine() !== $node->getEndLine()) {
                
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
            }

            return;
        }

        if (
$node instanceof NodeStmtIf_ ||
            
$node instanceof NodeStmtElseIf_ ||
            
$node instanceof NodeStmtCase_) {
            if (
null === $node->cond) {
                return;
            }

            
$this->setLineBranch(
                
$node->cond->getStartLine(),
                
$node->cond->getStartLine(),
                ++
$this->nextBranch
            
);

            return;
        }

        if (
$node instanceof NodeStmtFor_) {
            
$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 NodeStmtForeach_) {
            
$this->setLineBranch(
                
$node->expr->getStartLine(),
                
$node->valueVar->getEndLine(),
                ++
$this->nextBranch
            
);

            return;
        }

        if (
$node instanceof NodeStmtWhile_ ||
            
$node instanceof NodeStmtDo_) {
            
$this->setLineBranch(
                
$node->cond->getStartLine(),
                
$node->cond->getEndLine(),
                ++
$this->nextBranch
            
);

            return;
        }

        if (
$node instanceof NodeStmtCatch_) {
            
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 NodeExprCallLike) {
            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 (
=== preg_match('/^s*$/'$line) ||
                (
                    isset(
$this->commentsToCheckForUnset[$lineNumber]) &&
                    
=== preg_match(sprintf('/^s*%ss*$/'preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)
                )) {
                unset(
$this->executableLinesGroupedByBranch[$lineNumber]);
            }
        }

        
$this->executableLinesGroupedByBranch array_diff_key(
            
$this->executableLinesGroupedByBranch,
            
$this->unsets
        
);
    }

    public function 
executableLinesGroupedByBranch(): array
    {
        return 
$this->executableLinesGroupedByBranch;
    }

    private function 
setLineBranch(int $startint $endint $branch): void
    
{
        foreach (
range($start$end) as $line) {
            
$this->executableLinesGroupedByBranch[$line] = $branch;
        }
    }
}
Онлайн: 0
Реклама