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\CodeLocation;
|
|
|
|
use Psalm\Context;
|
|
|
|
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) {
|
|
|
|
$statements_analyzer->node_data->setType($stmt, $stmt_var_type ?: Type::getMixed());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
|
|
|
|
&& $stmt_var_type->hasString()
|
|
|
|
) {
|
2020-05-18 21:13:27 +02:00
|
|
|
$return_type = null;
|
|
|
|
|
|
|
|
$fake_right_expr = new PhpParser\Node\Scalar\LNumber(1, $stmt->getAttributes());
|
|
|
|
$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
|
|
|
|
);
|
|
|
|
|
|
|
|
$stmt_type = clone $stmt_var_type;
|
|
|
|
|
|
|
|
$statements_analyzer->node_data->setType($stmt, $stmt_type);
|
|
|
|
|
|
|
|
$var_id = ExpressionIdentifier::getArrayVarId($stmt->var, null);
|
|
|
|
|
|
|
|
$codebase = $statements_analyzer->getCodebase();
|
|
|
|
|
|
|
|
if ($var_id && isset($context->vars_in_scope[$var_id])) {
|
|
|
|
$context->vars_in_scope[$var_id] = $stmt_type;
|
|
|
|
|
|
|
|
if ($codebase->find_unused_variables && $stmt->var instanceof PhpParser\Node\Expr\Variable) {
|
|
|
|
$location = new CodeLocation($statements_analyzer, $stmt->var);
|
|
|
|
$context->assigned_var_ids[$var_id] = true;
|
|
|
|
$context->possibly_assigned_var_ids[$var_id] = true;
|
2020-07-10 22:14:24 +02:00
|
|
|
|
2020-07-10 23:13:11 +02:00
|
|
|
if (!$context->inside_isset) {
|
2020-07-10 22:14:24 +02:00
|
|
|
$statements_analyzer->registerVariableAssignment(
|
|
|
|
$var_id,
|
|
|
|
$location
|
|
|
|
);
|
|
|
|
|
|
|
|
$context->unreferenced_vars[$var_id] = [$location->getHash() => $location];
|
|
|
|
}
|
2020-05-18 21:13:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// removes dependent vars from $context
|
|
|
|
$context->removeDescendents(
|
|
|
|
$var_id,
|
|
|
|
$context->vars_in_scope[$var_id],
|
|
|
|
$return_type,
|
|
|
|
$statements_analyzer
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
2020-07-31 17:26:54 +02:00
|
|
|
$fake_right_expr = new PhpParser\Node\Scalar\LNumber(1, $stmt->getAttributes());
|
|
|
|
|
|
|
|
$operation = $stmt instanceof PostInc || $stmt instanceof PreInc
|
|
|
|
? new PhpParser\Node\Expr\BinaryOp\Plus(
|
|
|
|
$stmt->var,
|
|
|
|
$fake_right_expr
|
|
|
|
)
|
|
|
|
: new PhpParser\Node\Expr\BinaryOp\Minus(
|
|
|
|
$stmt->var,
|
|
|
|
$fake_right_expr
|
|
|
|
);
|
|
|
|
|
|
|
|
$fake_assignment = new PhpParser\Node\Expr\Assign(
|
|
|
|
$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;
|
|
|
|
}
|
|
|
|
}
|