mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Use types for method_exists checks in some places
This commit is contained in:
parent
80949b2b29
commit
920c2d83c3
@ -700,7 +700,17 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
||||
$first_arg = isset($stmt->args[0]) ? $stmt->args[0] : null;
|
||||
|
||||
if ($function->parts === ['method_exists']) {
|
||||
$context->check_methods = false;
|
||||
$second_arg = isset($stmt->args[1]) ? $stmt->args[1] : null;
|
||||
|
||||
if ($first_arg
|
||||
&& $first_arg->value instanceof PhpParser\Node\Expr\Variable
|
||||
&& $second_arg
|
||||
&& $second_arg->value instanceof PhpParser\Node\Scalar\String_
|
||||
) {
|
||||
// do nothing
|
||||
} else {
|
||||
$context->check_methods = false;
|
||||
}
|
||||
} elseif ($function->parts === ['class_exists']) {
|
||||
if ($first_arg) {
|
||||
if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) {
|
||||
|
@ -495,22 +495,29 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
|
||||
$has_mixed_method_call = true;
|
||||
|
||||
if ($stmt->name instanceof PhpParser\Node\Identifier) {
|
||||
$codebase->analyzer->addMixedMemberName(
|
||||
strtolower($stmt->name->name),
|
||||
$context->calling_method_id ?: $statements_analyzer->getFileName()
|
||||
);
|
||||
}
|
||||
if ($lhs_type_part instanceof Type\Atomic\TObjectWithProperties
|
||||
&& $stmt->name instanceof PhpParser\Node\Identifier
|
||||
&& isset($lhs_type_part->methods[$stmt->name->name])
|
||||
) {
|
||||
$existent_method_ids[] = $lhs_type_part->methods[$stmt->name->name];
|
||||
} else {
|
||||
if ($stmt->name instanceof PhpParser\Node\Identifier) {
|
||||
$codebase->analyzer->addMixedMemberName(
|
||||
strtolower($stmt->name->name),
|
||||
$context->calling_method_id ?: $statements_analyzer->getFileName()
|
||||
);
|
||||
}
|
||||
|
||||
if ($context->check_methods) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MixedMethodCall(
|
||||
'Cannot determine the type of the object on the left hand side of this expression',
|
||||
new CodeLocation($source, $stmt->name)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
if ($context->check_methods) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MixedMethodCall(
|
||||
'Cannot determine the type of the object on the left hand side of this expression',
|
||||
new CodeLocation($source, $stmt->name)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -820,12 +820,18 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
$object_types[] = $type;
|
||||
|
||||
if (!$codebase->methodExists($type->value . '::' . $method_id)) {
|
||||
$obj = new Atomic\TObjectWithProperties([], [$method_id => true]);
|
||||
$obj = new Atomic\TObjectWithProperties(
|
||||
[],
|
||||
[$method_id => $type->value . '::' . $method_id]
|
||||
);
|
||||
$type->extra_types[$obj->getKey()] = $obj;
|
||||
$did_remove_type = true;
|
||||
}
|
||||
} elseif ($type instanceof TObject || $type instanceof TMixed) {
|
||||
$object_types[] = new Atomic\TObjectWithProperties([], [$method_id => true]);
|
||||
$object_types[] = new Atomic\TObjectWithProperties(
|
||||
[],
|
||||
[$method_id => 'object::' . $method_id]
|
||||
);
|
||||
$did_remove_type = true;
|
||||
} elseif ($type instanceof TTemplateParam) {
|
||||
$object_types[] = $type;
|
||||
|
@ -18,7 +18,7 @@ class TObjectWithProperties extends TObject
|
||||
public $properties;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $methods;
|
||||
|
||||
@ -26,7 +26,7 @@ class TObjectWithProperties extends TObject
|
||||
* Constructs a new instance of a generic type
|
||||
*
|
||||
* @param array<string|int, Union> $properties
|
||||
* @param array<string, bool> $methods
|
||||
* @param array<string, string> $methods
|
||||
*/
|
||||
public function __construct(array $properties, array $methods = [])
|
||||
{
|
||||
|
@ -341,6 +341,31 @@ class MethodCallTest extends TestCase
|
||||
);
|
||||
}',
|
||||
],
|
||||
'callMethodAfterCheckingExistence' => [
|
||||
'<?php
|
||||
class A {}
|
||||
|
||||
function foo(A $a) : void {
|
||||
if (method_exists($a, "bar")) {
|
||||
/** @psalm-suppress MixedArgument */
|
||||
echo $a->bar();
|
||||
}
|
||||
}'
|
||||
],
|
||||
'callMethodAfterCheckingExistenceInClosure' => [
|
||||
'<?php
|
||||
class A {}
|
||||
|
||||
function foo(A $a) : void {
|
||||
if (method_exists($a, "bar")) {
|
||||
(function() use ($a) : void {
|
||||
/** @psalm-suppress MixedArgument */
|
||||
echo $a->bar();
|
||||
})();
|
||||
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user