Skip to content

Commit 500b160

Browse files
committed
functionMetadata - read #[Pure] from methods in phpstorm-stubs
1 parent c8e4f8b commit 500b160

File tree

4 files changed

+80
-7
lines changed

4 files changed

+80
-7
lines changed

bin/generate-function-metadata.php

+35-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use PhpParser\Node;
55
use PhpParser\NodeTraverser;
66
use PhpParser\NodeVisitor\NameResolver;
7+
use PhpParser\NodeVisitor\NodeConnectingVisitor;
78
use PhpParser\ParserFactory;
89

910
(function () {
@@ -18,17 +19,34 @@
1819
/** @var string[] */
1920
public $functions = [];
2021

22+
/** @var string[] */
23+
public $methods = [];
24+
2125
public function enterNode(Node $node)
2226
{
23-
if (!$node instanceof Node\Stmt\Function_) {
24-
return;
27+
if ($node instanceof Node\Stmt\Function_) {
28+
foreach ($node->attrGroups as $attrGroup) {
29+
foreach ($attrGroup->attrs as $attr) {
30+
if ($attr->name->toString() === \JetBrains\PhpStorm\Pure::class) {
31+
$this->functions[] = $node->namespacedName->toLowerString();
32+
break;
33+
}
34+
}
35+
}
2536
}
2637

27-
foreach ($node->attrGroups as $attrGroup) {
28-
foreach ($attrGroup->attrs as $attr) {
29-
if ($attr->name->toString() === \JetBrains\PhpStorm\Pure::class) {
30-
$this->functions[] = $node->namespacedName->toLowerString();
31-
break;
38+
if ($node instanceof Node\Stmt\ClassMethod) {
39+
$class = $node->getAttribute('parent');
40+
if (!$class instanceof Node\Stmt\ClassLike) {
41+
throw new \PHPStan\ShouldNotHappenException($node->name->toString());
42+
}
43+
$className = $class->namespacedName->toString();
44+
foreach ($node->attrGroups as $attrGroup) {
45+
foreach ($attrGroup->attrs as $attr) {
46+
if ($attr->name->toString() === \JetBrains\PhpStorm\Pure::class) {
47+
$this->methods[] = sprintf('%s::%s', $className, $node->name->toString());
48+
break;
49+
}
3250
}
3351
}
3452
}
@@ -41,6 +59,7 @@ public function enterNode(Node $node)
4159
$path = $stubFile->getPathname();
4260
$traverser = new NodeTraverser();
4361
$traverser->addVisitor(new NameResolver());
62+
$traverser->addVisitor(new NodeConnectingVisitor());
4463
$traverser->addVisitor($visitor);
4564

4665
$traverser->traverse(
@@ -58,6 +77,15 @@ public function enterNode(Node $node)
5877
$metadata[$functionName] = ['hasSideEffects' => false];
5978
}
6079

80+
foreach ($visitor->methods as $methodName) {
81+
if (array_key_exists($methodName, $metadata)) {
82+
if ($metadata[$methodName]['hasSideEffects']) {
83+
throw new \PHPStan\ShouldNotHappenException($methodName);
84+
}
85+
}
86+
$metadata[$methodName] = ['hasSideEffects' => false];
87+
}
88+
6189
ksort($metadata);
6290

6391
$template = <<<'php'

resources/functionMetadata.php

+36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
11
<?php declare(strict_types = 1);
22

33
return [
4+
'Cassandra\\Exception\\AlreadyExistsException::__construct' => ['hasSideEffects' => false],
5+
'Cassandra\\Exception\\AuthenticationException::__construct' => ['hasSideEffects' => false],
6+
'Cassandra\\Exception\\ConfigurationException::__construct' => ['hasSideEffects' => false],
7+
'Cassandra\\Exception\\DivideByZeroException::__construct' => ['hasSideEffects' => false],
8+
'Cassandra\\Exception\\DomainException::__construct' => ['hasSideEffects' => false],
9+
'Cassandra\\Exception\\ExecutionException::__construct' => ['hasSideEffects' => false],
10+
'Cassandra\\Exception\\InvalidArgumentException::__construct' => ['hasSideEffects' => false],
11+
'Cassandra\\Exception\\InvalidQueryException::__construct' => ['hasSideEffects' => false],
12+
'Cassandra\\Exception\\InvalidSyntaxException::__construct' => ['hasSideEffects' => false],
13+
'Cassandra\\Exception\\IsBootstrappingException::__construct' => ['hasSideEffects' => false],
14+
'Cassandra\\Exception\\LogicException::__construct' => ['hasSideEffects' => false],
15+
'Cassandra\\Exception\\OverloadedException::__construct' => ['hasSideEffects' => false],
16+
'Cassandra\\Exception\\ProtocolException::__construct' => ['hasSideEffects' => false],
17+
'Cassandra\\Exception\\RangeException::__construct' => ['hasSideEffects' => false],
18+
'Cassandra\\Exception\\ReadTimeoutException::__construct' => ['hasSideEffects' => false],
19+
'Cassandra\\Exception\\RuntimeException::__construct' => ['hasSideEffects' => false],
20+
'Cassandra\\Exception\\ServerException::__construct' => ['hasSideEffects' => false],
21+
'Cassandra\\Exception\\TimeoutException::__construct' => ['hasSideEffects' => false],
22+
'Cassandra\\Exception\\TruncateException::__construct' => ['hasSideEffects' => false],
23+
'Cassandra\\Exception\\UnauthorizedException::__construct' => ['hasSideEffects' => false],
24+
'Cassandra\\Exception\\UnavailableException::__construct' => ['hasSideEffects' => false],
25+
'Cassandra\\Exception\\UnpreparedException::__construct' => ['hasSideEffects' => false],
26+
'Cassandra\\Exception\\ValidationException::__construct' => ['hasSideEffects' => false],
27+
'Cassandra\\Exception\\WriteTimeoutException::__construct' => ['hasSideEffects' => false],
428
'DateTime::add' => ['hasSideEffects' => true],
529
'DateTime::createFromFormat' => ['hasSideEffects' => false],
630
'DateTime::createFromImmutable' => ['hasSideEffects' => false],
@@ -33,6 +57,18 @@
3357
'DateTimeImmutable::setTimestamp' => ['hasSideEffects' => false],
3458
'DateTimeImmutable::setTimezone' => ['hasSideEffects' => false],
3559
'DateTimeImmutable::sub' => ['hasSideEffects' => false],
60+
'ErrorException::__construct' => ['hasSideEffects' => false],
61+
'Exception::__construct' => ['hasSideEffects' => false],
62+
'Exception::getCode' => ['hasSideEffects' => false],
63+
'Exception::getFile' => ['hasSideEffects' => false],
64+
'Exception::getLine' => ['hasSideEffects' => false],
65+
'Exception::getMessage' => ['hasSideEffects' => false],
66+
'Exception::getPrevious' => ['hasSideEffects' => false],
67+
'Exception::getTrace' => ['hasSideEffects' => false],
68+
'Exception::getTraceAsString' => ['hasSideEffects' => false],
69+
'MemcachedException::__construct' => ['hasSideEffects' => false],
70+
'SQLiteException::__construct' => ['hasSideEffects' => false],
71+
'SoapFault::__construct' => ['hasSideEffects' => false],
3672
'_' => ['hasSideEffects' => false],
3773
'abs' => ['hasSideEffects' => false],
3874
'acos' => ['hasSideEffects' => false],

tests/PHPStan/Rules/Methods/CallToMethodStamentWithoutSideEffectsRuleTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ public function testRule(): void
2828
'Call to static method DateTimeImmutable::createFromFormat() on a separate line has no effect.',
2929
16,
3030
],
31+
[
32+
'Call to method Exception::getCode() on a separate line has no effect.',
33+
21,
34+
],
3135
]);
3236
}
3337

tests/PHPStan/Rules/Methods/data/method-call-statement-no-side-effects.php

+5
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,9 @@ public function doBar(\DateTimeImmutable $dti)
1616
$dti->createFromFormat('Y-m-d', '2019-07-24');
1717
}
1818

19+
public function doBaz(\Exception $e)
20+
{
21+
$e->getCode();
22+
}
23+
1924
}

0 commit comments

Comments
 (0)