Skip to content

Commit 8049438

Browse files
committed
forbid calling impure callable in immutable context
1 parent f9b3600 commit 8049438

File tree

3 files changed

+35
-1
lines changed

3 files changed

+35
-1
lines changed

src/Psalm/Context.php

+2
Original file line numberDiff line numberDiff line change
@@ -353,11 +353,13 @@ class Context
353353

354354
/**
355355
* @var bool
356+
* Set by @psalm-immutable
356357
*/
357358
public $mutation_free = false;
358359

359360
/**
360361
* @var bool
362+
* Set by @psalm-external-mutation-free
361363
*/
362364
public $external_mutation_free = false;
363365

src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ private static function getAnalyzeNamedExpression(
642642
}
643643

644644
if ($var_type_part instanceof TClosure || $var_type_part instanceof TCallable) {
645-
if (!$var_type_part->is_pure && $context->pure) {
645+
if (!$var_type_part->is_pure && ($context->pure || $context->mutation_free)) {
646646
IssueBuffer::maybeAdd(
647647
new ImpureFunctionCall(
648648
'Cannot call an impure function from a mutation-free context',

tests/PureAnnotationTest.php

+32
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,38 @@ function testImpure(): void
876876
',
877877
'error_message' => 'ImpureMethodCall',
878878
],
879+
'impureCallableInImmutableContext' => [
880+
'<?php
881+
882+
/**
883+
* @psalm-immutable
884+
*/
885+
class Either
886+
{
887+
/**
888+
* @psalm-param callable $_
889+
*/
890+
public function fold($_): void
891+
{
892+
$_();
893+
}
894+
}
895+
896+
class Whatever
897+
{
898+
public function __construct()
899+
{
900+
$either = new Either();
901+
$either->fold(
902+
function (): void {}
903+
);
904+
}
905+
}
906+
907+
new Whatever();
908+
',
909+
'error_message' => 'ImpureFunctionCall',
910+
],
879911
];
880912
}
881913
}

0 commit comments

Comments
 (0)