Skip to content

Commit

Permalink
Closes #1055
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbergmann committed Dec 14, 2024
1 parent 88c7c0b commit a0adc21
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 49 deletions.
63 changes: 45 additions & 18 deletions src/StaticAnalysis/CodeUnitFindingVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ public function enterNode(Node $node): null

public function leaveNode(Node $node): null
{
if (!$node instanceof Class_) {
if ($node instanceof Class_ && $node->isAnonymous()) {
return null;
}

if ($node->isAnonymous()) {
if (!$node instanceof Class_ && !$node instanceof Trait_) {
return null;
}

Expand All @@ -116,22 +116,7 @@ public function leaveNode(Node $node): null
return null;
}

$namespacedClassName = $node->namespacedName->toString();

assert(isset($this->classes[$namespacedClassName]));

$this->classes[$namespacedClassName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_(
$this->classes[$namespacedClassName]->name(),
$this->classes[$namespacedClassName]->namespacedName(),
$this->classes[$namespacedClassName]->namespace(),
$this->classes[$namespacedClassName]->file(),
$this->classes[$namespacedClassName]->startLine(),
$this->classes[$namespacedClassName]->endLine(),
$this->classes[$namespacedClassName]->parentClass(),
$this->classes[$namespacedClassName]->interfaces(),
$traits,
$this->classes[$namespacedClassName]->methods(),
);
$this->postProcessClassOrTrait($node, $traits);

return null;
}
Expand Down Expand Up @@ -308,8 +293,10 @@ private function processTrait(Trait_ $node): void
$name,
$namespacedName,
$this->namespace($namespacedName, $name),
$this->file,
$node->getStartLine(),
$node->getEndLine(),
[],
$this->processMethods($node->getMethods()),
);
}
Expand Down Expand Up @@ -398,4 +385,44 @@ private function typeAsString(Identifier|Name $node): string

return $node->toString();
}

/**
* @param list<non-empty-string> $traits
*/
private function postProcessClassOrTrait(Class_|Trait_ $node, array $traits): void
{
$name = $node->namespacedName->toString();

if ($node instanceof Class_) {
assert(isset($this->classes[$name]));

$this->classes[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_(
$this->classes[$name]->name(),
$this->classes[$name]->namespacedName(),
$this->classes[$name]->namespace(),
$this->classes[$name]->file(),
$this->classes[$name]->startLine(),
$this->classes[$name]->endLine(),
$this->classes[$name]->parentClass(),
$this->classes[$name]->interfaces(),
$traits,
$this->classes[$name]->methods(),
);

return;
}

assert(isset($this->traits[$name]));

$this->traits[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_(
$this->traits[$name]->name(),
$this->traits[$name]->namespacedName(),
$this->traits[$name]->namespace(),
$this->traits[$name]->file(),
$this->traits[$name]->startLine(),
$this->traits[$name]->endLine(),
$traits,
$this->traits[$name]->methods(),
);
}
}
32 changes: 31 additions & 1 deletion src/StaticAnalysis/Value/Trait_.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
private string $namespacedName;
private string $namespace;

/**
* @var non-empty-string
*/
private string $file;

/**
* @var non-negative-int
*/
Expand All @@ -35,6 +40,11 @@
*/
private int $endLine;

/**
* @var list<non-empty-string>
*/
private array $traits;

/**
* @var array<non-empty-string, Method>
*/
Expand All @@ -43,17 +53,21 @@
/**
* @param non-empty-string $name
* @param non-empty-string $namespacedName
* @param non-empty-string $file
* @param non-negative-int $startLine
* @param non-negative-int $endLine
* @param list<non-empty-string> $traits
* @param array<non-empty-string, Method> $methods
*/
public function __construct(string $name, string $namespacedName, string $namespace, int $startLine, int $endLine, array $methods)
public function __construct(string $name, string $namespacedName, string $namespace, string $file, int $startLine, int $endLine, array $traits, array $methods)
{
$this->name = $name;
$this->namespacedName = $namespacedName;
$this->namespace = $namespace;
$this->file = $file;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->traits = $traits;
$this->methods = $methods;
}

Expand Down Expand Up @@ -83,6 +97,14 @@ public function namespace(): string
return $this->namespace;
}

/**
* @return non-empty-string
*/
public function file(): string
{
return $this->file;
}

/**
* @return non-negative-int
*/
Expand All @@ -99,6 +121,14 @@ public function endLine(): int
return $this->endLine;
}

/**
* @return list<non-empty-string>
*/
public function traits(): array
{
return $this->traits;
}

/**
* @return array<non-empty-string, Method>
*/
Expand Down
84 changes: 66 additions & 18 deletions src/Target/MapBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,47 +44,95 @@ public function build(Filter $filter, FileAnalyser $analyser): array
$reverseLookup = [];

