1
0
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:
Matthew Brown 2020-06-14 11:06:53 -04:00
parent 683bde9540
commit a49a0e5650
4 changed files with 66 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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',
],
];
}
}