mirror of https://github.com/rectorphp/rector.git
Use php-parser to work with literal _ number separator (#2321)
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
73055d87fb
commit
f01725a084
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -60,7 +60,6 @@ final class NodeByTypeAndPositionCollector
|
|||
|
||||
foreach ($assignedVariablesUse as $assignedVariableUse) {
|
||||
$startTokenPos = $assignedVariableUse->getStartTokenPos();
|
||||
|
||||
// "-1" is empty value default
|
||||
if ($startTokenPos === -1) {
|
||||
continue;
|
||||
|
|
|
@ -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, '_');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue