diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -32,6 +32,8 @@ 'ArcanistBaseXHPASTLinter' => 'lint/linter/ArcanistBaseXHPASTLinter.php', 'ArcanistBinaryExpressionSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBinaryExpressionSpacingXHPASTLinterRule.php', 'ArcanistBinaryExpressionSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBinaryExpressionSpacingXHPASTLinterRuleTestCase.php', + 'ArcanistBinaryNumericScalarCasingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBinaryNumericScalarCasingXHPASTLinterRule.php', + 'ArcanistBinaryNumericScalarCasingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBinaryNumericScalarCasingXHPASTLinterRuleTestCase.php', 'ArcanistBlacklistedFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBlacklistedFunctionXHPASTLinterRule.php', 'ArcanistBlacklistedFunctionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBlacklistedFunctionXHPASTLinterRuleTestCase.php', 'ArcanistBookmarkWorkflow' => 'workflow/ArcanistBookmarkWorkflow.php', @@ -434,6 +436,8 @@ 'ArcanistBaseXHPASTLinter' => 'ArcanistFutureLinter', 'ArcanistBinaryExpressionSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistBinaryExpressionSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', + 'ArcanistBinaryNumericScalarCasingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', + 'ArcanistBinaryNumericScalarCasingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistBlacklistedFunctionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistBlacklistedFunctionXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistBookmarkWorkflow' => 'ArcanistFeatureWorkflow', diff --git a/src/lint/linter/xhpast/rules/ArcanistBinaryNumericScalarCasingXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistBinaryNumericScalarCasingXHPASTLinterRule.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/ArcanistBinaryNumericScalarCasingXHPASTLinterRule.php @@ -0,0 +1,49 @@ +<?php + +final class ArcanistBinaryNumericScalarCasingXHPASTLinterRule + extends ArcanistXHPASTLinterRule { + + const ID = 131; + + public function getLintName() { + return pht('Binary Integer Casing'); + } + + public function getLintSeverity() { + return ArcanistLintSeverity::SEVERITY_WARNING; + } + + public function process(XHPASTNode $root) { + $binaries = $this->getBinaryNumericScalars($root); + + foreach ($binaries as $binary) { + $value = substr($binary->getConcreteString(), 2); + + if (!preg_match('/^0b[01]+$/', $binary->getConcreteString())) { + $this->raiseLintAtNode( + $binary, + pht( + 'For consistency, write binary integers with a leading `%s`.', + '0b'), + '0b'.$value); + } + } + } + + private function getBinaryNumericScalars(XHPASTNode $root) { + $numeric_scalars = $root->selectDescendantsOfType('n_NUMERIC_SCALAR'); + $binary_numeric_scalars = array(); + + foreach ($numeric_scalars as $numeric_scalar) { + $number = $numeric_scalar->getConcreteString(); + + if (preg_match('/^0b[01]+$/i', $number)) { + $binary_numeric_scalars[] = $numeric_scalar; + } + } + + return $binary_numeric_scalars; + + } + +} diff --git a/src/lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php --- a/src/lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php @@ -6,7 +6,7 @@ const ID = 127; public function getLintName() { - return pht('Hexadecimal Casing'); + return pht('Hexadecimal Integer Casing'); } public function getLintSeverity() { @@ -23,8 +23,8 @@ $this->raiseLintAtNode( $hexadecimal, pht( - 'For consistency, write hexadecimals in uppercase '. - 'with a leading `%s`.', + 'For consistency, write hexadecimals integers '. + 'in uppercase with a leading `%s`.', '0x'), '0x'.strtoupper($value)); } diff --git a/src/lint/linter/xhpast/rules/__tests__/ArcanistBinaryNumericScalarCasingXHPASTLinterRuleTestCase.php b/src/lint/linter/xhpast/rules/__tests__/ArcanistBinaryNumericScalarCasingXHPASTLinterRuleTestCase.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/ArcanistBinaryNumericScalarCasingXHPASTLinterRuleTestCase.php @@ -0,0 +1,11 @@ +<?php + +final class ArcanistBinaryNumericScalarCasingXHPASTLinterRuleTestCase + extends ArcanistXHPASTLinterRuleTestCase { + + public function testLinter() { + $this->executeTestsInDirectory( + dirname(__FILE__).'/binary-numeric-scalar-casing/'); + } + +} diff --git a/src/lint/linter/xhpast/rules/__tests__/binary-numeric-scalar-casing/binary.lint-test b/src/lint/linter/xhpast/rules/__tests__/binary-numeric-scalar-casing/binary.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/binary-numeric-scalar-casing/binary.lint-test @@ -0,0 +1,5 @@ +<?php +0b1; +0B1; +~~~~~~~~~~ +warning:3:1