Skip to content

Commit 5b9eabc

Browse files
committed
Closures are impure by default
1 parent 3ee15aa commit 5b9eabc

File tree

5 files changed

+30
-4
lines changed

5 files changed

+30
-4
lines changed

src/PhpDoc/TypeNodeResolver.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
3737
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
3838
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
39+
use PHPStan\Reflection\Callables\SimpleImpurePoint;
3940
use PHPStan\Reflection\InitializerExprContext;
4041
use PHPStan\Reflection\InitializerExprTypeResolver;
4142
use PHPStan\Reflection\Native\NativeParameterReflection;
@@ -933,7 +934,13 @@ function (CallableTypeParameterNode $parameterNode) use ($nameScope, &$isVariadi
933934
$mainType instanceof ObjectType
934935
&& $mainType->getClassName() === Closure::class
935936
) {
936-
return new ClosureType($parameters, $returnType, $isVariadic, $templateTypeMap, null, null, $templateTags, [], []);
937+
return new ClosureType($parameters, $returnType, $isVariadic, $templateTypeMap, null, null, $templateTags, [], [
938+
new SimpleImpurePoint(
939+
'functionCall',
940+
'call to a Closure',
941+
false,
942+
),
943+
]);
937944
}
938945

939946
return new ErrorType();

src/Reflection/TrivialParametersAcceptor.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
99
use PHPStan\Type\MixedType;
1010
use PHPStan\Type\Type;
11+
use function sprintf;
1112

1213
/** @api */
1314
class TrivialParametersAcceptor implements ParametersAcceptorWithPhpDocs, CallableParametersAcceptor
1415
{
1516

1617
/** @api */
17-
public function __construct()
18+
public function __construct(private string $callableName = 'callable')
1819
{
1920
}
2021

@@ -68,7 +69,7 @@ public function getImpurePoints(): array
6869
return [
6970
new SimpleImpurePoint(
7071
'functionCall',
71-
'call to a callable',
72+
sprintf('call to a %s', $this->callableName),
7273
false,
7374
),
7475
];

src/Type/ObjectType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ public function isCallable(): TrinaryLogic
12491249
public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
12501250
{
12511251
if ($this->className === Closure::class) {
1252-
return [new TrivialParametersAcceptor()];
1252+
return [new TrivialParametersAcceptor('Closure')];
12531253
}
12541254
$parametersAcceptors = $this->findCallableParametersAcceptors();
12551255
if ($parametersAcceptors === null) {

tests/PHPStan/Rules/Pure/PureFunctionRuleTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ public function testRule(): void
9292
'Impure static variable in pure function PureFunction\functionWithStaticVariable().',
9393
128,
9494
],
95+
[
96+
'Possibly impure call to a Closure in pure function PureFunction\callsClosures().',
97+
139,
98+
],
99+
[
100+
'Possibly impure call to a Closure in pure function PureFunction\callsClosures().',
101+
140,
102+
],
95103
]);
96104
}
97105

tests/PHPStan/Rules/Pure/data/pure-function.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,13 @@ function functionWithStaticVariable(): int
129129

130130
return $v;
131131
}
132+
133+
/**
134+
* @phpstan-pure
135+
* @param \Closure(): int $closure2
136+
*/
137+
function callsClosures(\Closure $closure1, \Closure $closure2): int
138+
{
139+
$closure1();
140+
return $closure2();
141+
}

0 commit comments

Comments
 (0)