Skip to content
Permalink
Browse files

Coerce mixed values when passed as arguments

  • Loading branch information...
muglug committed May 21, 2019
1 parent a1eb191 commit 4ecf370900e22060cc5275a3f8eb4f00bedb8f2f
@@ -1586,6 +1586,15 @@ private static function checkFunctionLikeTypeMatches(
$static_fq_class_name
);
$fleshed_out_signature_type = $function_param->signature_type
? ExpressionAnalyzer::fleshOutType(
$codebase,
$function_param->signature_type,
$self_fq_class_name,
$static_fq_class_name
)
: null;
if ($arg->unpack) {
if ($arg_type->hasMixed()) {
if (!$context->collect_initializations
@@ -1647,6 +1656,7 @@ private static function checkFunctionLikeTypeMatches(
$statements_analyzer,
$arg_type,
$fleshed_out_type,
$fleshed_out_signature_type,
$cased_method_id,
$argument_offset,
new CodeLocation($statements_analyzer->getSource(), $arg->value),
@@ -2171,6 +2181,7 @@ public static function checkFunctionArgumentType(
StatementsAnalyzer $statements_analyzer,
Type\Union $input_type,
Type\Union $param_type,
?Type\Union $signature_param_type,
$cased_method_id,
$argument_offset,
CodeLocation $code_location,
@@ -2229,6 +2240,21 @@ public static function checkFunctionArgumentType(
// fall through
}
if (!$by_ref
&& !($variadic xor $unpack)
&& $cased_method_id !== 'echo'
) {
self::coerceValueAfterGatekeeperArgument(
$statements_analyzer,
$input_type,
$input_expr,
$param_type,
$signature_param_type,
$context,
$unpack
);
}
return null;
}
@@ -2587,47 +2613,71 @@ public static function checkFunctionArgumentType(
}
if ($type_match_found
&& !$param_type->hasMixed()
&& !$param_type->from_docblock
&& !($variadic xor $unpack)
&& !$by_ref
&& !($variadic xor $unpack)
&& $cased_method_id !== 'echo'
) {
$var_id = ExpressionAnalyzer::getVarId(
self::coerceValueAfterGatekeeperArgument(
$statements_analyzer,
$input_type,
$input_expr,
$statements_analyzer->getFQCLN(),
$statements_analyzer
$param_type,
$signature_param_type,
$context,
$unpack
);
}
if ($var_id) {
if ($input_type->isNullable() && !$param_type->isNullable()) {
$input_type->removeType('null');
}
return null;
}
if ($input_type->getId() === $param_type->getId()) {
$input_type->from_docblock = false;
private static function coerceValueAfterGatekeeperArgument(
StatementsAnalyzer $statements_analyzer,
Type\Union $input_type,
PhpParser\Node\Expr $input_expr,
Type\Union $param_type,
?Type\Union $signature_param_type,
Context $context,
bool $unpack
) : void {
if ($param_type->hasMixed() || ($param_type->from_docblock && !$input_type->isMixed())) {
return;
}
foreach ($input_type->getTypes() as $atomic_type) {
$atomic_type->from_docblock = false;
}
}
$var_id = ExpressionAnalyzer::getVarId(
$input_expr,
$statements_analyzer->getFQCLN(),
$statements_analyzer
);
$context->removeVarFromConflictingClauses($var_id, null, $statements_analyzer);
if ($var_id) {
if ($input_type->isNullable() && !$param_type->isNullable()) {
$input_type->removeType('null');
}
if ($unpack) {
$input_type = new Type\Union([
new TArray([
Type::getInt(),
$input_type
]),
]);
if ($input_type->getId() === $param_type->getId()) {
$input_type->from_docblock = false;
foreach ($input_type->getTypes() as $atomic_type) {
$atomic_type->from_docblock = false;
}
} elseif ($input_type->isMixed() && $signature_param_type) {
$input_type = clone $signature_param_type;
}
$context->removeVarFromConflictingClauses($var_id, null, $statements_analyzer);
$context->vars_in_scope[$var_id] = $input_type;
if ($unpack) {
$input_type = new Type\Union([
new TArray([
Type::getInt(),
$input_type
]),
]);
}
}
return null;
$context->vars_in_scope[$var_id] = $input_type;
}
}
/**
@@ -454,6 +454,7 @@ function ($line) {
$this,
$expr->inferredType,
Type::getString(),
null,
'echo',
(int)$i,
new CodeLocation($this->getSource(), $expr),
@@ -56,12 +56,12 @@ public static function getParamsFromCallMap($function_id)
$call_map_functions[] = $call_map[$call_map_key . '\'' . $i];
}
$function_type_options = [];
$function_param_options = [];
foreach ($call_map_functions as $call_map_function_args) {
array_shift($call_map_function_args);
$function_types = [];
$function_params = [];
/** @var string $arg_name - key type changed with above array_shift */
foreach ($call_map_function_args as $arg_name => $arg_type) {
@@ -88,7 +88,7 @@ public static function getParamsFromCallMap($function_id)
? Type::parseString($arg_type)
: Type::getMixed();
$function_types[] = new FunctionLikeParameter(
$function_param = new FunctionLikeParameter(
$arg_name,
$by_reference,
$param_type,
@@ -98,12 +98,16 @@ public static function getParamsFromCallMap($function_id)
false,
$variadic
);
$function_param->signature_type = null;
$function_params[] = $function_param;
}
$function_type_options[] = $function_types;
$function_param_options[] = $function_params;
}
return $function_type_options;
return $function_param_options;
}
/**
@@ -435,7 +435,7 @@ function (ParseTree $child_tree) use ($template_type_map) {
$tree_type = $tree_type instanceof Union ? $tree_type : new Union([$tree_type]);
return new FunctionLikeParameter(
$param = new FunctionLikeParameter(
'',
false,
$tree_type,
@@ -445,6 +445,11 @@ function (ParseTree $child_tree) use ($template_type_map) {
false,
$is_variadic
);
// type is not authoratative
$param->signature_type = null;
return $param;
},
$parse_tree->children
);
@@ -112,34 +112,42 @@ function fooFoo($a): void {
],
'noParamTypeButConcatAndStringUsage' => [
'<?php
function takesString(string $s) : void {}
function fooFoo($a): void {
echo $a . "foo";
echo substr($a, 4, 2);
echo takesString($a);
}',
'<?php
function takesString(string $s) : void {}
/**
* @param string $a
*/
function fooFoo($a): void {
echo $a . "foo";
echo substr($a, 4, 2);
echo takesString($a);
}',
'7.1',
['MissingParamType'],
true,
],
'noParamTypeButConcatAndStringUsageReversed' => [
'<?php
function takesString(string $s) : void {}
function fooFoo($a): void {
echo substr($a, 4, 2);
echo takesString($a);
echo $a . "foo";
}',
'<?php
function takesString(string $s) : void {}
/**
* @param string $a
*/
function fooFoo($a): void {
echo substr($a, 4, 2);
echo takesString($a);
echo $a . "foo";
}',
'7.1',
@@ -1646,6 +1646,21 @@ function ($dateTime) {
);
}',
],
'noImplicitAssignmentToStringFromMixedWithDocblockTypes' => [
'<?php
/** @param string $s */
function takesString($s) : void {}
function takesInt(int $i) : void {}
/**
* @param mixed $s
* @psalm-suppress MixedArgument
*/
function bar($s) : void {
takesString($s);
takesInt($s);
}',
],
];
}
@@ -2167,7 +2182,23 @@ function foo(int &$i) : void {}
$a = rand(0, 1) ? null : 5;
/** @psalm-suppress MixedArgument */
foo((int) $a);',
'InvalidPassByReference',
'error_message' => 'InvalidPassByReference',
],
'implicitAssignmentToStringFromMixed' => [
'<?php
/** @param "a"|"b" $s */
function takesString(string $s) : void {}
function takesInt(int $i) : void {}
/**
* @param mixed $s
* @psalm-suppress MixedArgument
*/
function bar($s) : void {
takesString($s);
takesInt($s);
}',
'error_message' => 'InvalidScalarArgument'
],
];
}

0 comments on commit 4ecf370

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