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

Refine inferred type when possible

This commit is contained in:
Matthew Brown 2019-02-02 12:10:52 -05:00
parent db89b3cc3f
commit 3a01afbe0a
6 changed files with 97 additions and 17 deletions

View File

@ -390,7 +390,8 @@ class Context
public function inferType(
PhpParser\Node\Expr $expr,
FunctionLikeStorage $function_storage,
Type\Union $inferred_type
Type\Union $inferred_type,
Codebase $codebase
) {
if (!isset($expr->inferredType)) {
return;
@ -398,20 +399,27 @@ class Context
$expr_type = $expr->inferredType;
if (($expr_type->hasMixed() || $expr_type->getId() === $inferred_type->getId())
&& $expr instanceof PhpParser\Node\Expr\Variable
if ($expr instanceof PhpParser\Node\Expr\Variable
&& is_string($expr->name)
&& !isset($this->assigned_var_ids['$' . $expr->name])
&& array_key_exists($expr->name, $function_storage->param_types)
&& !$function_storage->param_types[$expr->name]
) {
if (isset($this->possible_param_types[$expr->name])) {
$this->possible_param_types[$expr->name] = Type::combineUnionTypes(
$this->possible_param_types[$expr->name],
$inferred_type
);
if (\Psalm\Internal\Analyzer\TypeAnalyzer::isContainedBy(
$codebase,
$inferred_type,
$this->possible_param_types[$expr->name]
)) {
$this->possible_param_types[$expr->name] = clone $inferred_type;
} else {
$this->possible_param_types[$expr->name] = Type::combineUnionTypes(
$this->possible_param_types[$expr->name],
$inferred_type
);
}
} else {
$this->possible_param_types[$expr->name] = $inferred_type;
$this->possible_param_types[$expr->name] = clone $inferred_type;
$this->vars_in_scope['$' . $expr->name] = clone $inferred_type;
}
}

View File

@ -551,8 +551,18 @@ class BinaryOpAnalyzer
if ($source_analyzer instanceof FunctionLikeAnalyzer) {
$function_storage = $source_analyzer->getFunctionLikeStorage($statements_analyzer);
$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]));
$context->inferType(
$stmt->left,
$function_storage,
new Type\Union([new TInt, new TFloat]),
$codebase
);
$context->inferType(
$stmt->right,
$function_storage,
new Type\Union([new TInt, new TFloat]),
$codebase
);
}
}
@ -657,8 +667,18 @@ class BinaryOpAnalyzer
) {
$function_storage = $source_analyzer->getFunctionLikeStorage($statements_source);
$context->inferType($left, $function_storage, new Type\Union([new TInt, new TFloat]));
$context->inferType($right, $function_storage, new Type\Union([new TInt, new TFloat]));
$context->inferType(
$left,
$function_storage,
new Type\Union([new TInt, new TFloat]),
$codebase
);
$context->inferType(
$right,
$function_storage,
new Type\Union([new TInt, new TFloat]),
$codebase
);
}
}
@ -1198,8 +1218,18 @@ class BinaryOpAnalyzer
if ($source_analyzer instanceof FunctionLikeAnalyzer) {
$function_storage = $source_analyzer->getFunctionLikeStorage($statements_analyzer);
$context->inferType($left, $function_storage, Type::getString());
$context->inferType($right, $function_storage, Type::getString());
$context->inferType(
$left,
$function_storage,
new Type\Union([new Type\Atomic\TString, new Type\Atomic\TInt, new Type\Atomic\TFloat]),
$codebase
);
$context->inferType(
$right,
$function_storage,
new Type\Union([new Type\Atomic\TString, new Type\Atomic\TInt, new Type\Atomic\TFloat]),
$codebase
);
}
}

View File

@ -1768,7 +1768,8 @@ class CallAnalyzer
$context->inferType(
$input_expr,
$source_analyzer->getFunctionLikeStorage($statements_analyzer),
$param_type
$param_type,
$codebase
);
}
}

View File

@ -1325,7 +1325,12 @@ class ExpressionAnalyzer
}
if ($function_storage) {
$context->inferType($part, $function_storage, Type::getString());
$context->inferType(
$part,
$function_storage,
new Type\Union([new Type\Atomic\TString, new Type\Atomic\TInt, new Type\Atomic\TFloat]),
$statements_analyzer->getCodebase()
);
}
}

View File

@ -308,7 +308,7 @@ class FunctionDocblockManipulator
foreach ($parsed_docblock['specials']['param'] as &$param_block) {
$doc_parts = CommentAnalyzer::splitDocLine($param_block);
if ($doc_parts[1] === '$' . $param_name) {
if (($doc_parts[1] ?? null) === '$' . $param_name) {
$param_block = $new_param_block;
$found_in_params = true;
break;

View File

@ -1323,12 +1323,30 @@ class FileManipulationTest extends TestCase
function fooFoo($a): void {
echo $a . "foo";
}',
'<?php
/**
* @param string|int|float $a
*/
function fooFoo($a): void {
echo $a . "foo";
}',
'7.1',
['MissingParamType'],
true,
],
'noParamTypeButConcatAndStringUsage' => [
'<?php
function fooFoo($a): void {
echo $a . "foo";
echo substr($a, 4, 2);
}',
'<?php
/**
* @param string $a
*/
function fooFoo($a): void {
echo $a . "foo";
echo substr($a, 4, 2);
}',
'7.1',
['MissingParamType'],
@ -1371,12 +1389,30 @@ class FileManipulationTest extends TestCase
function fooFoo($a): void {
echo "$a";
}',
'<?php
/**
* @param string|int|float $a
*/
function fooFoo($a): void {
echo "$a";
}',
'7.1',
['MissingParamType'],
true,
],
'noParamTypeButTemplatedStringAntStringUsage' => [
'<?php
function fooFoo($a): void {
echo "$a";
echo substr($a, 4, 2);
}',
'<?php
/**
* @param string $a
*/
function fooFoo($a): void {
echo "$a";
echo substr($a, 4, 2);
}',
'7.1',
['MissingParamType'],