Skip to content

Commit 8f4bc33

Browse files
committed
Add PHPStan\dumpPhpDocType() function
1 parent ce3ffbd commit 8f4bc33

File tree

6 files changed

+196
-0
lines changed

6 files changed

+196
-0
lines changed

conf/config.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ extensions:
282282

283283
rules:
284284
- PHPStan\Rules\Debug\DebugScopeRule
285+
- PHPStan\Rules\Debug\DumpPhpDocTypeRule
285286
- PHPStan\Rules\Debug\DumpTypeRule
286287
- PHPStan\Rules\Debug\FileAssertRule
287288

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Debug;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\ReflectionProvider;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use function count;
11+
use function sprintf;
12+
use function strtolower;
13+
14+
/**
15+
* @implements Rule<Node\Expr\FuncCall>
16+
*/
17+
final class DumpPhpDocTypeRule implements Rule
18+
{
19+
20+
public function __construct(private ReflectionProvider $reflectionProvider)
21+
{
22+
}
23+
24+
public function getNodeType(): string
25+
{
26+
return Node\Expr\FuncCall::class;
27+
}
28+
29+
public function processNode(Node $node, Scope $scope): array
30+
{
31+
if (!$node->name instanceof Node\Name) {
32+
return [];
33+
}
34+
35+
$functionName = $this->reflectionProvider->resolveFunctionName($node->name, $scope);
36+
if ($functionName === null) {
37+
return [];
38+
}
39+
40+
if (strtolower($functionName) !== 'phpstan\dumpphpdoctype') {
41+
return [];
42+
}
43+
44+
if (count($node->getArgs()) === 0) {
45+
return [];
46+
}
47+
48+
return [
49+
RuleErrorBuilder::message(
50+
sprintf(
51+
'Dumped type: %s',
52+
$scope->getType($node->getArgs()[0]->value)->toPhpDocNode(),
53+
),
54+
)->nonIgnorable()->identifier('phpstan.dumpPhpDocType')->build(),
55+
];
56+
}
57+
58+
}

src/Rules/Functions/CallToFunctionStatementWithoutSideEffectsRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ final class CallToFunctionStatementWithoutSideEffectsRule implements Rule
2929

3030
public const PHPSTAN_TESTING_FUNCTIONS = [
3131
'PHPStan\\dumpType',
32+
'PHPStan\\dumpPhpDocType',
3233
'PHPStan\\debugScope',
3334
'PHPStan\\Testing\\assertType',
3435
'PHPStan\\Testing\\assertNativeType',

src/dumpType.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,15 @@ function dumpType($value) // phpcs:ignore Squiz.Functions.GlobalFunction.Found
1313
{
1414
return null;
1515
}
16+
17+
/**
18+
* @phpstan-pure
19+
* @param mixed $value
20+
* @return mixed
21+
*
22+
* @throws void
23+
*/
24+
function dumpPhpDocType($value) // phpcs:ignore Squiz.Functions.GlobalFunction.Found
25+
{
26+
return null;
27+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Debug;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<DumpPhpDocTypeRule>
10+
*/
11+
class DumpPhpDocTypeRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new DumpPhpDocTypeRule($this->createReflectionProvider());
17+
}
18+
19+
public function testRuleSymbols(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/dump-phpdoc-type.php'], [
22+
[
23+
"Dumped type: array{'': ''}",
24+
5,
25+
],
26+
[
27+
"Dumped type: array{'\0': 'NUL', NUL: '\0'}",
28+
6,
29+
],
30+
[
31+
"Dumped type: array{' ': 'SP', SP: ' '}",
32+
9,
33+
],
34+
[
35+
"Dumped type: array{'foo ': 'ends with SP', ' foo': 'starts with SP', ' foo ': 'surrounded by SP', foo: 'no SP'}",
36+
10,
37+
],
38+
[
39+
"Dumped type: array{'foo?': 'foo?'}",
40+
13,
41+
],
42+
[
43+
"Dumped type: array{shallwedance: 'yes'}",
44+
14,
45+
],
46+
[
47+
"Dumped type: array{'shallwedance?': 'yes'}",
48+
15,
49+
],
50+
[
51+
"Dumped type: array{'Shall we dance': 'yes'}",
52+
16,
53+
],
54+
[
55+
"Dumped type: array{'Shall we dance?': 'yes'}",
56+
17,
57+
],
58+
[
59+
"Dumped type: array{shall_we_dance: 'yes'}",
60+
18,
61+
],
62+
[
63+
"Dumped type: array{'shall_we_dance?': 'yes'}",
64+
19,
65+
],
66+
[
67+
"Dumped type: array{shall-we-dance: 'yes'}",
68+
20,
69+
],
70+
[
71+
"Dumped type: array{'shall-we-dance?': 'yes'}",
72+
21,
73+
],
74+
[
75+
"Dumped type: array{'Let\'s go': 'Let\'s go'}",
76+
22,
77+
],
78+
[
79+
"Dumped type: array{Foo\\Bar: 'Foo\\\\Bar'}",
80+
23,
81+
],
82+
[
83+
"Dumped type: T",
84+
32,
85+
],
86+
]);
87+
}
88+
89+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace PHPStan;
4+
5+
dumpPhpDocType(['' => '']);
6+
dumpPhpDocType(["\0" => 'NUL', 'NUL' => "\0"]);
7+
8+
// Space
9+
dumpPhpDocType([" " => 'SP', 'SP' => ' ']);
10+
dumpPhpDocType(["foo " => 'ends with SP', " foo" => 'starts with SP', " foo " => 'surrounded by SP', 'foo' => 'no SP']);
11+
12+
// Punctuation marks
13+
dumpPhpDocType(["foo?" => 'foo?']);
14+
dumpPhpDocType(["shallwedance" => 'yes']);
15+
dumpPhpDocType(["shallwedance?" => 'yes']);
16+
dumpPhpDocType(["Shall we dance" => 'yes']);
17+
dumpPhpDocType(["Shall we dance?" => 'yes']);
18+
dumpPhpDocType(["shall_we_dance" => 'yes']);
19+
dumpPhpDocType(["shall_we_dance?" => 'yes']);
20+
dumpPhpDocType(["shall-we-dance" => 'yes']);
21+
dumpPhpDocType(["shall-we-dance?" => 'yes']);
22+
dumpPhpDocType(['Let\'s go' => "Let's go"]);
23+
dumpPhpDocType(['Foo\\Bar' => 'Foo\\Bar']);
24+
25+
/**
26+
* @template T
27+
* @param T $value
28+
* @return T
29+
*/
30+
function id(mixed $value): mixed
31+
{
32+
dumpPhpDocType($value);
33+
34+
return $value;
35+
}

0 commit comments

Comments
 (0)