Skip to content
Permalink
Browse files

Use types for method_exists checks in some places

  • Loading branch information...
muglug committed Aug 19, 2019
1 parent 80949b2 commit 920c2d83c39c3b25e273a5f1cfe087064ddee4c9
@@ -700,7 +700,17 @@ function (Assertion $assertion) use ($generic_params) : Assertion {
$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 @@ private static function analyzeAtomicCall(
$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 @@ private static function reconcileHasMethod(
$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,15 +18,15 @@ class TObjectWithProperties extends TObject
public $properties;
/**
* @var array<string, bool>
* @var array<string, string>
*/
public $methods;
/**
* 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 @@ function foo(DateTime $d1, DateTime $d2) : void {
);
}',
],
'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();
})();
}
}'
],
];
}

0 comments on commit 920c2d8

Please sign in to comment.
You can’t perform that action at this time.