Skip to content
Permalink
Browse files

Always evaluate static calls when method call cannot

  • Loading branch information...
muglug committed Apr 18, 2019
1 parent 5003533 commit 4807ebe04ab14df271e446fbc1f74a2133f7153a
@@ -174,57 +174,87 @@ public static function analyze(
$no_method_id = false;
if ($class_type) {
$return_type = null;
if (!$class_type) {
$class_type = Type::getMixed();
}
$lhs_types = $class_type->getTypes();
$return_type = null;
foreach ($lhs_types as $lhs_type_part) {
$result = self::analyzeAtomicCall(
$statements_analyzer,
$stmt,
$codebase,
$context,
$lhs_type_part,
$lhs_var_id,
$return_type,
$returns_by_ref,
$has_mock,
$has_valid_method_call_type,
$has_mixed_method_call,
$invalid_method_call_types,
$existent_method_ids,
$non_existent_class_method_ids,
$non_existent_interface_method_ids
);
$lhs_types = $class_type->getTypes();
if ($result === false) {
return false;
}
foreach ($lhs_types as $lhs_type_part) {
$result = self::analyzeAtomicCall(
$statements_analyzer,
$stmt,
$codebase,
$context,
$lhs_type_part,
$lhs_var_id,
$return_type,
$returns_by_ref,
$has_mock,
$has_valid_method_call_type,
$has_mixed_method_call,
$invalid_method_call_types,
$existent_method_ids,
$non_existent_class_method_ids,
$non_existent_interface_method_ids
);
if ($result === true) {
$no_method_id = true;
}
if ($result === false) {
return false;
}
if ($invalid_method_call_types) {
$invalid_class_type = $invalid_method_call_types[0];
if ($result === true) {
$no_method_id = true;
}
}
if ($has_valid_method_call_type || $has_mixed_method_call) {
if ($invalid_method_call_types) {
$invalid_class_type = $invalid_method_call_types[0];
if ($has_valid_method_call_type || $has_mixed_method_call) {
if (IssueBuffer::accepts(
new PossiblyInvalidMethodCall(
'Cannot call method on possible ' . $invalid_class_type . ' variable ' . $lhs_var_id,
new CodeLocation($source, $stmt->name)
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
} else {
if (IssueBuffer::accepts(
new InvalidMethodCall(
'Cannot call method on ' . $invalid_class_type . ' variable ' . $lhs_var_id,
new CodeLocation($source, $stmt->name)
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
}
}
if ($non_existent_class_method_ids) {
if ($context->check_methods) {
if ($existent_method_ids || $has_mixed_method_call) {
if (IssueBuffer::accepts(
new PossiblyInvalidMethodCall(
'Cannot call method on possible ' . $invalid_class_type . ' variable ' . $lhs_var_id,
new CodeLocation($source, $stmt->name)
new PossiblyUndefinedMethod(
'Method ' . $non_existent_class_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_class_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
} else {
if (IssueBuffer::accepts(
new InvalidMethodCall(
'Cannot call method on ' . $invalid_class_type . ' variable ' . $lhs_var_id,
new CodeLocation($source, $stmt->name)
new UndefinedMethod(
'Method ' . $non_existent_class_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_class_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
@@ -233,75 +263,47 @@ public static function analyze(
}
}
if ($non_existent_class_method_ids) {
if ($context->check_methods) {
if ($existent_method_ids || $has_mixed_method_call) {
if (IssueBuffer::accepts(
new PossiblyUndefinedMethod(
'Method ' . $non_existent_class_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_class_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
} else {
if (IssueBuffer::accepts(
new UndefinedMethod(
'Method ' . $non_existent_class_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_class_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
}
}
return null;
}
return null;
}
if ($non_existent_interface_method_ids) {
if ($context->check_methods) {
if ($existent_method_ids || $has_mixed_method_call) {
if (IssueBuffer::accepts(
new PossiblyUndefinedMethod(
'Method ' . $non_existent_interface_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_interface_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
} else {
if (IssueBuffer::accepts(
new UndefinedInterfaceMethod(
'Method ' . $non_existent_interface_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_interface_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
if ($non_existent_interface_method_ids) {
if ($context->check_methods) {
if ($existent_method_ids || $has_mixed_method_call) {
if (IssueBuffer::accepts(
new PossiblyUndefinedMethod(
'Method ' . $non_existent_interface_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_interface_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
} else {
if (IssueBuffer::accepts(
new UndefinedInterfaceMethod(
'Method ' . $non_existent_interface_method_ids[0] . ' does not exist',
new CodeLocation($source, $stmt->name),
$non_existent_interface_method_ids[0]
),
$statements_analyzer->getSuppressedIssues()
)) {
// keep going
}
}
return null;
}
$stmt->inferredType = $return_type;
return null;
}
if ($returns_by_ref) {
if (!$stmt->inferredType) {
$stmt->inferredType = Type::getMixed();
}
$stmt->inferredType = $return_type;
$stmt->inferredType->by_ref = $returns_by_ref;
if ($returns_by_ref) {
if (!$stmt->inferredType) {
$stmt->inferredType = Type::getMixed();
}
$stmt->inferredType->by_ref = $returns_by_ref;
}
if ($no_method_id) {
@@ -334,7 +336,7 @@ public static function analyze(
// if we called a method on this nullable variable, remove the nullable status here
// because any further calls must have worked
if ($lhs_var_id
&& $class_type
&& !$class_type->isMixed()
&& $has_valid_method_call_type
&& !$has_mixed_method_call
&& !$invalid_method_call_types
@@ -370,6 +370,7 @@ protected static function checkFunctionArguments(
|| $arg->value instanceof PhpParser\Node\Expr\ConstFetch
|| $arg->value instanceof PhpParser\Node\Expr\FuncCall
|| $arg->value instanceof PhpParser\Node\Expr\MethodCall
|| $arg->value instanceof PhpParser\Node\Expr\StaticCall
|| $arg->value instanceof PhpParser\Node\Expr\Assign
|| $arg->value instanceof PhpParser\Node\Expr\Array_
)
@@ -448,6 +449,7 @@ private static function evaluateAribitraryParam(
|| $arg->value instanceof PhpParser\Node\Expr\ConstFetch
|| $arg->value instanceof PhpParser\Node\Expr\FuncCall
|| $arg->value instanceof PhpParser\Node\Expr\MethodCall
|| $arg->value instanceof PhpParser\Node\Expr\StaticCall
|| $arg->value instanceof PhpParser\Node\Expr\Assign
|| $arg->value instanceof PhpParser\Node\Expr\ArrayDimFetch
|| $arg->value instanceof PhpParser\Node\Expr\Array_
@@ -1278,6 +1280,7 @@ private static function handlePossiblyMatchingByRefParam(
$arg->value instanceof PhpParser\Node\Expr\ConstFetch
|| $arg->value instanceof PhpParser\Node\Expr\FuncCall
|| $arg->value instanceof PhpParser\Node\Expr\MethodCall
|| $arg->value instanceof PhpParser\Node\Expr\StaticCall
) && (
!isset($arg->value->inferredType)
|| !$arg->value->inferredType->by_ref
@@ -577,6 +577,16 @@ public function foo(string $s): void
}',
'error_message' => 'UndefinedClass',
],
'checkMixedMethodCallStaticMethodCallArg' => [
'<?php
class B {}
/** @param mixed $a */
function foo($a) : void {
/** @psalm-suppress MixedMethodCall */
$a->bar(B::bat());
}',
'error_message' => 'UndefinedMethod',
],
];
}
}

0 comments on commit 4807ebe

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