Skip to content

Commit dacb337

Browse files
authored
Ensure tested PHP code is valid (#177)
1 parent 57f589c commit dacb337

File tree

15 files changed

+152
-75
lines changed

15 files changed

+152
-75
lines changed

tests/Rule/DeadCodeRuleTest.php

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,20 @@
4242
use ShipMonk\PHPStan\DeadCode\Provider\VendorUsageProvider;
4343
use ShipMonk\PHPStan\DeadCode\Provider\VirtualUsageData;
4444
use ShipMonk\PHPStan\DeadCode\Transformer\FileSystem;
45+
use Throwable;
46+
use Traversable;
47+
use function array_merge;
48+
use function error_reporting;
4549
use function file_get_contents;
4650
use function is_array;
51+
use function iterator_to_array;
52+
use function ob_end_clean;
53+
use function ob_start;
4754
use function preg_replace;
4855
use function str_replace;
4956
use function strpos;
57+
use const E_ALL;
58+
use const E_DEPRECATED;
5059
use const PHP_VERSION_ID;
5160

5261
/**
@@ -126,6 +135,53 @@ public function testDeadWithGroups($files, bool $requirementsMet = true): void
126135
$this->doTestDead($files, $requirementsMet);
127136
}
128137

138+
/**
139+
* Ensure we test real PHP code
140+
* - mainly targets invalid class/trait/iface compositions
141+
*
142+
* @runInSeparateProcess
143+
*/
144+
public function testNoFatalError(): void
145+
{
146+
if (PHP_VERSION_ID < 8_04_00) {
147+
self::markTestSkipped('Requires PHP 8.4+ to allow any PHP feature in test code');
148+
}
149+
150+
// when lowest versions are installed, we get "Implicitly marking parameter xxx as nullable is deprecated" for symfony deps
151+
error_reporting(E_ALL & ~E_DEPRECATED);
152+
153+
$required = [];
154+
155+
$fileProviders = array_merge(
156+
iterator_to_array(self::provideFiles(), false),
157+
iterator_to_array(self::provideGroupingFiles(), false),
158+
iterator_to_array(self::provideAutoRemoveFiles(), false),
159+
);
160+
161+
foreach ($fileProviders as $args) {
162+
$files = is_array($args[0]) ? $args[0] : [$args[0]];
163+
164+
foreach ($files as $file) {
165+
if (isset($required[$file])) {
166+
continue;
167+
}
168+
169+
try {
170+
ob_start();
171+
require $file;
172+
ob_end_clean();
173+
174+
} catch (Throwable $e) {
175+
self::fail("Fatal error in {$e->getFile()}:{$e->getLine()}:\n {$e->getMessage()}");
176+
}
177+
178+
$required[$file] = true;
179+
}
180+
}
181+
182+
$this->expectNotToPerformAssertions();
183+
}
184+
129185
/**
130186
* @param string|non-empty-list<string> $files
131187
*/
@@ -371,9 +427,9 @@ public function testGrouping($files, array $expectedErrors): void
371427
}
372428

373429
/**
374-
* @return iterable<string, array{0: string|list<string>, 1: list<array{0: string, 1: int, 2?: string|null}>}>
430+
* @return Traversable<string, array{0: string|list<string>, 1: list<array{0: string, 1: int, 2?: string|null}>}>
375431
*/
376-
public static function provideGroupingFiles(): iterable
432+
public static function provideGroupingFiles(): Traversable
377433
{
378434
yield 'default' => [
379435
__DIR__ . '/data/grouping/default.php',
@@ -454,9 +510,9 @@ public static function provideGroupingFiles(): iterable
454510
}
455511

456512
/**
457-
* @return array<string, array{0: string|list<string>, 1?: bool}>
513+
* @return Traversable<string, array{0: string|list<string>, 1?: bool}>
458514
*/
459-
public static function provideFiles(): iterable
515+
public static function provideFiles(): Traversable
460516
{
461517
// methods
462518
yield 'method-anonym' => [__DIR__ . '/data/methods/anonym.php'];
@@ -572,9 +628,9 @@ public static function provideFiles(): iterable
572628
}
573629

574630
/**
575-
* @return iterable<string, array{0: string}>
631+
* @return Traversable<string, array{0: string}>
576632
*/
577-
public function provideAutoRemoveFiles(): iterable
633+
public static function provideAutoRemoveFiles(): Traversable
578634
{
579635
yield 'sample' => [__DIR__ . '/data/removing/sample.php'];
580636
yield 'no-namespace' => [__DIR__ . '/data/removing/no-namespace.php'];

tests/Rule/data/constants/constant-function.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ class Test extends TestParent {
1212
}
1313

1414

15-
$fn = 'constant';
16-
echo constant('DeadConstFn\Test::A');
17-
echo constant('Unknown::A');
18-
echo $fn('\DeadConstFn\Test::B');
15+
function test() {
16+
$fn = 'constant';
17+
echo constant('DeadConstFn\Test::A');
18+
echo constant('Unknown::A');
19+
echo $fn('\DeadConstFn\Test::B');
20+
}

tests/Rule/data/constants/mixed/tracked.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,6 @@ function testMethodExists(Iface $iface)
6161
}
6262
}
6363

64-
new Tester();
64+
function test() {
65+
new Tester();
66+
}

