Use php-parser to work with literal _ number separator (#2321)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Tomas Votruba 2022-05-31 23:09:06 +02:00 committed by GitHub
parent 73055d87fb
commit f01725a084
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 61 additions and 67 deletions

View File

@ -60,6 +60,7 @@ expectedArguments(
\Rector\NodeTypeResolver\Node\AttributeKey::VIRTUAL_NODE,
\Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::RAW_VALUE,
);
expectedArguments(
@ -79,4 +80,5 @@ expectedArguments(
\Rector\NodeTypeResolver\Node\AttributeKey::VIRTUAL_NODE,
\Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION,
\Rector\NodeTypeResolver\Node\AttributeKey::RAW_VALUE,
);

View File

@ -15,7 +15,7 @@
"composer/xdebug-handler": "^3.0",
"doctrine/inflector": "^2.0",
"nette/utils": "^3.2.7",
"nikic/php-parser": "^4.13.2",
"nikic/php-parser": "^4.14.0",
"ondram/ci-detector": "^4.1",
"phpstan/phpdoc-parser": "^1.5.1",
"phpstan/phpstan": "^1.7.6",

View File

@ -9,6 +9,12 @@ namespace Rector\NodeTypeResolver\Node;
*/
final class AttributeKey
{
/**
* Internal php-parser key for String_, LNumber and DNumber nodes to hold original value (with "_" separators etc.)
* @var string
*/
public const RAW_VALUE = 'rawValue';
/**
* @var string
*/
@ -103,14 +109,6 @@ final class AttributeKey
*/
public const DOC_INDENTATION = 'docIndentation';
/**
* Internal php-parser name.
* Do not change this even if you want!
*
* @var string
*/
public const START_TOKEN_POSITION = 'startTokenPos';
/**
* @var string
* Use often in php-parser

View File

@ -2,13 +2,11 @@
namespace Rector\Tests\DowngradePhp74\Rector\LNumber\DowngradeNumericLiteralSeparatorRector\Fixture;
class Fixture
final class DowngradeFloats
{
public function run()
{
$int = 1_000;
$float = 1_000_500.001;
$negative_int = -1_000;
$negative_float = -1_000_500.001;
}
}
@ -19,13 +17,11 @@ class Fixture
namespace Rector\Tests\DowngradePhp74\Rector\LNumber\DowngradeNumericLiteralSeparatorRector\Fixture;
class Fixture
final class DowngradeFloats
{
public function run()
{
$int = 1000;
$float = 1000500.001;
$negative_int = -1000;
$negative_float = -1000500.001;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\Tests\DowngradePhp74\Rector\LNumber\DowngradeNumericLiteralSeparatorRector\Fixture;
final class DowngradeIntegers
{
public function run()
{
$int = 1_000;
$negative_int = -1_000;
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp74\Rector\LNumber\DowngradeNumericLiteralSeparatorRector\Fixture;
final class DowngradeIntegers
{
public function run()
{
$int = 1000;
$negative_int = -1000;
}
}
?>

View File

@ -60,7 +60,6 @@ final class NodeByTypeAndPositionCollector
foreach ($assignedVariablesUse as $assignedVariableUse) {
$startTokenPos = $assignedVariableUse->getStartTokenPos();
// "-1" is empty value default
if ($startTokenPos === -1) {
continue;

View File

@ -64,40 +64,27 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
$rawValue = $node->getAttribute(AttributeKey::RAW_VALUE);
if ($this->shouldSkip($node, $rawValue)) {
return null;
}
$numberValueAsString = (string) $node->value;
if (\str_contains($numberValueAsString, '+')) {
if (\str_contains((string) $rawValue, '+')) {
return null;
}
// trigger reprint
$node->setAttribute(AttributeKey::ORIGINAL_NODE, null);
if ($node instanceof LNumber) {
return $node;
}
/**
* This code follows a guess, to avoid modifying floats needlessly.
* If the node is a float, but it doesn't contain ".",
* then it's likely that the number was forced to be a float
* by adding ".0" at the end (eg: 0.0).
* Then, add it again.
*/
if (! \str_contains($numberValueAsString, '.')) {
$numberValueAsString .= '.0';
}
$node->value = (float) $numberValueAsString;
return $node;
}
private function shouldSkip(LNumber | DNumber $node): bool
private function shouldSkip(LNumber | DNumber $node, mixed $rawValue): bool
{
if (! is_string($rawValue)) {
return true;
}
// "_" notation can be applied to decimal numbers only
if ($node instanceof LNumber) {
$numberKind = $node->getAttribute(AttributeKey::KIND);
@ -106,14 +93,6 @@ CODE_SAMPLE
}
}
// we have to hack around tokens to get original value, see https://github.com/nikic/PHP-Parser/pull/832
$oldTokens = $this->file->getOldTokens();
$tokenValue = $oldTokens[$node->getStartTokenPos()][1] ?? null;
if ($tokenValue === null) {
return true;
}
return ! str_contains((string) $tokenValue, '_');
return ! str_contains($rawValue, '_');
}
}

View File

@ -107,18 +107,19 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
$numericValueAsString = (string) $node->value;
if ($this->shouldSkip($node, $numericValueAsString)) {
$rawValue = $node->getAttribute(AttributeKey::RAW_VALUE);
if ($this->shouldSkip($node, $rawValue)) {
return null;
}
if (\str_contains($numericValueAsString, '.')) {
[$mainPart, $decimalPart] = explode('.', $numericValueAsString);
if (\str_contains((string) $rawValue, '.')) {
[$mainPart, $decimalPart] = explode('.', (string) $rawValue);
$chunks = $this->strSplitNegative($mainPart, self::GROUP_SIZE);
$literalSeparatedNumber = implode('_', $chunks) . '.' . $decimalPart;
} else {
$chunks = $this->strSplitNegative($numericValueAsString, self::GROUP_SIZE);
$chunks = $this->strSplitNegative($rawValue, self::GROUP_SIZE);
$literalSeparatedNumber = implode('_', $chunks);
// PHP converts: (string) 1000.0 -> "1000"!
@ -137,28 +138,18 @@ CODE_SAMPLE
return PhpVersionFeature::LITERAL_SEPARATOR;
}
private function shouldSkip(LNumber | DNumber $node, string $numericValueAsString): bool
private function shouldSkip(LNumber | DNumber $node, mixed $rawValue): bool
{
$startTokenPos = $node->getStartTokenPos();
$oldTokens = $this->file->getOldTokens();
$tokenValue = $oldTokens[$startTokenPos][1] ?? null;
if (! is_string($tokenValue)) {
if (! is_string($rawValue)) {
return true;
}
// already contains separator
if (str_contains($tokenValue, '_')) {
if (str_contains($rawValue, '_')) {
return true;
}
if ($numericValueAsString < $this->limitValue) {
return true;
}
// already separated
if (\str_contains($numericValueAsString, '_')) {
if ($node->value < $this->limitValue) {
return true;
}
@ -168,12 +159,12 @@ CODE_SAMPLE
}
// e+/e-
if (StringUtils::isMatch($numericValueAsString, '#e#i')) {
if (StringUtils::isMatch($rawValue, '#e#i')) {
return true;
}
// too short
return Strings::length($numericValueAsString) <= self::GROUP_SIZE;
return Strings::length($rawValue) <= self::GROUP_SIZE;
}
/**