1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Infer types from numeric operations

This commit is contained in:
Matthew Brown 2017-09-06 21:44:26 -04:00
parent 9f9e8a1b30
commit e89a2929d5
3 changed files with 61 additions and 8 deletions

View File

@ -366,7 +366,8 @@ class AssignmentChecker
$stmt->var,
$stmt->expr,
$stmt,
$result_type
$result_type,
$context
);
if ($result_type && $var_id) {

View File

@ -1069,17 +1069,32 @@ class ExpressionChecker
$stmt->left,
$stmt->right,
$stmt,
$result_type
$result_type,
$context
);
if ($result_type) {
$stmt->inferredType = $result_type;
}
} elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Div
&& ($stmt->left->inferredType->hasInt() || $stmt->left->inferredType->hasFloat())
&& ($stmt->right->inferredType->hasInt() || $stmt->right->inferredType->hasFloat())
) {
$stmt->inferredType = Type::combineUnionTypes(Type::getFloat(), Type::getInt());
} elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Div) {
$project_checker = $statements_checker->getFileChecker()->project_checker;
if ($project_checker->infer_types_from_usage
&& isset($stmt->left->inferredType)
&& isset($stmt->right->inferredType)
&& ($stmt->left->inferredType->isMixed() || $stmt->right->inferredType->isMixed())
) {
$source_checker = $statements_checker->getSource();
if ($source_checker instanceof FunctionLikeChecker) {
$function_storage = $source_checker->getFunctionLikeStorage();
$context->inferType($stmt->left, $function_storage, new Type\Union([new TInt, new TFloat]));
$context->inferType($stmt->right, $function_storage, new Type\Union([new TInt, new TFloat]));
}
}
$stmt->inferredType = new Type\Union([new TInt, new TFloat]);
} elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
self::analyzeConcatOp(
$statements_checker,
@ -1132,12 +1147,31 @@ class ExpressionChecker
PhpParser\Node\Expr $left,
PhpParser\Node\Expr $right,
PhpParser\Node $parent,
Type\Union &$result_type = null
Type\Union &$result_type = null,
Context $context = null
) {
$project_checker = $statements_source->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
&& $context
&& $left_type
&& $right_type
&& ($left_type->isMixed() || $right_type->isMixed())
&& ($left_type->hasNumericType() || $right_type->hasNumericType())
) {
$source_checker = $statements_source->getSource();
if ($source_checker instanceof FunctionLikeChecker) {
$function_storage = $source_checker->getFunctionLikeStorage();
$context->inferType($left, $function_storage, new Type\Union([new TInt, new TFloat]));
$context->inferType($right, $function_storage, new Type\Union([new TInt, new TFloat]));
}
}
if ($left_type && $right_type) {
foreach ($left_type->types as $left_type_part) {
foreach ($right_type->types as $right_type_part) {

View File

@ -399,6 +399,24 @@ class AnnotationTest extends TestCase
. ' should be string',
'error_levels' => ['MixedOperand'],
],
'noParamTypeButAddition' => [
'<?php
function fooFoo($a) : void {
echo $a + 5;
}',
'error_message' => 'UntypedParam - src/somefile.php:2 - Parameter $a has no provided type,'
. ' should be int|float',
'error_levels' => ['MixedOperand', 'MixedArgument'],
],
'noParamTypeButDivision' => [
'<?php
function fooFoo($a) : void {
echo $a / 5;
}',
'error_message' => 'UntypedParam - src/somefile.php:2 - Parameter $a has no provided type,'
. ' should be int|float',
'error_levels' => ['MixedOperand', 'MixedArgument'],
],
'noParamTypeButTemplatedString' => [
'<?php
function fooFoo($a) : void {