diff --git a/src/StaticAnalysis/ExecutableLinesFindingVisitor.php b/src/StaticAnalysis/ExecutableLinesFindingVisitor.php index f69363caf..821701a3a 100644 --- a/src/StaticAnalysis/ExecutableLinesFindingVisitor.php +++ b/src/StaticAnalysis/ExecutableLinesFindingVisitor.php @@ -49,6 +49,7 @@ use PhpParser\Node\Stmt\TryCatch; use PhpParser\Node\Stmt\Unset_; use PhpParser\Node\Stmt\While_; +use PhpParser\NodeAbstract; use PhpParser\NodeVisitorAbstract; /** @@ -71,6 +72,11 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract */ private $returns = []; + /** + * @psalm-var array + */ + private $arrayDimFetchVars = []; + public function enterNode(Node $node): void { $this->savePropertyLines($node); @@ -123,7 +129,7 @@ private function computeReturns(): void $line = $return->getEndLine(); if ($return->expr !== null) { - $line = $return->expr->getStartLine(); + $line = $this->getNodeStartLine($return->expr); } $this->executableLines[$line] = $line; @@ -136,13 +142,6 @@ private function computeReturns(): void private function getLines(Node $node): array { if ($node instanceof BinaryOp) { - if (($node->left instanceof Node\Scalar || - $node->left instanceof Node\Expr\ConstFetch) && - ($node->right instanceof Node\Scalar || - $node->right instanceof Node\Expr\ConstFetch)) { - return [$node->right->getStartLine()]; - } - return []; } @@ -154,6 +153,8 @@ private function getLines(Node $node): array } if ($node instanceof ArrayDimFetch) { + $this->arrayDimFetchVars[spl_object_id($node->var)] = true; + if (null === $node->dim) { return []; } @@ -162,6 +163,10 @@ private function getLines(Node $node): array } if ($node instanceof Array_) { + if (isset($this->arrayDimFetchVars[spl_object_id($node)])) { + return []; + } + $startLine = $node->getStartLine(); if (isset($this->executableLines[$startLine])) { @@ -242,6 +247,29 @@ private function getLines(Node $node): array return [$node->getStartLine()]; } + private function getNodeStartLine(NodeAbstract $node): int + { + if ($node instanceof Node\Expr\BooleanNot || + $node instanceof Node\Expr\AssignOp\ShiftLeft || + $node instanceof Node\Expr\AssignOp\ShiftRight + ) { + return $node->getEndLine(); + } + + if ($node instanceof BinaryOp) { + return $this->getNodeStartLine($node->right); + } + + if ($node instanceof Node\Scalar\String_ && ( + $node->getAttribute('kind') === Node\Scalar\String_::KIND_HEREDOC || + $node->getAttribute('kind') === Node\Scalar\String_::KIND_NOWDOC + )) { + return $node->getStartLine() + 1; + } + + return $node->getStartLine(); + } + private function isExecutable(Node $node): bool { return $node instanceof Assign || diff --git a/tests/_files/source_with_multiline_constant_return.php b/tests/_files/source_with_multiline_constant_return.php index 6c8a6bf4a..9ccfed9b0 100644 --- a/tests/_files/source_with_multiline_constant_return.php +++ b/tests/_files/source_with_multiline_constant_return.php @@ -245,22 +245,7 @@ public function Spaceship(): int ; } - public function nowdocSimpleA(): string - { - return <<<'EOF' - foo - EOF; - } - - public function nowdocSimpleB(): string - { - return - <<<'EOF' - foo - EOF; - } - - public function nowdocSimpleC(): string + public function nowdoc(): string { return <<<'EOF' @@ -316,17 +301,8 @@ public function complexAssociativityRight(): int ** 2 ** - 3; - } - - public function complexAssociativityLeft(): int - { - return - 1 - >> - 2 - >> - 3; + 3 + ; } public function complexAssociativityNa(): bool @@ -335,7 +311,8 @@ public function complexAssociativityNa(): bool ! ! ! - false; + false + ; } public function complexTernary(): int @@ -347,17 +324,8 @@ public function complexTernary(): int ? 3 : 4 ) - : 5; - } - - public function complexNullCoalescing(): int - { - return - null - ?? - 1 - ?? - null; + : 5 + ; } public function constFromArray(): string @@ -368,165 +336,48 @@ public function constFromArray(): string 'ro', 'fi', 'omega', - ][2]; + ] + [2] + ; } public function withNotConstInTheMiddle(): string { return '' - . '' - . phpversion() - . '' - . ''; - } - - public function andA(): bool - { - return - true - && false; - } - - public function andB(): bool - { - return - true - && true; - } - - public function andC(): bool - { - return - false - && true; - } - - public function andD(): bool - { - return - false - && false; - } - - public function andE(): bool - { - return - __TRAIT__ // compile time constant evaluated to false - && 1 - && 0; - } - - public function andF(): bool - { - return - PHP_VERSION_ID // compile time constant evaluated to true - && 1 - && 0; - } - - public function orA(): bool - { - return - true - || false; - } - - public function orB(): bool - { - return - true - || true; - } - - public function orC(): bool - { - return - false - || true; - } - - public function orD(): bool - { - return - false - || false; - } - - public function orE(): bool - { - return - __TRAIT__ - || true - || false; - } - - public function orF(): bool - { - return - PHP_VERSION_ID - || true - || false; - } - - public function orG(): bool - { - return - PHP_VERSION_ID === PHP_VERSION_ID - || true - || false; + . + '' + . + phpversion() + . + '' + . + '' + ; } - public function orH(): bool + public function multipleConcats(): string { return - PHP_VERSION_ID !== PHP_VERSION_ID - || true - || false; - } - - public function constIfFalseA(): bool - { - if (false) { - return true; - } - - return false; - } - - public function constIfFalseB(): bool - { - if (__TRAIT__) { - return true; - } - - return false; - } - - public function constIfTrueA(): bool - { - if (true) { - return true; - } - - return false; - } - - public function constIfTrueB(): bool - { - if (PHP_VERSION_ID) { - return true; - } - - return false; + 'a' + . + 'b' + . + 'c' + . + 'd' + . + 'e' + ; } - public function constIfUnknown(): bool + public function multilineHeredoc(): string { - if (__NOT_EXPECTED_TO_BE_KNOWN_CONSTANT__) { - return true; - } + return <<lineCoverage()[$file]) );