Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #984 from PHPCompatibility/php74/new-numeric-liter…
…als-sniff PHP 7.4: New NewNumericLiteralSeparator sniff
- Loading branch information
Showing
3 changed files
with
268 additions
and
0 deletions.
There are no files selected for viewing
86 changes: 86 additions & 0 deletions
86
PHPCompatibility/Sniffs/Numbers/NewNumericLiteralSeparatorSniff.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
/** | ||
* PHPCompatibility, an external standard for PHP_CodeSniffer. | ||
* | ||
* @package PHPCompatibility | ||
* @copyright 2012-2020 PHPCompatibility Contributors | ||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 | ||
* @link https://github.com/PHPCompatibility/PHPCompatibility | ||
*/ | ||
|
||
namespace PHPCompatibility\Sniffs\Numbers; | ||
|
||
use PHP_CodeSniffer_File as File; | ||
use PHP_CodeSniffer\Exceptions\RuntimeException; | ||
use PHPCompatibility\Sniff; | ||
use PHPCSUtils\Utils\Numbers; | ||
|
||
/** | ||
* Support for an underscore in numeric literals to visually separate groups of digits | ||
* is available since PHP 7.4. | ||
* | ||
* PHP version 7.4 | ||
* | ||
* @link https://www.php.net/manual/en/migration74.new-features.php#migration74.new-features.core.numeric-literal-separator | ||
* @link https://wiki.php.net/rfc/numeric_literal_separator | ||
* | ||
* @since 10.0.0 | ||
*/ | ||
class NewNumericLiteralSeparatorSniff extends Sniff | ||
{ | ||
|
||
/** | ||
* Returns an array of tokens this test wants to listen for. | ||
* | ||
* @since 10.0.0 | ||
* | ||
* @return array | ||
*/ | ||
public function register() | ||
{ | ||
return array( | ||
\T_LNUMBER, | ||
\T_DNUMBER, | ||
); | ||
} | ||
|
||
/** | ||
* Processes this test, when one of its tokens is encountered. | ||
* | ||
* @since 10.0.0 | ||
* | ||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned. | ||
* @param int $stackPtr The position of the current token in the | ||
* stack passed in $tokens. | ||
* | ||
* @return int|void Integer stack pointer to skip forward or void to continue | ||
* normal file processing. | ||
*/ | ||
public function process(File $phpcsFile, $stackPtr) | ||
{ | ||
if ($this->supportsBelow('7.3') === false) { | ||
return; | ||
} | ||
|
||
try { | ||
$numberInfo = Numbers::getCompleteNumber($phpcsFile, $stackPtr); | ||
if ($numberInfo['orig_content'] === $numberInfo['content']) { | ||
// Content is the same, i.e. no underscores found, move on. | ||
return; | ||
} | ||
|
||
$phpcsFile->addError( | ||
'The use of underscore separators in numeric literals is not supported in PHP 7.3 or lower. Found: %s', | ||
$stackPtr, | ||
'Found', | ||
array($numberInfo['orig_content']) | ||
); | ||
|
||
// Skip past the parts we've already taken into account to prevent double reporting. | ||
return ($numberInfo['last_token'] + 1); | ||
} catch (RuntimeException $e) { | ||
// Running on an unsupport PHPCS version. | ||
return; | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
PHPCompatibility/Tests/Numbers/NewNumericLiteralSeparatorUnitTest.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
// OK: ordinary numeric sequences. | ||
$a = 1000000000; | ||
$b = 107925284.88; | ||
$discount = 13500; | ||
|
||
// OK: Constant name starting with an underscore is perfectly valid. | ||
const _100 = 100; | ||
|
||
$a = 100 'test'; // Intentional parse error, not our concern. | ||
|
||
// PHP 7.4 numeric sequences with a numeric literal separator. | ||
$threshold = 1_000_000_000; // a billion! | ||
$testValue = 107_925_284.88; // scale is hundreds of millions | ||
$discount = 135_00; // $135, stored as cents | ||
|
||
$a = 6.674_083e-11; // float | ||
$a = 6.674_083e+11; // float | ||
$a = 299_792_458; // decimal | ||
$a = 0xCAFE_F00D; // hexadecimal | ||
$a = 0b0101_1111; // binary | ||
$a = 0137_041; // octal | ||
|
||
// More test cases. | ||
$a = 1_2.3_4e1_23; | ||
|
||
// Invalid use of underscores in numeric sequences. | ||
// Each underscore in a numeric literal must be directly between two digits. | ||
// These below all produce "Parse error: syntax error" in PHP 7.4. | ||
$a = 100_; // trailing | ||
$a = 1__1; // next to underscore | ||
$a = 1_.0; // next to decimal point | ||
$a = 1._0; // next to decimal point | ||
$a = 0x_123; // next to x | ||
$a = 0b_101; // next to b | ||
$a = 1_e2; // next to e | ||
$a = 1e_2; // next to e | ||
|
||
// More test cases. | ||
$a = 0xCAFE_F00D_.892; |
141 changes: 141 additions & 0 deletions
141
PHPCompatibility/Tests/Numbers/NewNumericLiteralSeparatorUnitTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
<?php | ||
/** | ||
* PHPCompatibility, an external standard for PHP_CodeSniffer. | ||
* | ||
* @package PHPCompatibility | ||
* @copyright 2012-2020 PHPCompatibility Contributors | ||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 | ||
* @link https://github.com/PHPCompatibility/PHPCompatibility | ||
*/ | ||
|
||
namespace PHPCompatibility\Tests\Numbers; | ||
|
||
use PHPCompatibility\Tests\BaseSniffTest; | ||
use PHPCSUtils\BackCompat\Helper; | ||
|
||
/** | ||
* New Numeric Literal Separator Sniff tests | ||
* | ||
* @group newNumericLiteralSeparator | ||
* @group numbers | ||
* | ||
* @covers \PHPCompatibility\Sniffs\Numbers\NewNumericLiteralSeparatorSniff | ||
* | ||
* @since 10.0.0 | ||
*/ | ||
class NewNumericLiteralSeparatorUnitTest extends BaseSniffTest | ||
{ | ||
|
||
/** | ||
* Test recognizing numeric literals with underscores correctly. | ||
* | ||
* @dataProvider dataNewNumericLiteralSeparator | ||
* | ||
* @param array $line The line number on which the error should occur. | ||
* | ||
* @return void | ||
*/ | ||
public function testNewNumericLiteralSeparator($line) | ||
{ | ||
if (version_compare(Helper::getVersion(), '3.5.3', '==')) { | ||
$this->markTestSkipped('PHPCS 3.5.3 is not supported for this sniff'); | ||
} | ||
|
||
$file = $this->sniffFile(__FILE__, '7.3'); | ||
$this->assertError($file, $line, 'The use of underscore separators in numeric literals is not supported in PHP 7.3 or lower. Found:'); | ||
} | ||
|
||
/** | ||
* Data provider. | ||
* | ||
* @see testNewNumericLiteralSeparator() | ||
* | ||
* @return array | ||
*/ | ||
public function dataNewNumericLiteralSeparator() | ||
{ | ||
$data = array( | ||
array(14), | ||
array(15), | ||
array(16), | ||
array(18), | ||
array(19), | ||
array(20), | ||
array(21), | ||
array(22), | ||
array(23), | ||
array(26), | ||
); | ||
|
||
// The test case on line 39 is half a valid numeric literal with underscore, half parse error. | ||
// The sniff will behave differently on PHP 7.4 vs PHP < 7.4. | ||
if (version_compare(\PHP_VERSION_ID, '70399', '>') || version_compare(Helper::getVersion(), '3.5.3', '<')) { | ||
$data[] = array(41); | ||
} | ||
|
||
return $data; | ||
} | ||
|
||
|
||
/** | ||
* Verify there are no false positives for a PHP version on which this sniff throws errors. | ||
* | ||
* @dataProvider dataNoFalsePositives | ||
* | ||
* @param int $line The line number. | ||
* | ||
* @return void | ||
*/ | ||
public function testNoFalsePositives($line) | ||
{ | ||
$file = $this->sniffFile(__FILE__, '7.3'); | ||
$this->assertNoViolation($file, $line); | ||
} | ||
|
||
/** | ||
* Data provider. | ||
* | ||
* @see testNoFalsePositives() | ||
* | ||
* @return array | ||
*/ | ||
public function dataNoFalsePositives() | ||
{ | ||
$data = array(); | ||
|
||
// No issues expected on the first 12 lines. | ||
for ($i = 1; $i <= 12; $i++) { | ||
$data[] = array($i); | ||
} | ||
|
||
// Parse errors, should be ignored by the sniff. | ||
$data[] = array(31); | ||
$data[] = array(32); | ||
$data[] = array(33); | ||
$data[] = array(34); | ||
$data[] = array(35); | ||
$data[] = array(36); | ||
$data[] = array(37); | ||
$data[] = array(38); | ||
|
||
// The test case on line 39 is half a valid numeric literal with underscore, half parse error. | ||
// The sniff will behave differently on PHP 7.4 vs PHP < 7.4. | ||
if (version_compare(\PHP_VERSION_ID, '70399', '<=') && version_compare(Helper::getVersion(), '3.5.3', '>')) { | ||
$data[] = array(41); | ||
} | ||
|
||
return $data; | ||
} | ||
|
||
|
||
/** | ||
* Verify no notices are thrown at all. | ||
* | ||
* @return void | ||
*/ | ||
public function testNoViolationsInFileOnValidVersion() | ||
{ | ||
$file = $this->sniffFile(__FILE__, '7.4'); | ||
$this->assertNoViolation($file); | ||
} | ||
} |