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 @@ -145,6 +145,8 @@ 'ArcanistFlake8LinterTestCase' => 'lint/linter/__tests__/ArcanistFlake8LinterTestCase.php', 'ArcanistFormattedStringXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistFormattedStringXHPASTLinterRule.php', 'ArcanistFormattedStringXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistFormattedStringXHPASTLinterRuleTestCase.php', + 'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule.php', + 'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase.php', 'ArcanistFutureLinter' => 'lint/linter/ArcanistFutureLinter.php', 'ArcanistGeneratedLinter' => 'lint/linter/ArcanistGeneratedLinter.php', 'ArcanistGeneratedLinterTestCase' => 'lint/linter/__tests__/ArcanistGeneratedLinterTestCase.php', @@ -539,6 +541,8 @@ 'ArcanistFlake8LinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistFormattedStringXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistFormattedStringXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', + 'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', + 'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistFutureLinter' => 'ArcanistLinter', 'ArcanistGeneratedLinter' => 'ArcanistLinter', 'ArcanistGeneratedLinterTestCase' => 'ArcanistLinterTestCase', diff --git a/src/lint/linter/xhpast/rules/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule.php @@ -0,0 +1,54 @@ + 'bool', + 'doubleval' => 'double', + 'floatval' => 'double', + 'intval' => 'int', + 'strval' => 'string', + ); + + $casts = $this->getFunctionCalls($root, array_keys($cast_functions)); + + foreach ($casts as $cast) { + $function_name = $cast + ->getChildOfType(0, 'n_SYMBOL_NAME') + ->getConcreteString(); + $cast_name = $cast_functions[$function_name]; + + $parameters = $cast->getChildOfType(1, 'n_CALL_PARAMETER_LIST'); + $replacement = null; + + // Only suggest a replacement if the function call has exactly + // one parameter. + if (count($parameters->getChildren()) == 1) { + $parameter = $parameters->getChildByIndex(0); + $replacement = '('.$cast_name.')'.$parameter->getConcreteString(); + } + + $this->raiseLintAtNode( + $cast, + pht( + 'For consistency, use `%s` (a type cast) instead of `%s` '. + '(a function call). Function calls impose additional overhead.', + '('.$cast_name.')', + $function_name), + $replacement); + } + } + +} diff --git a/src/lint/linter/xhpast/rules/__tests__/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase.php b/src/lint/linter/xhpast/rules/__tests__/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase.php @@ -0,0 +1,11 @@ +executeTestsInDirectory( + dirname(__FILE__).'/function-call-should-be-type-cast/'); + } + +} diff --git a/src/lint/linter/xhpast/rules/__tests__/function-call-should-be-type-cast/cast-functions.lint-test b/src/lint/linter/xhpast/rules/__tests__/function-call-should-be-type-cast/cast-functions.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/function-call-should-be-type-cast/cast-functions.lint-test @@ -0,0 +1,19 @@ +