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

Massage arg type after coerced param

Ref #1927
This commit is contained in:
Brown 2019-07-10 12:12:51 -04:00
parent 1ae9ea5fed
commit 2c6854f403
4 changed files with 95 additions and 25 deletions

View File

@ -2298,6 +2298,7 @@ class CallAnalyzer
self::coerceValueAfterGatekeeperArgument(
$statements_analyzer,
$input_type,
false,
$input_expr,
$param_type,
$signature_param_type,
@ -2349,6 +2350,13 @@ class CallAnalyzer
$union_comparison_results
);
$replace_input_type = false;
if ($union_comparison_results->replacement_union_type) {
$replace_input_type = true;
$input_type = $union_comparison_results->replacement_union_type;
}
if ($type_match_found
&& $param_type->hasCallableType()
) {
@ -2671,6 +2679,7 @@ class CallAnalyzer
self::coerceValueAfterGatekeeperArgument(
$statements_analyzer,
$input_type,
$replace_input_type,
$input_expr,
$param_type,
$signature_param_type,
@ -2685,6 +2694,7 @@ class CallAnalyzer
private static function coerceValueAfterGatekeeperArgument(
StatementsAnalyzer $statements_analyzer,
Type\Union $input_type,
bool $input_type_changed,
PhpParser\Node\Expr $input_expr,
Type\Union $param_type,
?Type\Union $signature_param_type,
@ -2695,9 +2705,7 @@ class CallAnalyzer
return;
}
if ($param_type->from_docblock && !$input_type->hasMixed()) {
$input_type_changed = false;
if (!$input_type_changed && $param_type->from_docblock && !$input_type->hasMixed()) {
$input_type = clone $input_type;
foreach ($param_type->getTypes() as $param_atomic_type) {
@ -2707,7 +2715,9 @@ class CallAnalyzer
&& $input_atomic_type->value === $param_atomic_type->value
) {
foreach ($input_atomic_type->type_params as $i => $type_param) {
if ($type_param->isEmpty() && isset($param_atomic_type->type_params[$i])) {
if (($type_param->isEmpty() || $type_param->had_template)
&& isset($param_atomic_type->type_params[$i])
) {
$input_type_changed = true;
$input_atomic_type->type_params[$i] = clone $param_atomic_type->type_params[$i];

View File

@ -152,6 +152,21 @@ class TypeAnalyzer
$union_comparison_result->type_coerced_from_scalar
= $atomic_comparison_result->type_coerced_from_scalar;
}
if ($is_atomic_contained_by
&& $union_comparison_result
&& $atomic_comparison_result->replacement_atomic_type
) {
if (!$union_comparison_result->replacement_union_type) {
$union_comparison_result->replacement_union_type = clone $input_type;
}
$union_comparison_result->replacement_union_type->removeType($input_type->getKey());
$union_comparison_result->replacement_union_type->addType(
$atomic_comparison_result->replacement_atomic_type
);
}
}
if ($input_type_part instanceof TNumeric
@ -1759,34 +1774,43 @@ class TypeAnalyzer
)) {
$all_types_contain = false;
} elseif (!$input_type_part instanceof TIterable
&& !$container_param->had_template
&& !$input_param->had_template
&& !$container_param->hasTemplate()
&& !$input_param->hasTemplate()
&& !$input_param->hasLiteralValue()
&& !$input_param->hasEmptyArray()
) {
$input_storage = $codebase->classlike_storage_provider->get($input_type_part->value);
if ($input_param->had_template) {
if (!$atomic_comparison_result->replacement_atomic_type) {
$atomic_comparison_result->replacement_atomic_type = clone $input_type_part;
}
if (!($input_storage->template_covariants[$i] ?? false)) {
// Make sure types are basically the same
if (!self::isContainedBy(
$codebase,
$container_param,
$input_param,
$container_param->ignore_nullable_issues,
$container_param->ignore_falsable_issues,
$atomic_comparison_result,
$allow_interface_equality
) || $atomic_comparison_result->type_coerced
) {
if ($container_param->hasMixed() || $container_param->isArrayKey()) {
$atomic_comparison_result->type_coerced_from_mixed = true;
} else {
$all_types_contain = false;
if ($atomic_comparison_result->replacement_atomic_type instanceof TGenericObject) {
$atomic_comparison_result->replacement_atomic_type->type_params[$i]
= clone $container_param;
}
} else {
$input_storage = $codebase->classlike_storage_provider->get($input_type_part->value);
if (!($input_storage->template_covariants[$i] ?? false)) {
// Make sure types are basically the same
if (!self::isContainedBy(
$codebase,
$container_param,
$input_param,
$container_param->ignore_nullable_issues,
$container_param->ignore_falsable_issues,
$atomic_comparison_result,
$allow_interface_equality
) || $atomic_comparison_result->type_coerced
) {
if ($container_param->hasMixed() || $container_param->isArrayKey()) {
$atomic_comparison_result->type_coerced_from_mixed = true;
} else {
$all_types_contain = false;
}
$atomic_comparison_result->type_coerced = false;
}
$atomic_comparison_result->type_coerced = false;
}
}
}

View File

@ -18,4 +18,10 @@ class TypeComparisonResult
/** @var ?bool */
public $type_coerced_from_scalar = null;
/** @var ?\Psalm\Type\Union */
public $replacement_union_type = null;
/** @var ?\Psalm\Type\Atomic */
public $replacement_atomic_type = null;
}

View File

@ -1999,6 +1999,36 @@ class ClassTemplateTest extends TestCase
$mario->ame = "Luigi";',
'error_message' => 'InvalidArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:47:29 - Argument 1 of CharacterRow::__set expects string(id)|string(name)|string(height), string(ame) provided',
],
'specialiseTypeBeforeReturning' => [
'<?php
class Base {}
class Derived extends Base {}
/**
* @template T of Base
*/
class Foo {
/**
* @param T $t
*/
public function __construct ($t) {}
}
/**
* @return Foo<Base>
*/
function returnFooBase() {
$f = new Foo(new Derived());
takesFooDerived($f);
return $f;
}
/**
* @param Foo<Derived> $foo
*/
function takesFooDerived($foo): void {}',
'error_message' => 'InvalidReturnStatement'
],
];
}
}