foreach ($filter->files() as $file) {
foreach ($analyser->interfacesIn($file) as $interface) {
$classesThatImplementInterface[$interface->namespacedName()] = [];
}

foreach ($analyser->classesIn($file) as $class) {
if ($class->isNamespaced()) {
$this->process($namespaces, $class->namespace(), $file, $class->startLine(), $class->endLine());
foreach ($analyser->traitsIn($file) as $trait) {
if ($trait->isNamespaced()) {
$this->process($namespaces, $trait->namespace(), $file, $trait->startLine(), $trait->endLine());
}

$this->process($classes, $class->namespacedName(), $file, $class->startLine(), $class->endLine());
$this->process($traits, $trait->namespacedName(), $file, $trait->startLine(), $trait->endLine());

foreach ($class->methods() as $method) {
$methodName = $class->namespacedName() . '::' . $method->name();
foreach ($trait->methods() as $method) {
$methodName = $trait->namespacedName() . '::' . $method->name();

$this->process($methods, $methodName, $file, $method->startLine(), $method->endLine());

foreach (range($method->startLine(), $method->endLine()) as $line) {
$reverseLookup[$file . ':' . $line] = $methodName;
}
}

$classesThatExtendClass[$class->namespacedName()] = [];
$classDetails[] = $class;
}
}

foreach ($filter->files() as $file) {
foreach ($analyser->traitsIn($file) as $trait) {
if ($trait->isNamespaced()) {
$this->process($namespaces, $trait->namespace(), $file, $trait->startLine(), $trait->endLine());
foreach ($trait->traits() as $traitName) {
if (!isset($traits[$traitName])) {
continue;
}

$file = array_keys($traits[$traitName])[0];

if (!isset($traits[$trait->namespacedName()][$file])) {
$traits[$trait->namespacedName()][$file] = $traits[$traitName][$file];

continue;
}

$traits[$trait->namespacedName()][$file] = array_unique(
array_merge(
$traits[$trait->namespacedName()][$file],
$traits[$traitName][$file],
),
);
}
}
}

$this->process($traits, $trait->namespacedName(), $file, $trait->startLine(), $trait->endLine());
foreach ($filter->files() as $file) {
foreach ($analyser->interfacesIn($file) as $interface) {
$classesThatImplementInterface[$interface->namespacedName()] = [];
}

foreach ($trait->methods() as $method) {
$methodName = $trait->namespacedName() . '::' . $method->name();
foreach ($analyser->classesIn($file) as $class) {
if ($class->isNamespaced()) {
$this->process($namespaces, $class->namespace(), $file, $class->startLine(), $class->endLine());
}

$this->process($classes, $class->namespacedName(), $file, $class->startLine(), $class->endLine());

foreach ($class->traits() as $traitName) {
if (!isset($traits[$traitName])) {
continue;
}

foreach ($traits[$traitName] as $file => $lines) {
if (!isset($classes[$class->namespacedName()][$file])) {
$classes[$class->namespacedName()][$file] = $lines;

continue;
}

$classes[$class->namespacedName()][$file] = array_unique(
array_merge(
$classes[$class->namespacedName()][$file],
$lines,
),
);
}
}

foreach ($class->methods() as $method) {
$methodName = $class->namespacedName() . '::' . $method->name();

$this->process($methods, $methodName, $file, $method->startLine(), $method->endLine());

foreach (range($method->startLine(), $method->endLine()) as $line) {
$reverseLookup[$file . ':' . $line] = $methodName;
}
}

$classesThatExtendClass[$class->namespacedName()] = [];
$classDetails[] = $class;
}

foreach ($analyser->functionsIn($file) as $function) {
Expand Down
11 changes: 11 additions & 0 deletions tests/_files/Target/ClassUsingTraitUsingTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace SebastianBergmann\CodeCoverage\TestFixture\Target;

final class ClassUsingTraitUsingTrait
{
use TraitTwo;

public function three(): void
{
}
}
9 changes: 9 additions & 0 deletions tests/_files/Target/TraitOne.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php declare(strict_types=1);
namespace SebastianBergmann\CodeCoverage\TestFixture\Target;

trait TraitOne
{
public function one(): void
{
}
}
11 changes: 11 additions & 0 deletions tests/_files/Target/TraitTwo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace SebastianBergmann\CodeCoverage\TestFixture\Target;

trait TraitTwo
{
use TraitOne;

public function two(): void
{
}
}
19 changes: 17 additions & 2 deletions tests/tests/StaticAnalysis/Value/TraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public function testHasNamespace(): void
$this->assertSame('example', $this->trait()->namespace());
}

public function testHasFile(): void
{
$this->assertSame('file.php', $this->trait()->file());
}

public function testHasStartLine(): void
{
$this->assertSame(1, $this->trait()->startLine());
Expand All @@ -42,6 +47,13 @@ public function testHasEndLine(): void
$this->assertSame(2, $this->trait()->endLine());
}

public function testMayHaveTraits(): void
{
$traits = ['trait'];

$this->assertSame($traits, $this->trait(traits: $traits)->traits());
}

public function testMayHaveMethods(): void
{
$methods = [
Expand All @@ -55,20 +67,23 @@ public function testMayHaveMethods(): void
),
];

$this->assertSame($methods, $this->trait($methods)->methods());
$this->assertSame($methods, $this->trait(methods: $methods)->methods());
}

/**
* @param list<non-empty-string> $traits
* @param array<non-empty-string, Method> $methods
*/
private function trait(array $methods = []): Trait_
private function trait(array $traits = [], array $methods = []): Trait_
{
return new Trait_(
'Example',
'example\Example',
'example',
'file.php',
1,
2,
$traits,
$methods,
);
}
Expand Down
Loading

0 comments on commit a0adc21

Please sign in to comment.