Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/CodeCoverage.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ final class CodeCoverage
private bool $includeUncoveredFiles = true;
private bool $ignoreDeprecatedCode = false;
private bool $useAnnotationsForIgnoringCode = true;
private bool $staticallyDetectDeadCode = false;

/**
* @var list<class-string>
Expand Down Expand Up @@ -330,6 +331,21 @@ public function doNotIgnoreDeprecatedCode(): void
$this->ignoreDeprecatedCode = false;
}

public function enableStaticDeadCodeDetection(): void
{
$this->staticallyDetectDeadCode = true;
}

public function disableStaticDeadCodeDetection(): void
{
$this->staticallyDetectDeadCode = false;
}

public function staticallyDetectsDeadCode(): bool
{
return $this->staticallyDetectDeadCode;
}

/**
* @phpstan-assert-if-true !null $this->cacheDirectory
*/
Expand Down Expand Up @@ -455,6 +471,7 @@ private function analyser(): FileAnalyser
$this->cacheDirectory,
$this->useAnnotationsForIgnoringCode,
$this->ignoreDeprecatedCode,
$this->staticallyDetectDeadCode,
);
}
}
23 changes: 22 additions & 1 deletion src/Data/RawCodeCoverageData.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,17 @@ public static function fromLineAndBranchCoverage(array $lineCoverage, array $fun
*/
public static function fromUncoveredFile(string $filename, FileAnalyser $analyser): self
{
$analysisResult = $analyser->analyse($filename);

$lineCoverage = array_map(
static fn (): int => Driver::LINE_NOT_EXECUTED,
$analyser->analyse($filename)->executableLines(),
$analysisResult->executableLines(),
);

foreach ($analysisResult->deadLines() as $line => $_) {
$lineCoverage[$line] = Driver::LINE_NOT_EXECUTABLE;
}

return new self([$filename => $lineCoverage], []);
}

Expand Down Expand Up @@ -184,6 +190,21 @@ public function addMissingExecutableLines(string $filename, array $lines): void
}
}

/**
* @param non-empty-string $filename
* @param array<positive-int, true> $lines
*/
public function markLinesAsNotExecutable(string $filename, array $lines): void
{
if (!isset($this->lineCoverage[$filename])) {
return;
}

foreach ($lines as $line => $_) {
$this->lineCoverage[$filename][$line] = Driver::LINE_NOT_EXECUTABLE;
}
}

