mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Fix #3551 - count method can be impure
This commit is contained in:
parent
683bde9540
commit
a49a0e5650
@ -1070,7 +1070,13 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
$must_use = true;
|
||||
|
||||
$callmap_function_pure = $function_id && $in_call_map
|
||||
? $codebase->functions->isCallMapFunctionPure($codebase, $function_id, $stmt->args, $must_use)
|
||||
? $codebase->functions->isCallMapFunctionPure(
|
||||
$codebase,
|
||||
$statements_analyzer->node_data,
|
||||
$function_id,
|
||||
$stmt->args,
|
||||
$must_use
|
||||
)
|
||||
: null;
|
||||
|
||||
if ((!$in_call_map
|
||||
|
@ -1973,6 +1973,7 @@ class TypeAnalyzer
|
||||
|
||||
$matching_callable->is_pure = $codebase->functions->isCallMapFunctionPure(
|
||||
$codebase,
|
||||
$statements_analyzer ? $statements_analyzer->node_data : null,
|
||||
$input_type_part->value,
|
||||
null,
|
||||
$must_use
|
||||
|
@ -16,6 +16,8 @@ use function strpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
use Closure;
|
||||
use Psalm\Type\Atomic\TNamedObject;
|
||||
use Psalm\Internal\MethodIdentifier;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -290,6 +292,7 @@ class Functions
|
||||
*/
|
||||
public function isCallMapFunctionPure(
|
||||
Codebase $codebase,
|
||||
?\Psalm\NodeTypeProvider $type_provider,
|
||||
string $function_id,
|
||||
?array $args,
|
||||
bool &$must_use = true
|
||||
@ -403,6 +406,28 @@ class Functions
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($function_id === 'count' && isset($args[0]) && $type_provider) {
|
||||
$count_type = $type_provider->getType($args[0]->value);
|
||||
|
||||
if ($count_type) {
|
||||
foreach ($count_type->getAtomicTypes() as $atomic_count_type) {
|
||||
if ($atomic_count_type instanceof TNamedObject) {
|
||||
$count_method_id = new MethodIdentifier(
|
||||
$atomic_count_type->value,
|
||||
'count'
|
||||
);
|
||||
|
||||
try {
|
||||
$method_storage = $codebase->methods->getStorage($count_method_id);
|
||||
return $method_storage->mutation_free;
|
||||
} catch (\Exception $e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$function_callable = \Psalm\Internal\Codebase\InternalCallMapHandler::getCallableFromCallMapById(
|
||||
$codebase,
|
||||
$function_id,
|
||||
|
@ -301,6 +301,22 @@ class PureAnnotationTest extends TestCase
|
||||
return $sum;
|
||||
}'
|
||||
],
|
||||
'countMethodCanBePure' => [
|
||||
'<?php
|
||||
class A implements Countable {
|
||||
/** @psalm-mutation-free */
|
||||
public function count(): int {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
function thePurest(A $countable): int {
|
||||
return count($countable);
|
||||
}',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -537,6 +553,23 @@ class PureAnnotationTest extends TestCase
|
||||
}',
|
||||
'error_message' => 'ImpureMethodCall'
|
||||
],
|
||||
'countCanBeImpure' => [
|
||||
'<?php
|
||||
class A implements Countable {
|
||||
public function count(): int {
|
||||
echo "oops";
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
function thePurest(A $countable): int {
|
||||
return count($countable);
|
||||
}',
|
||||
'error_message' => 'ImpureFunctionCall',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user