tests/Rule/data/excluders/tests/src/code.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ class DeclaredInSrcUsedInSrc {
1212
const CONST = 1;
1313
}
1414

15-
echo DeclaredInSrcUsedInSrc::CONST;
16-
echo DeclaredInSrcUsedInBoth::CONST;
17-
echo DeclaredInTestsUsedInBoth::CONST;
18-
echo DeclaredInTestsUsedInSrc::CONST;
15+
function test2() {
16+
echo DeclaredInSrcUsedInSrc::CONST;
17+
echo DeclaredInSrcUsedInBoth::CONST;
18+
echo DeclaredInTestsUsedInBoth::CONST;
19+
echo DeclaredInTestsUsedInSrc::CONST;
20+
}

tests/Rule/data/excluders/tests/tests/code.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ class DeclaredInTestsUsedInSrc {
1212
const CONST = 1;
1313
}
1414

15-
echo DeclaredInSrcUsedInBoth::CONST;
16-
echo DeclaredInSrcUsedInTests::CONST;
17-
echo DeclaredInTestsUsedInTests::CONST;
18-
echo DeclaredInTestsUsedInBoth::CONST;
15+
function test1() {
16+
echo DeclaredInSrcUsedInBoth::CONST;
17+
echo DeclaredInSrcUsedInTests::CONST;
18+
echo DeclaredInTestsUsedInTests::CONST;
19+
echo DeclaredInTestsUsedInBoth::CONST;
20+
}

tests/Rule/data/methods/array-map-1.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@ public function method7(): void {} // error: Unused DeadMap\Child::method7
3535

3636
}
3737

38-
new ArrayMapTest();
38+
function test() {
39+
new ArrayMapTest();
40+
}

tests/Rule/data/methods/basic.php

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,36 +32,21 @@ public function interfaceMethod(): void
3232
}
3333
}
3434

35-
final class TestChild extends TestParent {
36-
37-
public function __construct(TestA|TestB $class)
38-
{
39-
$class->commonMethod();
40-
$class->differentMethod();
41-
$this->overwrittenParentMethodUsedByChild();
42-
$this->childMethodNowUsed();
43-
}
35+
trait TestTrait {
4436

45-
public function childMethodNowUsed(TestInterface|TestA $class, TestInterface $interface): void
37+
public function __construct() // error: Unused DeadBasic\TestTrait::__construct
4638
{
47-
$class->differentMethod();
48-
$interface->interfaceMethod();
4939
}
5040

51-
public function childMethodUnused(): void // error: Unused DeadBasic\TestChild::childMethodUnused
41+
public function traitMethodUsed(): void
5242
{
5343

5444
}
5545

56-
public function childMethodUsed(): void
46+
public function traitMethodUnused(): void // error: Unused DeadBasic\TestTrait::traitMethodUnused
5747
{
5848

5949
}
60-
61-
public function overwrittenParentMethodUsedByChild(): void
62-
{
63-
$this->parentMethodUsed($this);
64-
}
6550
}
6651

6752
abstract class TestParent {
@@ -89,21 +74,38 @@ public function parentMethodUnused(): void // error: Unused DeadBasic\TestParen
8974
}
9075
}
9176

92-
trait TestTrait {
77+
final class TestChild extends TestParent {
9378

94-
public function __construct() // error: Unused DeadBasic\TestTrait::__construct
79+
public function __construct(TestA|TestB $class)
9580
{
81+
$class->commonMethod();
82+
$class->differentMethod();
83+
$this->overwrittenParentMethodUsedByChild();
84+
$this->childMethodNowUsed();
9685
}
9786

98-
public function traitMethodUsed(): void
87+
public function childMethodNowUsed(TestInterface|TestA $class, TestInterface $interface): void
88+
{
89+
$class->differentMethod();
90+
$interface->interfaceMethod();
91+
}
92+
93+
public function childMethodUnused(): void // error: Unused DeadBasic\TestChild::childMethodUnused
9994
{
10095

10196
}
10297

103-
public function traitMethodUnused(): void // error: Unused DeadBasic\TestTrait::traitMethodUnused
98+
public function childMethodUsed(): void
10499
{
105100

106101
}
102+
103+
public function overwrittenParentMethodUsedByChild(): void
104+
{
105+
$this->parentMethodUsed($this);
106+
}
107107
}
108108

109-
new TestChild();
109+
function test() {
110+
new TestChild();
111+
}

tests/Rule/data/methods/ctor-private.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace CtorPrivate;
44

5-
class StaticCtor implements MyInterface
5+
class StaticCtor
66
{
77
private function __construct()
88
{

tests/Rule/data/methods/cycles.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@ public function b(A $a): void // error: Unused Cycles\B::b
2929
}
3030
}
3131

32-
(new Foo())->recursion1();
32+
function test() {
33+
(new Foo())->recursion1();
34+
}

tests/Rule/data/methods/hierarchy-in-vendor.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
namespace HierachyInVendor;
44

5+
use PHPStan\Rules\Rule;
56
use PHPUnit\Framework\TestCase;
67
use ShipMonk\PHPStan\DeadCode\Rule\RuleTestCase;
78

8-
class SomeTest extends RuleTestCase {
9+
abstract class SomeTest extends RuleTestCase {
910

1011
public static function someMethod(): void {}
1112
}

0 commit comments

Comments
 (0)