2020-10-04 02:21:44 +02:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Internal\Analyzer\Statements\Expression;
|
|
|
|
|
|
|
|
use PhpParser;
|
2021-06-08 04:55:21 +02:00
|
|
|
use Psalm\Context;
|
2020-10-04 02:21:44 +02:00
|
|
|
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
|
|
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
2021-02-15 22:18:41 +01:00
|
|
|
use Psalm\Node\Expr\BinaryOp\VirtualIdentical;
|
|
|
|
use Psalm\Node\Expr\VirtualConstFetch;
|
|
|
|
use Psalm\Node\Expr\VirtualMethodCall;
|
|
|
|
use Psalm\Node\Expr\VirtualPropertyFetch;
|
|
|
|
use Psalm\Node\Expr\VirtualTernary;
|
|
|
|
use Psalm\Node\Expr\VirtualVariable;
|
|
|
|
use Psalm\Node\VirtualName;
|
2020-10-04 02:21:44 +02:00
|
|
|
use Psalm\Type;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
class NullsafeAnalyzer
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @param PhpParser\Node\Expr\NullsafePropertyFetch|PhpParser\Node\Expr\NullsafeMethodCall $stmt
|
|
|
|
*/
|
|
|
|
public static function analyze(
|
|
|
|
StatementsAnalyzer $statements_analyzer,
|
|
|
|
PhpParser\Node\Expr $stmt,
|
|
|
|
Context $context
|
|
|
|
) : bool {
|
|
|
|
if (!$stmt->var instanceof PhpParser\Node\Expr\Variable) {
|
2021-06-25 15:54:39 +02:00
|
|
|
$was_inside_general_use = $context->inside_general_use;
|
2021-03-31 16:08:52 +02:00
|
|
|
|
2021-06-25 15:54:39 +02:00
|
|
|
$context->inside_general_use = true;
|
2020-10-04 02:21:44 +02:00
|
|
|
ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context);
|
2021-06-25 15:54:39 +02:00
|
|
|
$context->inside_general_use = $was_inside_general_use;
|
2020-10-04 02:21:44 +02:00
|
|
|
|
|
|
|
$tmp_name = '__tmp_nullsafe__' . (int) $stmt->var->getAttribute('startFilePos');
|
|
|
|
|
|
|
|
$condition_type = $statements_analyzer->node_data->getType($stmt->var);
|
|
|
|
|
|
|
|
if ($condition_type) {
|
|
|
|
$context->vars_in_scope['$' . $tmp_name] = $condition_type;
|
|
|
|
|
2021-02-15 22:18:41 +01:00
|
|
|
$tmp_var = new VirtualVariable($tmp_name, $stmt->var->getAttributes());
|
2020-10-04 02:21:44 +02:00
|
|
|
} else {
|
|
|
|
$tmp_var = $stmt->var;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$tmp_var = $stmt->var;
|
|
|
|
}
|
|
|
|
|
|
|
|
$old_node_data = $statements_analyzer->node_data;
|
|
|
|
$statements_analyzer->node_data = clone $statements_analyzer->node_data;
|
|
|
|
|
2021-02-15 22:18:41 +01:00
|
|
|
$null_value1 = new VirtualConstFetch(
|
|
|
|
new VirtualName('null'),
|
2020-10-04 02:21:44 +02:00
|
|
|
$stmt->var->getAttributes()
|
|
|
|
);
|
|
|
|
|
2021-02-15 22:18:41 +01:00
|
|
|
$null_comparison = new VirtualIdentical(
|
2020-10-04 02:21:44 +02:00
|
|
|
$tmp_var,
|
|
|
|
$null_value1,
|
|
|
|
$stmt->var->getAttributes()
|
|
|
|
);
|
|
|
|
|
2021-02-15 22:18:41 +01:00
|
|
|
$null_value2 = new VirtualConstFetch(
|
|
|
|
new VirtualName('null'),
|
2020-10-04 02:21:44 +02:00
|
|
|
$stmt->var->getAttributes()
|
|
|
|
);
|
|
|
|
|
|
|
|
if ($stmt instanceof PhpParser\Node\Expr\NullsafePropertyFetch) {
|
2021-02-15 22:18:41 +01:00
|
|
|
$ternary = new VirtualTernary(
|
2020-10-04 02:21:44 +02:00
|
|
|
$null_comparison,
|
|
|
|
$null_value2,
|
2021-02-15 22:18:41 +01:00
|
|
|
new VirtualPropertyFetch($tmp_var, $stmt->name, $stmt->getAttributes()),
|
2020-10-04 02:21:44 +02:00
|
|
|
$stmt->getAttributes()
|
|
|
|
);
|
|
|
|
} else {
|
2021-02-15 22:18:41 +01:00
|
|
|
$ternary = new VirtualTernary(
|
2020-10-04 02:21:44 +02:00
|
|
|
$null_comparison,
|
|
|
|
$null_value2,
|
2021-02-15 22:18:41 +01:00
|
|
|
new VirtualMethodCall($tmp_var, $stmt->name, $stmt->args, $stmt->getAttributes()),
|
2020-10-04 02:21:44 +02:00
|
|
|
$stmt->getAttributes()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
ExpressionAnalyzer::analyze($statements_analyzer, $ternary, $context);
|
|
|
|
|
|
|
|
$ternary_type = $statements_analyzer->node_data->getType($ternary);
|
|
|
|
|
|
|
|
$statements_analyzer->node_data = $old_node_data;
|
|
|
|
|
|
|
|
$statements_analyzer->node_data->setType($stmt, $ternary_type ?: Type::getMixed());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|