From 90ac9a2ff281d64861ddb2a6bc08159a10303a3a Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Mon, 11 Jan 2021 04:04:59 +0000 Subject: [PATCH] Fix ArcanistFormattedStringXHPASTLinterRule for PHP 8 Summary: PHP 8's sprintf raises a ValueError when encountering unknown format specifiers (previously it would eat the argument and print nothing), so linting format strings like %Ls dies with an uncaught ValueError. Fix this by using a custom callback during linting to turn all format specifiers into %s and replace the dummy null argument with the original format specifier, ensuring we always end up providing valid input to the sprintf at the end. This has the nice property that the output of the call to xsprintf is the original format string, though any transformation into valid input would do. Test Plan: Ran arc lint Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T13588 Differential Revision: https://secure.phabricator.com/D21500 --- ...rcanistFormattedStringXHPASTLinterRule.php | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/lint/linter/xhpast/rules/ArcanistFormattedStringXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistFormattedStringXHPASTLinterRule.php index 7b5ed3bb..1f35cbf1 100644 --- a/src/lint/linter/xhpast/rules/ArcanistFormattedStringXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistFormattedStringXHPASTLinterRule.php @@ -94,7 +94,10 @@ final class ArcanistFormattedStringXHPASTLinterRule $argv = array($format->evalStatic()) + array_fill(0, $argc, null); try { - xsprintf(null, null, $argv); + xsprintf( + 'ArcanistFormattedStringXHPASTLinterRule::processXsprintfCallback', + null, + $argv); } catch (BadFunctionCallException $ex) { $this->raiseLintAtNode( $call, @@ -105,4 +108,23 @@ final class ArcanistFormattedStringXHPASTLinterRule } } + public static function processXsprintfCallback( + $userdata, + &$pattern, + &$pos, + &$value, + &$length) { + + if ($value !== null) { + throw new Exception('Expected dummy value to be null'); + } + + // Turn format "%$pattern" with argument null into format "%s" with + // argument "%$pattern". This ensures we always provide valid input for + // sprintf to avoid getting a ValueError when using custom format + // specifiers. + $value = '%'.$pattern[$pos]; + $pattern[$pos] = 's'; + } + }