Skip to content

Commit a0adc21

Browse files
Closes #1055
1 parent 88c7c0b commit a0adc21

File tree

9 files changed

+220
-49
lines changed

9 files changed

+220
-49
lines changed

src/StaticAnalysis/CodeUnitFindingVisitor.php

+45-18
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ public function enterNode(Node $node): null
9696

9797
public function leaveNode(Node $node): null
9898
{
99-
if (!$node instanceof Class_) {
99+
if ($node instanceof Class_ && $node->isAnonymous()) {
100100
return null;
101101
}
102102

103-
if ($node->isAnonymous()) {
103+
if (!$node instanceof Class_ && !$node instanceof Trait_) {
104104
return null;
105105
}
106106

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

119-
$namespacedClassName = $node->namespacedName->toString();
120-
121-
assert(isset($this->classes[$namespacedClassName]));
122-
123-
$this->classes[$namespacedClassName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_(
124-
$this->classes[$namespacedClassName]->name(),
125-
$this->classes[$namespacedClassName]->namespacedName(),
126-
$this->classes[$namespacedClassName]->namespace(),
127-
$this->classes[$namespacedClassName]->file(),
128-
$this->classes[$namespacedClassName]->startLine(),
129-
$this->classes[$namespacedClassName]->endLine(),
130-
$this->classes[$namespacedClassName]->parentClass(),
131-
$this->classes[$namespacedClassName]->interfaces(),
132-
$traits,
133-
$this->classes[$namespacedClassName]->methods(),
134-
);
119+
$this->postProcessClassOrTrait($node, $traits);
135120

136121
return null;
137122
}
@@ -308,8 +293,10 @@ private function processTrait(Trait_ $node): void
308293
$name,
309294
$namespacedName,
310295
$this->namespace($namespacedName, $name),
296+
$this->file,
311297
$node->getStartLine(),
312298
$node->getEndLine(),
299+
[],
313300
$this->processMethods($node->getMethods()),
314301
);
315302
}
@@ -398,4 +385,44 @@ private function typeAsString(Identifier|Name $node): string
398385

399386
return $node->toString();
400387
}
388+
389+
/**
390+
* @param list<non-empty-string> $traits
391+
*/
392+
private function postProcessClassOrTrait(Class_|Trait_ $node, array $traits): void
393+
{
394+
$name = $node->namespacedName->toString();
395+
396+
if ($node instanceof Class_) {
397+
assert(isset($this->classes[$name]));
398+
399+
$this->classes[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_(
400+
$this->classes[$name]->name(),
401+
$this->classes[$name]->namespacedName(),
402+
$this->classes[$name]->namespace(),
403+
$this->classes[$name]->file(),
404+
$this->classes[$name]->startLine(),
405+
$this->classes[$name]->endLine(),
406+
$this->classes[$name]->parentClass(),
407+
$this->classes[$name]->interfaces(),
408+
$traits,
409+
$this->classes[$name]->methods(),
410+
);
411+
412+
return;
413+
}
414+
415+
assert(isset($this->traits[$name]));
416+
417+
$this->traits[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_(
418+
$this->traits[$name]->name(),
419+
$this->traits[$name]->namespacedName(),
420+
$this->traits[$name]->namespace(),
421+
$this->traits[$name]->file(),
422+
$this->traits[$name]->startLine(),
423+
$this->traits[$name]->endLine(),
424+
$traits,
425+
$this->traits[$name]->methods(),
426+
);
427+
}
401428
}

src/StaticAnalysis/Value/Trait_.php

+31-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
private string $namespacedName;
2626
private string $namespace;
2727

28+
/**
29+
* @var non-empty-string
30+
*/
31+
private string $file;
32+
2833
/**
2934
* @var non-negative-int
3035
*/
@@ -35,6 +40,11 @@
3540
*/
3641
private int $endLine;
3742

43+
/**
44+
* @var list<non-empty-string>
45+
*/
46+
private array $traits;
47+
3848
/**
3949
* @var array<non-empty-string, Method>
4050
*/
@@ -43,17 +53,21 @@
4353
/**
4454
* @param non-empty-string $name
4555
* @param non-empty-string $namespacedName
56+
* @param non-empty-string $file
4657
* @param non-negative-int $startLine
4758
* @param non-negative-int $endLine
59+
* @param list<non-empty-string> $traits
4860
* @param array<non-empty-string, Method> $methods
4961
*/
50-
public function __construct(string $name, string $namespacedName, string $namespace, int $startLine, int $endLine, array $methods)
62+
public function __construct(string $name, string $namespacedName, string $namespace, string $file, int $startLine, int $endLine, array $traits, array $methods)
5163
{
5264
$this->name = $name;
5365
$this->namespacedName = $namespacedName;
5466
$this->namespace = $namespace;
67+
$this->file = $file;
5568
$this->startLine = $startLine;
5669
$this->endLine = $endLine;
70+
$this->traits = $traits;
5771
$this->methods = $methods;
5872
}
5973

@@ -83,6 +97,14 @@ public function namespace(): string
8397
return $this->namespace;
8498
}
8599

