1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Always evaluate static calls when method call cannot

This commit is contained in:
Brown 2019-04-18 13:51:34 -04:00
parent 50035334d4
commit 4807ebe04a
3 changed files with 114 additions and 99 deletions

View File

@ -174,47 +174,76 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
$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()
)) {
@ -222,9 +251,10 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
}
} 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 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
}
}
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;
}
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;
}
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;
}
return null;
$stmt->inferredType = $return_type;
if ($returns_by_ref) {
if (!$stmt->inferredType) {
$stmt->inferredType = Type::getMixed();
}
$stmt->inferredType = $return_type;
if ($returns_by_ref) {
if (!$stmt->inferredType) {
$stmt->inferredType = Type::getMixed();
}
$stmt->inferredType->by_ref = $returns_by_ref;
}
$stmt->inferredType->by_ref = $returns_by_ref;
}
if ($no_method_id) {
@ -334,7 +336,7 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
// 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

View File

@ -370,6 +370,7 @@ class CallAnalyzer
|| $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 @@ class CallAnalyzer
|| $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 @@ class CallAnalyzer
$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

View File

@ -577,6 +577,16 @@ class MethodCallTest extends TestCase
}',
'error_message' => 'UndefinedClass',
],
'checkMixedMethodCallStaticMethodCallArg' => [
'<?php
class B {}
/** @param mixed $a */
function foo($a) : void {
/** @psalm-suppress MixedMethodCall */
$a->bar(B::bat());
}',
'error_message' => 'UndefinedMethod',
],
];
}
}