Skip to content

Commit

Permalink
Support for bitwise not
Browse files Browse the repository at this point in the history
  • Loading branch information
bugreportuser authored and muglug committed Apr 14, 2019
1 parent 0686d34 commit 8454c0d
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
66 changes: 66 additions & 0 deletions src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php
Expand Up @@ -35,7 +35,9 @@
use Psalm\Issue\InvalidCast; use Psalm\Issue\InvalidCast;
use Psalm\Issue\InvalidClone; use Psalm\Issue\InvalidClone;
use Psalm\Issue\InvalidDocblock; use Psalm\Issue\InvalidDocblock;
use Psalm\Issue\InvalidOperand;
use Psalm\Issue\PossiblyInvalidCast; use Psalm\Issue\PossiblyInvalidCast;
use Psalm\Issue\PossiblyInvalidOperand;
use Psalm\Issue\PossiblyUndefinedVariable; use Psalm\Issue\PossiblyUndefinedVariable;
use Psalm\Issue\UndefinedConstant; use Psalm\Issue\UndefinedConstant;
use Psalm\Issue\UndefinedVariable; use Psalm\Issue\UndefinedVariable;
Expand Down Expand Up @@ -232,6 +234,70 @@ public static function analyze(
if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) { if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
return false; return false;
} }

if (!isset($stmt->expr->inferredType)) {
$stmt->inferredType = new Type\Union([new TInt(), new TString()]);
} elseif ($stmt->expr->inferredType->isMixed()) {
$stmt->inferredType = Type::getMixed();
} else {
$acceptable_types = [];
$unacceptable_type = null;
$has_valid_operand = false;

foreach ($stmt->expr->inferredType->getTypes() 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->inferredType->removeType($type_string);
$stmt->expr->inferredType->addType($type_part);

$acceptable_types[] = $type_part;
$has_valid_operand = true;
} elseif (!$unacceptable_type) {
$unacceptable_type = $type_part;
}
}

if ($unacceptable_type) {
$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
}
}

$stmt->inferredType = Type::getMixed();
} else {
$stmt->inferredType = new Type\Union($acceptable_types);
}
}
} elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp) { } elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
if (BinaryOpAnalyzer::analyze( if (BinaryOpAnalyzer::analyze(
$statements_analyzer, $statements_analyzer,
Expand Down
28 changes: 28 additions & 0 deletions tests/BinaryOperationTest.php
Expand Up @@ -197,6 +197,19 @@ function foo(string $s) : int {
'$b' => 'int', '$b' => 'int',
], ],
], ],
'bitwiseNot' => [
'<?php
$a = ~4;
$b = ~4.0;
$c = ~4.4;
$d = ~"a";',
'assertions' => [
'$a' => 'int',
'$b' => 'int',
'$c' => 'int',
'$d' => 'string',
],
],
]; ];
} }


Expand Down Expand Up @@ -288,6 +301,21 @@ public function providerInvalidCodeParse()
$a = "x" | new stdClass;', $a = "x" | new stdClass;',
'error_message' => 'InvalidOperand', 'error_message' => 'InvalidOperand',
], ],
'invalidBitwiseNot' => [
'<?php
$a = ~new stdClass;',
'error_message' => 'InvalidOperand',
],
'possiblyInvalidBitwiseNot' => [
'<?php
$a = ~(rand(0, 1) ? 2 : null);',
'error_message' => 'PossiblyInvalidOperand',
],
'invalidBooleanBitwiseNot' => [
'<?php
$a = ~true;',
'error_message' => 'InvalidOperand',
],
]; ];
} }
} }

0 comments on commit 8454c0d

Please sign in to comment.