expr, $context) === false) { return false; } if (!($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr))) { $statements_analyzer->node_data->setType($stmt, new Type\Union([new TInt(), new TString()])); } elseif ($stmt_expr_type->isMixed()) { $statements_analyzer->node_data->setType($stmt, Type::getMixed()); } else { $acceptable_types = []; $unacceptable_type = null; $has_valid_operand = false; foreach ($stmt_expr_type->getAtomicTypes() as $type_string => $type_part) { if ($type_part instanceof TInt || $type_part instanceof TString) { if ($type_part instanceof Type\Atomic\TLiteralInt) { $type_part->value = ~$type_part->value; } elseif ($type_part instanceof Type\Atomic\TLiteralString) { $type_part->value = ~$type_part->value; } $acceptable_types[] = $type_part; $has_valid_operand = true; } elseif ($type_part instanceof TFloat) { $type_part = ($type_part instanceof Type\Atomic\TLiteralFloat) ? new Type\Atomic\TLiteralInt(~$type_part->value) : new TInt; $stmt_expr_type->removeType($type_string); $stmt_expr_type->addType($type_part); $acceptable_types[] = $type_part; $has_valid_operand = true; } elseif (!$unacceptable_type) { $unacceptable_type = $type_part; } } if ($unacceptable_type || !$acceptable_types) { $message = 'Cannot negate a non-numeric non-string type ' . $unacceptable_type; if ($has_valid_operand) { if (IssueBuffer::accepts( new PossiblyInvalidOperand( $message, new CodeLocation($statements_analyzer, $stmt) ), $statements_analyzer->getSuppressedIssues() )) { // fall through } } else { if (IssueBuffer::accepts( new InvalidOperand( $message, new CodeLocation($statements_analyzer, $stmt) ), $statements_analyzer->getSuppressedIssues() )) { // fall through } } $statements_analyzer->node_data->setType($stmt, Type::getMixed()); } else { $statements_analyzer->node_data->setType($stmt, new Type\Union($acceptable_types)); } } self::addDataFlow($statements_analyzer, $stmt, $stmt->expr); return true; } private static function addDataFlow( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr $stmt, PhpParser\Node\Expr $value ): void { $result_type = $statements_analyzer->node_data->getType($stmt); if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph && $result_type) { $var_location = new CodeLocation($statements_analyzer, $stmt); $stmt_value_type = $statements_analyzer->node_data->getType($value); $new_parent_node = DataFlowNode::getForAssignment('bitwisenot', $var_location); $statements_analyzer->data_flow_graph->addNode($new_parent_node); $result_type->parent_nodes = [ $new_parent_node->id => $new_parent_node, ]; if ($stmt_value_type && $stmt_value_type->parent_nodes) { foreach ($stmt_value_type->parent_nodes as $parent_node) { $statements_analyzer->data_flow_graph->addPath($parent_node, $new_parent_node, 'bitwisenot'); } } } } }