2020-05-18 21:13:27 +02:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Internal\Analyzer\Statements\Expression;
|
|
|
|
|
|
|
|
use PhpParser;
|
2020-09-12 17:24:40 +02:00
|
|
|
use PhpParser\Node\Expr\PostInc;
|
|
|
|
use PhpParser\Node\Expr\PostDec;
|
|
|
|
use PhpParser\Node\Expr\PreInc;
|
|
|
|
use PhpParser\Node\Expr\PreDec;
|
2020-05-18 21:13:27 +02:00
|
|
|
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
|
|
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
|
|
|
use Psalm\Context;
|
2021-02-15 22:18:41 +01:00
|
|
|
use Psalm\Node\Expr\BinaryOp\VirtualMinus;
|
|
|
|
use Psalm\Node\Expr\BinaryOp\VirtualPlus;
|
|
|
|
use Psalm\Node\Expr\VirtualAssign;
|
|
|
|
use Psalm\Node\Scalar\VirtualLNumber;
|
2020-05-18 21:13:27 +02:00
|
|
|
use Psalm\Type;
|
|
|
|
|
|
|
|
class IncDecExpressionAnalyzer
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @param PostInc|PostDec|PreInc|PreDec $stmt
|
|
|
|
*/
|
|
|
|
public static function analyze(
|
|
|
|
StatementsAnalyzer $statements_analyzer,
|
|
|
|
PhpParser\Node\Expr $stmt,
|
|
|
|
Context $context
|
|
|
|
) : bool {
|
|
|
|
$was_inside_assignment = $context->inside_assignment;
|
|
|
|
$context->inside_assignment = true;
|
|
|
|
|
|
|
|
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context) === false) {
|
|
|
|
if (!$was_inside_assignment) {
|
|
|
|
$context->inside_assignment = false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$was_inside_assignment) {
|
|
|
|
$context->inside_assignment = false;
|
|
|
|
}
|
|
|
|
|
2020-07-31 17:26:54 +02:00
|
|
|
$stmt_var_type = $statements_analyzer->node_data->getType($stmt->var);
|
|
|
|
|
|
|
|
if ($stmt instanceof PostInc || $stmt instanceof PostDec) {
|
2021-05-17 14:44:53 +02:00
|
|
|
$statements_analyzer->node_data->setType($stmt, $stmt_var_type ?? Type::getMixed());
|
2020-07-31 17:26:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
|
|
|
|
&& $stmt_var_type->hasString()
|
2020-09-30 18:28:13 +02:00
|
|
|
&& ($stmt instanceof PostInc || $stmt instanceof PreInc)
|
2020-07-31 17:26:54 +02:00
|
|
|
) {
|
2020-05-18 21:13:27 +02:00
|
|
|
$return_type = null;
|
|
|
|
|
2021-02-15 22:18:41 +01:00
|
|
|
$fake_right_expr = new VirtualLNumber(1, $stmt->getAttributes());
|
2020-05-18 21:13:27 +02:00
|
|
|
$statements_analyzer->node_data->setType($fake_right_expr, Type::getInt());
|
|
|
|
|
2020-05-19 00:57:02 +02:00
|
|
|
BinaryOp\NonDivArithmeticOpAnalyzer::analyze(
|
2020-05-18 21:13:27 +02:00
|
|
|
$statements_analyzer,
|
|
|
|
$statements_analyzer->node_data,
|
|
|
|
$stmt->var,
|
|
|
|
$fake_right_expr,
|
|
|
|
$stmt,
|
|
|
|
$return_type,
|
|
|
|
$context
|
|
|
|
);
|
|
|
|
|
2021-05-17 14:44:53 +02:00
|
|
|
$result_type = $return_type ?? Type::getMixed();
|
|
|
|
$statements_analyzer->node_data->setType($stmt, $result_type);
|
2020-05-18 21:13:27 +02:00
|
|
|
|
2020-10-13 22:49:03 +02:00
|
|
|
BinaryOpAnalyzer::addDataFlow(
|
2020-09-30 18:28:13 +02:00
|
|
|
$statements_analyzer,
|
|
|
|
$stmt,
|
|
|
|
$stmt->var,
|
|
|
|
$fake_right_expr,
|
|
|
|
'inc'
|
|
|
|
);
|
|
|
|
|
2020-05-18 21:13:27 +02:00
|
|
|
$var_id = ExpressionIdentifier::getArrayVarId($stmt->var, null);
|
|
|
|
|
|
|
|
$codebase = $statements_analyzer->getCodebase();
|
|
|
|
|
|
|
|
if ($var_id && isset($context->vars_in_scope[$var_id])) {
|
2021-05-17 14:44:53 +02:00
|
|
|
$context->vars_in_scope[$var_id] = $result_type;
|
2020-05-18 21:13:27 +02:00
|
|
|
|
|
|
|
if ($codebase->find_unused_variables && $stmt->var instanceof PhpParser\Node\Expr\Variable) {
|
2020-11-01 17:26:42 +01:00
|
|
|
$context->assigned_var_ids[$var_id] = (int) $stmt->var->getAttribute('startFilePos');
|
2020-05-18 21:13:27 +02:00
|
|
|
$context->possibly_assigned_var_ids[$var_id] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// removes dependent vars from $context
|
|
|
|
$context->removeDescendents(
|
|
|
|
$var_id,
|
|
|
|
$context->vars_in_scope[$var_id],
|
|
|
|
$return_type,
|
|
|
|
$statements_analyzer
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
2021-02-15 22:18:41 +01:00
|
|
|
$fake_right_expr = new VirtualLNumber(1, $stmt->getAttributes());
|
2020-07-31 17:26:54 +02:00
|
|
|
|
|
|
|
$operation = $stmt instanceof PostInc || $stmt instanceof PreInc
|
2021-02-15 22:18:41 +01:00
|
|
|
? new VirtualPlus(
|
2020-07-31 17:26:54 +02:00
|
|
|
$stmt->var,
|
2020-09-28 06:45:02 +02:00
|
|
|
$fake_right_expr,
|
|
|
|
$stmt->var->getAttributes()
|
2020-07-31 17:26:54 +02:00
|
|
|
)
|
2021-02-15 22:18:41 +01:00
|
|
|
: new VirtualMinus(
|
2020-07-31 17:26:54 +02:00
|
|
|
$stmt->var,
|
2020-09-28 06:45:02 +02:00
|
|
|
$fake_right_expr,
|
|
|
|
$stmt->var->getAttributes()
|
2020-07-31 17:26:54 +02:00
|
|
|
);
|
|
|
|
|
2021-02-15 22:18:41 +01:00
|
|
|
$fake_assignment = new VirtualAssign(
|
2020-07-31 17:26:54 +02:00
|
|
|
$stmt->var,
|
|
|
|
$operation,
|
|
|
|
$stmt->getAttributes()
|
|
|
|
);
|
|
|
|
|
|
|
|
$old_node_data = $statements_analyzer->node_data;
|
|
|
|
|
|
|
|
$statements_analyzer->node_data = clone $statements_analyzer->node_data;
|
|
|
|
|
|
|
|
if (ExpressionAnalyzer::analyze($statements_analyzer, $fake_assignment, $context) === false) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($stmt instanceof PreInc || $stmt instanceof PreDec) {
|
|
|
|
$old_node_data->setType(
|
|
|
|
$stmt,
|
|
|
|
$statements_analyzer->node_data->getType($operation) ?: Type::getMixed()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$statements_analyzer->node_data = $old_node_data;
|
2020-05-18 21:13:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|