/**
* @param non-empty-string $filename
* @param array<positive-int, int> $linesToBranchMap
Expand Down
5 changes: 5 additions & 0 deletions src/FilterProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public function applyExecutableLinesFilter(RawCodeCoverageData $data, Filter $fi
$filename,
$linesToBranchMap,
);

$data->markLinesAsNotExecutable(
$filename,
$analysisResult->deadLines(),
);
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/StaticAnalysis/CacheWarmer.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
*
* @return array{cacheHits: non-negative-int, cacheMisses: non-negative-int}
*/
public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter): array
public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter, bool $detectDeadCode = false): array
{
$analyser = new CachingSourceAnalyser(
$cacheDirectory,
new ParsingSourceAnalyser,
new ParsingSourceAnalyser($detectDeadCode),
$detectDeadCode,
);

foreach ($filter->files() as $file) {
Expand Down
5 changes: 4 additions & 1 deletion src/StaticAnalysis/CachingSourceAnalyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ final class CachingSourceAnalyser implements SourceAnalyser
*/
private readonly string $directory;
private readonly SourceAnalyser $sourceAnalyser;
private readonly bool $detectDeadCode;

/**
* @var non-negative-int
Expand All @@ -46,12 +47,13 @@ final class CachingSourceAnalyser implements SourceAnalyser
/**
* @param non-empty-string $directory
*/
public function __construct(string $directory, SourceAnalyser $sourceAnalyser)
public function __construct(string $directory, SourceAnalyser $sourceAnalyser, bool $detectDeadCode = false)
{
Filesystem::createDirectory($directory);

$this->directory = $directory;
$this->sourceAnalyser = $sourceAnalyser;
$this->detectDeadCode = $detectDeadCode;
}

/**
Expand Down Expand Up @@ -165,6 +167,7 @@ private function cacheFile(string $source, bool $useAnnotationsForIgnoringCode,
Version::id(),
$useAnnotationsForIgnoringCode,
$ignoreDeprecatedCode,
$this->detectDeadCode,
],
),
);
Expand Down
26 changes: 23 additions & 3 deletions src/StaticAnalysis/ParsingSourceAnalyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use const T_COMMENT;
use const T_DOC_COMMENT;
use function array_intersect_key;
use function array_keys;
use function array_replace;
use function assert;
Expand All @@ -37,10 +38,12 @@
final readonly class ParsingSourceAnalyser implements SourceAnalyser
{
private Parser $parser;
private bool $detectDeadCode;

public function __construct()
public function __construct(bool $detectDeadCode = false)
{
$this->parser = (new ParserFactory)->createForHostVersion();
$this->parser = (new ParserFactory)->createForHostVersion();
$this->detectDeadCode = $detectDeadCode;
}

/**
Expand All @@ -62,6 +65,7 @@ public function analyse(string $sourceCodeFile, string $sourceCode, bool $useAnn
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($useAnnotationsForIgnoringCode, $ignoreDeprecatedCode);
$executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($sourceCode);
$deadCodeFindingVisitor = null;

$traverser->addVisitor(new NameResolver);
$traverser->addVisitor(new AttributeParentConnectingVisitor);
Expand All @@ -70,6 +74,11 @@ public function analyse(string $sourceCodeFile, string $sourceCode, bool $useAnn
$traverser->addVisitor($ignoredLinesFindingVisitor);
$traverser->addVisitor($executableLinesFindingVisitor);

if ($this->detectDeadCode) {
$deadCodeFindingVisitor = new DeadCodeFindingVisitor;
$traverser->addVisitor($deadCodeFindingVisitor);
}

/* @noinspection UnusedFunctionResultInspection */
$traverser->traverse($nodes);
// @codeCoverageIgnoreStart
Expand Down Expand Up @@ -99,6 +108,16 @@ public function analyse(string $sourceCodeFile, string $sourceCode, bool $useAnn

$ignoredLines = array_keys($ignoredLines);

$executableLines = $executableLinesFindingVisitor->executableLinesGroupedByBranch();
$deadLines = [];

if ($deadCodeFindingVisitor !== null) {
$deadLines = array_intersect_key(
$deadCodeFindingVisitor->deadLines(),
$executableLines,
);
}

return new AnalysisResult(
$codeUnitFindingVisitor->interfaces(),
$codeUnitFindingVisitor->classes(),
Expand All @@ -109,8 +128,9 @@ public function analyse(string $sourceCodeFile, string $sourceCode, bool $useAnn
$lineCountingVisitor->result()->commentLinesOfCode(),
$lineCountingVisitor->result()->nonCommentLinesOfCode(),
),
$executableLinesFindingVisitor->executableLinesGroupedByBranch(),
$executableLines,
$executableLinesFindingVisitor->branchOperatorLines(),
$deadLines,
$ignoredLines,
);
}
Expand Down
5 changes: 3 additions & 2 deletions src/StaticAnalysis/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,19 @@ final class Registry
/**
* @param ?non-empty-string $cacheDirectory
*/
public static function analyser(?string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode): FileAnalyser
public static function analyser(?string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, bool $detectDeadCode = false): FileAnalyser
{
if (self::$analyser !== null) {
return self::$analyser;
}

$sourceAnalyser = new ParsingSourceAnalyser;
$sourceAnalyser = new ParsingSourceAnalyser($detectDeadCode);

if ($cacheDirectory !== null) {
$sourceAnalyser = new CachingSourceAnalyser(
$cacheDirectory,
$sourceAnalyser,
$detectDeadCode,
);
}

Expand Down
17 changes: 16 additions & 1 deletion src/StaticAnalysis/Value/AnalysisResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
*/
private array $branchOperatorLines;

/**
* @var array<positive-int, true>
*/
private array $deadLines;

/**
* @var array<int, int>
*/
Expand All @@ -61,9 +66,10 @@
* @param array<string, Function_> $functions
* @param LinesType $executableLines
* @param array<positive-int, true> $branchOperatorLines
* @param array<positive-int, true> $deadLines
* @param array<int, int> $ignoredLines
*/
public function __construct(array $interfaces, array $classes, array $traits, array $functions, LinesOfCode $linesOfCode, array $executableLines, array $branchOperatorLines, array $ignoredLines)
public function __construct(array $interfaces, array $classes, array $traits, array $functions, LinesOfCode $linesOfCode, array $executableLines, array $branchOperatorLines, array $deadLines, array $ignoredLines)
{
$this->interfaces = $interfaces;
$this->classes = $classes;
Expand All @@ -72,6 +78,7 @@ public function __construct(array $interfaces, array $classes, array $traits, ar
$this->linesOfCode = $linesOfCode;
$this->executableLines = $executableLines;
$this->branchOperatorLines = $branchOperatorLines;
$this->deadLines = $deadLines;
$this->ignoredLines = $ignoredLines;
}

Expand Down Expand Up @@ -128,6 +135,14 @@ public function branchOperatorLines(): array
return $this->branchOperatorLines;
}

/**
* @return array<positive-int, true>
*/
public function deadLines(): array
{
return $this->deadLines;
}

/**
* @return array<int, int>
*/
Expand Down
Loading
Loading