100+
/**
101+
* @return non-empty-string
102+
*/
103+
public function file(): string
104+
{
105+
return $this->file;
106+
}
107+
86108
/**
87109
* @return non-negative-int
88110
*/
@@ -99,6 +121,14 @@ public function endLine(): int
99121
return $this->endLine;
100122
}
101123

124+
/**
125+
* @return list<non-empty-string>
126+
*/
127+
public function traits(): array
128+
{
129+
return $this->traits;
130+
}
131+
102132
/**
103133
* @return array<non-empty-string, Method>
104134
*/

src/Target/MapBuilder.php

+66-18
Original file line numberDiff line numberDiff line change
@@ -44,47 +44,95 @@ public function build(Filter $filter, FileAnalyser $analyser): array
4444
$reverseLookup = [];
4545

4646
foreach ($filter->files() as $file) {
47-
foreach ($analyser->interfacesIn($file) as $interface) {
48-
$classesThatImplementInterface[$interface->namespacedName()] = [];
49-
}
50-
51-
foreach ($analyser->classesIn($file) as $class) {
52-
if ($class->isNamespaced()) {
53-
$this->process($namespaces, $class->namespace(), $file, $class->startLine(), $class->endLine());
47+
foreach ($analyser->traitsIn($file) as $trait) {
48+
if ($trait->isNamespaced()) {
49+
$this->process($namespaces, $trait->namespace(), $file, $trait->startLine(), $trait->endLine());
5450
}
5551

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

58-
foreach ($class->methods() as $method) {
59-
$methodName = $class->namespacedName() . '::' . $method->name();
54+
foreach ($trait->methods() as $method) {
55+
$methodName = $trait->namespacedName() . '::' . $method->name();
6056

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

6359
foreach (range($method->startLine(), $method->endLine()) as $line) {
6460
$reverseLookup[$file . ':' . $line] = $methodName;
6561
}
6662
}
67-
68-
$classesThatExtendClass[$class->namespacedName()] = [];
69-
$classDetails[] = $class;
7063
}
64+
}
7165

66+
foreach ($filter->files() as $file) {
7267
foreach ($analyser->traitsIn($file) as $trait) {
73-
if ($trait->isNamespaced()) {
74-
$this->process($namespaces, $trait->namespace(), $file, $trait->startLine(), $trait->endLine());
68+
foreach ($trait->traits() as $traitName) {
69+
if (!isset($traits[$traitName])) {
70+
continue;
71+
}
72+
73+
$file = array_keys($traits[$traitName])[0];
74+
75+
if (!isset($traits[$trait->namespacedName()][$file])) {
76+
$traits[$trait->namespacedName()][$file] = $traits[$traitName][$file];
77+
78+
continue;
79+
}
80+
81+
$traits[$trait->namespacedName()][$file] = array_unique(
82+
array_merge(
83+
$traits[$trait->namespacedName()][$file],
84+
$traits[$traitName][$file],
85+
),
86+
);
7587
}
88+
}
89+
}
7690

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

79-
foreach ($trait->methods() as $method) {
80-
$methodName = $trait->namespacedName() . '::' . $method->name();
96+
foreach ($analyser->classesIn($file) as $class) {
97+
if ($class->isNamespaced()) {
98+
$this->process($namespaces, $class->namespace(), $file, $class->startLine(), $class->endLine());
99+
}
100+
101+
$this->process($classes, $class->namespacedName(), $file, $class->startLine(), $class->endLine());
102+
103+
foreach ($class->traits() as $traitName) {
104+
if (!isset($traits[$traitName])) {
105+
continue;
106+
}
107+
108+
foreach ($traits[$traitName] as $file => $lines) {
109+
if (!isset($classes[$class->namespacedName()][$file])) {
110+
$classes[$class->namespacedName()][$file] = $lines;
111+
112+
continue;
113+
}
114+
115+
$classes[$class->namespacedName()][$file] = array_unique(
116+
array_merge(
117+
$classes[$class->namespacedName()][$file],
118+
$lines,
119+
),
120+
);
121+
}
122+
}
123+
124+
foreach ($class->methods() as $method) {
125+
$methodName = $class->namespacedName() . '::' . $method->name();
81126

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

84129
foreach (range($method->startLine(), $method->endLine()) as $line) {
85130
$reverseLookup[$file . ':' . $line] = $methodName;
86131
}
87132
}
133+
134+
$classesThatExtendClass[$class->namespacedName()] = [];
135+
$classDetails[] = $class;
88136
}
89137

90138
foreach ($analyser->functionsIn($file) as $function) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare(strict_types=1);
2+
namespace SebastianBergmann\CodeCoverage\TestFixture\Target;
3+
4+
final class ClassUsingTraitUsingTrait
5+
{
6+
use TraitTwo;
7+
8+
public function three(): void
9+
{
10+
}
11+
}

tests/_files/Target/TraitOne.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php declare(strict_types=1);
2+
namespace SebastianBergmann\CodeCoverage\TestFixture\Target;
3+
4+
trait TraitOne
5+
{
6+
public function one(): void
7+
{
8+
}
9+
}

tests/_files/Target/TraitTwo.php

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare(strict_types=1);
2+
namespace SebastianBergmann\CodeCoverage\TestFixture\Target;
3+
4+
trait TraitTwo
5+
{
6+
use TraitOne;
7+
8+
public function two(): void
9+
{
10+
}
11+
}

tests/tests/StaticAnalysis/Value/TraitTest.php

+17-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public function testHasNamespace(): void
3232
$this->assertSame('example', $this->trait()->namespace());
3333
}
3434

35+
public function testHasFile(): void
36+
{
37+
$this->assertSame('file.php', $this->trait()->file());
38+
}
39+
3540
public function testHasStartLine(): void
3641
{
3742
$this->assertSame(1, $this->trait()->startLine());
@@ -42,6 +47,13 @@ public function testHasEndLine(): void
4247
$this->assertSame(2, $this->trait()->endLine());
4348
}
4449

50+
public function testMayHaveTraits(): void
51+
{
52+
$traits = ['trait'];
53+
54+
$this->assertSame($traits, $this->trait(traits: $traits)->traits());
55+
}
56+
4557
public function testMayHaveMethods(): void
4658
{
4759
$methods = [
@@ -55,20 +67,23 @@ public function testMayHaveMethods(): void
5567
),
5668
];
5769

58-
$this->assertSame($methods, $this->trait($methods)->methods());
70+
$this->assertSame($methods, $this->trait(methods: $methods)->methods());
5971
}
6072

6173
/**
74+
* @param list<non-empty-string> $traits
6275
* @param array<non-empty-string, Method> $methods
6376
*/
64-
private function trait(array $methods = []): Trait_
77+
private function trait(array $traits = [], array $methods = []): Trait_
6578
{
6679
return new Trait_(
6780
'Example',
6881
'example\Example',
6982
'example',
83+
'file.php',
7084
1,
7185
2,
86+
$traits,
7287
$methods,
7388
);
7489
}

0 commit comments

Comments
 (0)