1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Fix #3877 - prevent impossible subtr comparisons

This commit is contained in:
Matthew Brown 2020-07-24 10:08:57 -04:00
parent b88b169464
commit 84945a7d1b
5 changed files with 93 additions and 36 deletions

View File

@ -157,6 +157,88 @@ class BinaryOpAnalyzer
$stmt_left_type = $statements_analyzer->node_data->getType($stmt->left);
$stmt_right_type = $statements_analyzer->node_data->getType($stmt->right);
if (($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical)
&& $stmt->left instanceof PhpParser\Node\Expr\FuncCall
&& $stmt->left->name instanceof PhpParser\Node\Name
&& $stmt->left->name->parts === ['substr']
&& isset($stmt->left->args[1])
&& $stmt_right_type
&& $stmt_right_type->hasLiteralString()
) {
$from_type = $statements_analyzer->node_data->getType($stmt->left->args[1]->value);
$length_type = isset($stmt->left->args[2])
? ($statements_analyzer->node_data->getType($stmt->left->args[2]->value) ?: Type::getMixed())
: null;
$string_length = null;
if ($from_type && $from_type->isSingleIntLiteral() && $length_type === null) {
$string_length = -$from_type->getSingleIntLiteral()->value;
} elseif ($length_type && $length_type->isSingleIntLiteral()) {
$string_length = $length_type->getSingleIntLiteral()->value;
}
if ($string_length > 0) {
foreach ($stmt_right_type->getAtomicTypes() as $atomic_right_type) {
if ($atomic_right_type instanceof Type\Atomic\TLiteralString) {
if (strlen($atomic_right_type->value) !== $string_length) {
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
) {
if ($atomic_right_type->from_docblock) {
if (IssueBuffer::accepts(
new \Psalm\Issue\DocblockTypeContradiction(
$atomic_right_type . ' string length is not ' . $string_length,
new CodeLocation($statements_analyzer, $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new \Psalm\Issue\TypeDoesNotContainType(
$atomic_right_type . ' string length is not ' . $string_length,
new CodeLocation($statements_analyzer, $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
}
} else {
if ($atomic_right_type->from_docblock) {
if (IssueBuffer::accepts(
new \Psalm\Issue\RedundantConditionGivenDocblockType(
$atomic_right_type . ' string length is never ' . $string_length,
new CodeLocation($statements_analyzer, $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new \Psalm\Issue\RedundantCondition(
$atomic_right_type . ' string length is never ' . $string_length,
new CodeLocation($statements_analyzer, $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
}
}
}
}
}
}
}
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
&& $stmt_left_type
&& $stmt_right_type

View File

@ -93,20 +93,6 @@ array_map(
error_log('Bad argument');
exit(1);
}
} elseif (substr($arg, 0, 2) === '-' && $arg !== '-' && $arg !== '--') {
$arg_name = preg_replace('/=.*$/', '', substr($arg, 1));
if (!in_array($arg_name, $valid_short_options, true)
&& !in_array($arg_name . ':', $valid_short_options, true)
) {
fwrite(
STDERR,
'Unrecognised argument "-' . $arg_name . '"' . PHP_EOL
. 'Type --help to see a list of supported arguments' . PHP_EOL
);
error_log('Bad argument');
exit(1);
}
}
},
$args

View File

@ -84,17 +84,6 @@ array_map(
);
exit(1);
}
} elseif (substr($arg, 0, 2) === '-' && $arg !== '-' && $arg !== '--') {
$arg_name = preg_replace('/=.*$/', '', substr($arg, 1));
if (!in_array($arg_name, $valid_short_options) && !in_array($arg_name . ':', $valid_short_options)) {
fwrite(
STDERR,
'Unrecognised argument "-' . $arg_name . '"' . PHP_EOL
. 'Type --help to see a list of supported arguments'. PHP_EOL
);
exit(1);
}
}
},
$args

View File

@ -106,17 +106,6 @@ array_map(
);
exit(1);
}
} elseif (substr($arg, 0, 2) === '-' && $arg !== '-' && $arg !== '--') {
$arg_name = preg_replace('/=.*$/', '', substr($arg, 1));
if (!in_array($arg_name, $valid_short_options) && !in_array($arg_name . ':', $valid_short_options)) {
fwrite(
STDERR,
'Unrecognised argument "-' . $arg_name . '"' . PHP_EOL
. 'Type --help to see a list of supported arguments'. PHP_EOL
);
exit(1);
}
}
},
$args

View File

@ -358,6 +358,17 @@ class BinaryOperationTest extends TestCase
$a = ~true;',
'error_message' => 'InvalidOperand',
],
'substrImpossible' => [
'<?php
class HelloWorld
{
public function sayHello(string $s): void
{
if (substr($s, 0, 6) === "abc") {}
}
}',
'error_message' => 'TypeDoesNotContainType',
],
];
}
}