Skip to content

Commit 8853da9

Browse files
committed
Treat #[Pure(true)] in PhpStorm stubs as hasSideEffects => true
1 parent d117c54 commit 8853da9

File tree

2 files changed

+21
-15
lines changed

2 files changed

+21
-15
lines changed

Diff for: bin/functionMetadata_original.php

-3
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,13 @@
7070
'chown' => ['hasSideEffects' => true],
7171
'copy' => ['hasSideEffects' => true],
7272
'count' => ['hasSideEffects' => false],
73-
'connection_aborted' => ['hasSideEffects' => true],
74-
'connection_status' => ['hasSideEffects' => true],
7573
'error_log' => ['hasSideEffects' => true],
7674
'fclose' => ['hasSideEffects' => true],
7775
'fflush' => ['hasSideEffects' => true],
7876
'fgetc' => ['hasSideEffects' => true],
7977
'fgetcsv' => ['hasSideEffects' => true],
8078
'fgets' => ['hasSideEffects' => true],
8179
'fgetss' => ['hasSideEffects' => true],
82-
'file_get_contents' => ['hasSideEffects' => true],
8380
'file_put_contents' => ['hasSideEffects' => true],
8481
'flock' => ['hasSideEffects' => true],
8582
'fopen' => ['hasSideEffects' => true],

Diff for: bin/generate-function-metadata.php

+21-12
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,27 @@
2525
/** @var string[] */
2626
public array $functions = [];
2727

28+
/** @var list<string> */
29+
public array $impureFunctions = [];
30+
2831
/** @var string[] */
2932
public array $methods = [];
3033

3134
public function enterNode(Node $node)
3235
{
3336
if ($node instanceof Node\Stmt\Function_) {
37+
assert(isset($node->namespacedName));
38+
$functionName = $node->namespacedName->toLowerString();
3439
foreach ($node->attrGroups as $attrGroup) {
3540
foreach ($attrGroup->attrs as $attr) {
3641
if ($attr->name->toString() === Pure::class) {
37-
$this->functions[] = $node->namespacedName->toLowerString();
42+
// PhpStorm stub's #[Pure(true)] mean sthe function has side effects but its return value is important.
43+
// In PHPStan's criteria, these functions are simply considered as ['hasSideEffect' => true].
44+
if (isset($attr->args[0]->value->name->name) && $attr->args[0]->value->name->name === 'true') {
45+
$this->impureFunctions[] = $functionName;
46+
} else {
47+
$this->functions[] = $functionName;
48+
}
3849
break 2;
3950
}
4051
}
@@ -74,26 +85,24 @@ public function enterNode(Node $node)
7485
);
7586
}
7687

88+
/** @var array<string, array{hasSideEffects: bool}> $metadata */
7789
$metadata = require __DIR__ . '/functionMetadata_original.php';
7890
foreach ($visitor->functions as $functionName) {
7991
if (array_key_exists($functionName, $metadata)) {
8092
if ($metadata[$functionName]['hasSideEffects']) {
81-
if (in_array($functionName, [
82-
'mt_rand',
83-
'rand',
84-
'random_bytes',
85-
'random_int',
86-
'connection_aborted',
87-
'connection_status',
88-
'file_get_contents',
89-
], true)) {
90-
continue;
91-
}
9293
throw new ShouldNotHappenException($functionName);
9394
}
9495
}
9596
$metadata[$functionName] = ['hasSideEffects' => false];
9697
}
98+
foreach ($visitor->impureFunctions as $functionName) {
99+
if (array_key_exists($functionName, $metadata)) {
100+
if ($metadata[$functionName]['hasSideEffects']) {
101+
throw new ShouldNotHappenException($functionName);
102+
}
103+
}
104+
$metadata[$functionName] = ['hasSideEffects' => true];
105+
}
97106

98107
foreach ($visitor->methods as $methodName) {
99108
if (array_key_exists($methodName, $metadata)) {

0 commit comments

Comments
 (0)