mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Allow param types to be inferred from presence of concat ops
This commit is contained in:
parent
a725009181
commit
a0c27cee4f
@ -112,6 +112,11 @@ class FunctionChecker extends FunctionLikeChecker
|
||||
return $file_storage->functions[$function_id];
|
||||
}
|
||||
|
||||
// closures can be returned here
|
||||
if (isset($file_storage->functions[$function_id])) {
|
||||
return $file_storage->functions[$function_id];
|
||||
}
|
||||
|
||||
if (!isset($file_storage->declaring_function_ids[$function_id])) {
|
||||
throw new \UnexpectedValueException(
|
||||
'Expecting ' . $function_id . ' to have storage in ' . $file_path
|
||||
|
@ -385,6 +385,7 @@ class AssignmentChecker
|
||||
$statements_checker,
|
||||
$stmt->var,
|
||||
$stmt->expr,
|
||||
$context,
|
||||
$result_type
|
||||
);
|
||||
|
||||
|
@ -7,6 +7,8 @@ use Psalm\Checker\ClassChecker;
|
||||
use Psalm\Checker\ClassLikeChecker;
|
||||
use Psalm\Checker\ClosureChecker;
|
||||
use Psalm\Checker\CommentChecker;
|
||||
use Psalm\Checker\FunctionChecker;
|
||||
use Psalm\Checker\FunctionLikeChecker;
|
||||
use Psalm\Checker\MethodChecker;
|
||||
use Psalm\Checker\ProjectChecker;
|
||||
use Psalm\Checker\Statements\Expression\AssignmentChecker;
|
||||
@ -1084,6 +1086,7 @@ class ExpressionChecker
|
||||
$statements_checker,
|
||||
$stmt->left,
|
||||
$stmt->right,
|
||||
$context,
|
||||
$result_type
|
||||
);
|
||||
|
||||
@ -1304,12 +1307,79 @@ class ExpressionChecker
|
||||
StatementsChecker $statements_checker,
|
||||
PhpParser\Node\Expr $left,
|
||||
PhpParser\Node\Expr $right,
|
||||
Context $context,
|
||||
Type\Union &$result_type = null
|
||||
) {
|
||||
$project_checker = $statements_checker->getFileChecker()->project_checker;
|
||||
|
||||
$left_type = isset($left->inferredType) ? $left->inferredType : null;
|
||||
$right_type = isset($right->inferredType) ? $right->inferredType : null;
|
||||
$config = Config::getInstance();
|
||||
|
||||
if ($project_checker->infer_types_from_usage
|
||||
&& $left_type
|
||||
&& $right_type
|
||||
&& ($left_type->isMixed() || $right_type->isMixed())
|
||||
) {
|
||||
$source_checker = $statements_checker->getSource();
|
||||
|
||||
if ($source_checker instanceof FunctionLikeChecker) {
|
||||
$function_id = $source_checker->getMethodId();
|
||||
|
||||
if (strpos($function_id, '::')) {
|
||||
$declaring_method_id = MethodChecker::getDeclaringMethodId($project_checker, $function_id);
|
||||
|
||||
if (!$declaring_method_id) {
|
||||
throw new \UnexpectedValueException('This should never happen');
|
||||
}
|
||||
|
||||
$function_storage = MethodChecker::getStorage($project_checker, $declaring_method_id);
|
||||
} else {
|
||||
$function_storage = FunctionChecker::getStorage($statements_checker, $function_id);
|
||||
}
|
||||
|
||||
if ($function_storage->param_types) {
|
||||
if ($left_type
|
||||
&& $left_type->isMixed()
|
||||
&& $left instanceof PhpParser\Node\Expr\Variable
|
||||
&& is_string($left->name)
|
||||
&& !isset($context->assigned_vars['$' . $left->name])
|
||||
&& array_key_exists($left->name, $function_storage->param_types)
|
||||
&& !$function_storage->param_types[$left->name]
|
||||
) {
|
||||
if (isset($context->possible_param_types[$left->name])) {
|
||||
$context->possible_param_types[$left->name] = Type::combineUnionTypes(
|
||||
$context->possible_param_types[$left->name],
|
||||
Type::getString()
|
||||
);
|
||||
} else {
|
||||
$context->possible_param_types[$left->name] = Type::getString();
|
||||
$context->vars_in_scope['$' . $left->name] = Type::getString();
|
||||
}
|
||||
}
|
||||
|
||||
if ($right_type
|
||||
&& $right_type->isMixed()
|
||||
&& $right instanceof PhpParser\Node\Expr\Variable
|
||||
&& is_string($right->name)
|
||||
&& !isset($context->assigned_vars['$' . $right->name])
|
||||
&& array_key_exists($right->name, $function_storage->param_types)
|
||||
&& !$function_storage->param_types[$right->name]
|
||||
) {
|
||||
if (isset($context->possible_param_types[$right->name])) {
|
||||
$context->possible_param_types[$right->name] = Type::combineUnionTypes(
|
||||
$context->possible_param_types[$right->name],
|
||||
Type::getString()
|
||||
);
|
||||
} else {
|
||||
$context->possible_param_types[$right->name] = Type::getString();
|
||||
$context->vars_in_scope['$' . $right->name] = Type::getString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($left_type && $right_type) {
|
||||
$result_type = Type::getString();
|
||||
|
||||
|
@ -390,6 +390,15 @@ class AnnotationTest extends TestCase
|
||||
. ' should be string',
|
||||
'error_levels' => ['MixedArgument'],
|
||||
],
|
||||
'noParamTypeButConcat' => [
|
||||
'<?php
|
||||
function fooFoo($a) : void {
|
||||
echo $a . "foo";
|
||||
}',
|
||||
'error_message' => 'UntypedParam - src/somefile.php:2 - Parameter $a has no provided type,'
|
||||
. ' should be string',
|
||||
'error_levels' => ['MixedOperand'],
|
||||
],
|
||||
'noStringIntParamType' => [
|
||||
'<?php
|
||||
function fooFoo($a) : void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user