mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Only the binary op 'plus' works with two arrays
Treat the result of any other operation as int|float Fixes #2123
This commit is contained in:
parent
13ea0c241d
commit
03e8d19eec
@ -542,97 +542,97 @@ class ArithmeticOpAnalyzer
|
||||
$has_valid_right_operand = true;
|
||||
}
|
||||
|
||||
$result_type = Type::getArray();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$has_valid_right_operand = true;
|
||||
$has_valid_left_operand = true;
|
||||
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Plus) {
|
||||
$has_valid_right_operand = true;
|
||||
$has_valid_left_operand = true;
|
||||
|
||||
if ($left_type_part instanceof TKeyedArray
|
||||
&& $right_type_part instanceof TKeyedArray
|
||||
) {
|
||||
$definitely_existing_mixed_right_properties = array_diff_key(
|
||||
$right_type_part->properties,
|
||||
$left_type_part->properties,
|
||||
);
|
||||
|
||||
$properties = $left_type_part->properties;
|
||||
|
||||
foreach ($right_type_part->properties as $key => $type) {
|
||||
if (!isset($properties[$key])) {
|
||||
$properties[$key] = $type;
|
||||
} elseif ($properties[$key]->possibly_undefined) {
|
||||
$properties[$key] = Type::combineUnionTypes(
|
||||
$properties[$key],
|
||||
$type,
|
||||
$codebase,
|
||||
false,
|
||||
true,
|
||||
500,
|
||||
$type->possibly_undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($left_type_part->fallback_params !== null) {
|
||||
foreach ($definitely_existing_mixed_right_properties as $key => $type) {
|
||||
$properties[$key] = Type::combineUnionTypes(Type::getMixed(), $type);
|
||||
}
|
||||
}
|
||||
|
||||
if ($left_type_part->fallback_params === null
|
||||
&& $right_type_part->fallback_params === null
|
||||
if ($left_type_part instanceof TKeyedArray
|
||||
&& $right_type_part instanceof TKeyedArray
|
||||
) {
|
||||
$fallback_params = null;
|
||||
} elseif ($left_type_part->fallback_params !== null
|
||||
&& $right_type_part->fallback_params !== null
|
||||
) {
|
||||
$fallback_params = [
|
||||
Type::combineUnionTypes(
|
||||
$left_type_part->fallback_params[0],
|
||||
$right_type_part->fallback_params[0],
|
||||
),
|
||||
Type::combineUnionTypes(
|
||||
$left_type_part->fallback_params[1],
|
||||
$right_type_part->fallback_params[1],
|
||||
),
|
||||
];
|
||||
$definitely_existing_mixed_right_properties = array_diff_key(
|
||||
$right_type_part->properties,
|
||||
$left_type_part->properties,
|
||||
);
|
||||
|
||||
$properties = $left_type_part->properties;
|
||||
|
||||
foreach ($right_type_part->properties as $key => $type) {
|
||||
if (!isset($properties[$key])) {
|
||||
$properties[$key] = $type;
|
||||
} elseif ($properties[$key]->possibly_undefined) {
|
||||
$properties[$key] = Type::combineUnionTypes(
|
||||
$properties[$key],
|
||||
$type,
|
||||
$codebase,
|
||||
false,
|
||||
true,
|
||||
500,
|
||||
$type->possibly_undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($left_type_part->fallback_params !== null) {
|
||||
foreach ($definitely_existing_mixed_right_properties as $key => $type) {
|
||||
$properties[$key] = Type::combineUnionTypes(Type::getMixed(), $type);
|
||||
}
|
||||
}
|
||||
|
||||
if ($left_type_part->fallback_params === null
|
||||
&& $right_type_part->fallback_params === null
|
||||
) {
|
||||
$fallback_params = null;
|
||||
} elseif ($left_type_part->fallback_params !== null
|
||||
&& $right_type_part->fallback_params !== null
|
||||
) {
|
||||
$fallback_params = [
|
||||
Type::combineUnionTypes(
|
||||
$left_type_part->fallback_params[0],
|
||||
$right_type_part->fallback_params[0],
|
||||
),
|
||||
Type::combineUnionTypes(
|
||||
$left_type_part->fallback_params[1],
|
||||
$right_type_part->fallback_params[1],
|
||||
),
|
||||
];
|
||||
} else {
|
||||
$fallback_params = $left_type_part->fallback_params ?: $right_type_part->fallback_params;
|
||||
}
|
||||
|
||||
$new_keyed_array = new TKeyedArray(
|
||||
$properties,
|
||||
null,
|
||||
$fallback_params,
|
||||
);
|
||||
$result_type_member = new Union([$new_keyed_array]);
|
||||
} else {
|
||||
$fallback_params = $left_type_part->fallback_params ?: $right_type_part->fallback_params;
|
||||
$result_type_member = TypeCombiner::combine(
|
||||
[$left_type_part, $right_type_part],
|
||||
$codebase,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
$new_keyed_array = new TKeyedArray(
|
||||
$properties,
|
||||
null,
|
||||
$fallback_params,
|
||||
);
|
||||
$result_type_member = new Union([$new_keyed_array]);
|
||||
} else {
|
||||
$result_type_member = TypeCombiner::combine(
|
||||
[$left_type_part, $right_type_part],
|
||||
$codebase,
|
||||
true,
|
||||
);
|
||||
$result_type = Type::combineUnionTypes($result_type_member, $result_type, $codebase, true);
|
||||
|
||||
if ($left instanceof PhpParser\Node\Expr\ArrayDimFetch
|
||||
&& $context
|
||||
&& $statements_source instanceof StatementsAnalyzer
|
||||
) {
|
||||
ArrayAssignmentAnalyzer::updateArrayType(
|
||||
$statements_source,
|
||||
$left,
|
||||
$right,
|
||||
$result_type,
|
||||
$context,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$result_type = Type::combineUnionTypes($result_type_member, $result_type, $codebase, true);
|
||||
|
||||
if ($left instanceof PhpParser\Node\Expr\ArrayDimFetch
|
||||
&& $context
|
||||
&& $statements_source instanceof StatementsAnalyzer
|
||||
) {
|
||||
ArrayAssignmentAnalyzer::updateArrayType(
|
||||
$statements_source,
|
||||
$left,
|
||||
$right,
|
||||
$result_type,
|
||||
$context,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (($left_type_part instanceof TNamedObject && strtolower($left_type_part->value) === 'gmp')
|
||||
|
@ -1022,6 +1022,58 @@ class BinaryOperationTest extends TestCase
|
||||
'$a===' => 'float(9.2233720368548E+18)',
|
||||
],
|
||||
],
|
||||
'invalidArrayOperations' => [
|
||||
'code' => <<<'PHP'
|
||||
<?php
|
||||
|
||||
$a1 = 1 + [];
|
||||
$a2 = [] + 1;
|
||||
// This is the one exception to this rule
|
||||
$a3 = [] + [];
|
||||
|
||||
$b1 = 1 - [];
|
||||
$b2 = [] - 1;
|
||||
$b3 = [] - [];
|
||||
|
||||
$c1 = 1 * [];
|
||||
$c2 = [] * 1;
|
||||
$c3 = [] * [];
|
||||
|
||||
$d1 = 1 / [];
|
||||
$d2 = [] / 1;
|
||||
$d3 = [] / [];
|
||||
|
||||
$e1 = 1 ** [];
|
||||
$e2 = [] ** 1;
|
||||
$e3 = [] ** [];
|
||||
|
||||
$f1 = 1 % [];
|
||||
$f2 = [] % 1;
|
||||
$f3 = [] % [];
|
||||
|
||||
PHP,
|
||||
'assertions' => [
|
||||
'$a1' => 'float|int',
|
||||
'$a2' => 'float|int',
|
||||
'$a3' => 'array<never, never>',
|
||||
'$b1' => 'float|int',
|
||||
'$b2' => 'float|int',
|
||||
'$b3' => 'float|int',
|
||||
'$c1' => 'float|int',
|
||||
'$c2' => 'float|int',
|
||||
'$c3' => 'float|int',
|
||||
'$d1' => 'float|int',
|
||||
'$d2' => 'float|int',
|
||||
'$d3' => 'float|int',
|
||||
'$e1' => 'float|int',
|
||||
'$e2' => 'float|int',
|
||||
'$e3' => 'float|int',
|
||||
'$f1' => 'float|int',
|
||||
'$f2' => 'float|int',
|
||||
'$f3' => 'float|int',
|
||||
],
|
||||
'ignored_issues' => ['InvalidOperand'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user