From 80494384081ea4b2f82b05d957cd223209fef5c1 Mon Sep 17 00:00:00 2001 From: orklah Date: Sat, 1 Jan 2022 11:13:45 +0100 Subject: [PATCH 1/2] forbid calling impure callable in immutable context --- src/Psalm/Context.php | 2 ++ .../Expression/Call/FunctionCallAnalyzer.php | 2 +- tests/PureAnnotationTest.php | 32 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Psalm/Context.php b/src/Psalm/Context.php index fd90976fd8a..3b9470d6f64 100644 --- a/src/Psalm/Context.php +++ b/src/Psalm/Context.php @@ -353,11 +353,13 @@ class Context /** * @var bool + * Set by @psalm-immutable */ public $mutation_free = false; /** * @var bool + * Set by @psalm-external-mutation-free */ public $external_mutation_free = false; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index 1fa7cdf11b9..bdc1dcbb8aa 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -642,7 +642,7 @@ private static function getAnalyzeNamedExpression( } if ($var_type_part instanceof TClosure || $var_type_part instanceof TCallable) { - if (!$var_type_part->is_pure && $context->pure) { + if (!$var_type_part->is_pure && ($context->pure || $context->mutation_free)) { IssueBuffer::maybeAdd( new ImpureFunctionCall( 'Cannot call an impure function from a mutation-free context', diff --git a/tests/PureAnnotationTest.php b/tests/PureAnnotationTest.php index b9264f20ebe..988459f9167 100644 --- a/tests/PureAnnotationTest.php +++ b/tests/PureAnnotationTest.php @@ -876,6 +876,38 @@ function testImpure(): void ', 'error_message' => 'ImpureMethodCall', ], + 'impureCallableInImmutableContext' => [ + 'fold( + function (): void {} + ); + } + } + + new Whatever(); + ', + 'error_message' => 'ImpureFunctionCall', + ], ]; } } From 8e5c9f02bbef37afb41dffcbaa833c6ae7103caa Mon Sep 17 00:00:00 2001 From: orklah Date: Sat, 1 Jan 2022 11:21:43 +0100 Subject: [PATCH 2/2] fix test --- tests/Template/ClassTemplateTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index ff2f03cfec1..eca908c2bdf 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -2991,7 +2991,7 @@ public function toString(): string /** * @psalm-template TResult - * @psalm-param callable(self::READ_UNCOMMITTED): TResult $readUncommitted + * @psalm-param pure-callable(self::READ_UNCOMMITTED): TResult $readUncommitted * @psalm-return TResult */ public function resolve(callable $readUncommitted) {