Skip to content
Permalink
Browse files

Massage arg type after coerced param

Ref #1927
  • Loading branch information...
muglug committed Jul 10, 2019
1 parent 1ae9ea5 commit 2c6854f4035d9c7e17d2c93cf71b1fa86710c42d
@@ -2298,6 +2298,7 @@ public static function checkFunctionArgumentType(
self::coerceValueAfterGatekeeperArgument(
$statements_analyzer,
$input_type,
false,
$input_expr,
$param_type,
$signature_param_type,
@@ -2349,6 +2350,13 @@ public static function checkFunctionArgumentType(
$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 @@ public static function checkFunctionArgumentType(
self::coerceValueAfterGatekeeperArgument(
$statements_analyzer,
$input_type,
$replace_input_type,
$input_expr,
$param_type,
$signature_param_type,
@@ -2685,6 +2694,7 @@ public static function checkFunctionArgumentType(
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 @@ private static function coerceValueAfterGatekeeperArgument(
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 @@ private static function coerceValueAfterGatekeeperArgument(
&& $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];
@@ -152,6 +152,21 @@ public static function isContainedBy(
$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 @@ private static function isMatchingTypeContainedBy(
)) {
$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;
}
}
}
}
@@ -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;
}
@@ -1999,6 +1999,36 @@ class CharacterRow extends Row {}
$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'
],
];
}
}

0 comments on commit 2c6854f

Please sign in to comment.
You can’t perform that action at this time.