1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 00:02:40 +01:00

Allow AAST to extract string literal values from HEREDOCs

Summary:
Ref T13577. I'd like to `arc lint --everything` to find other bad calls to `pht()` and similar functions, but `n_HEREDOC` nodes currently can not generate a response to "getStringLiteralValue()".

Support literal extraction from heredocs.

Test Plan: Added a test, made it pass. Will lint everything.

Maniphest Tasks: T13577

Differential Revision: https://secure.phabricator.com/D21455
This commit is contained in:
epriestley 2020-09-04 17:24:25 -07:00
parent 1c327208d7
commit 83e63aeb07
2 changed files with 49 additions and 16 deletions

View file

@ -14,6 +14,16 @@ foobar(null, null, '%s');
pht('x %s y');
pht('x %s y'.'z');
pht(<<<HEREDOC
a b c
HEREDOC
);
pht(<<<HEREDOC
a %s c
HEREDOC
);
~~~~~~~~~~
error:3:1:XHP54:Formatted String
error:7:1:XHP54:Formatted String
@ -22,6 +32,7 @@ error:11:1:XHP54:Formatted String
error:13:1:XHP54:Formatted String
error:15:1:XHP54:Formatted String
error:16:1:XHP54:Formatted String
error:23:1:XHP54:Formatted String
~~~~~~~~~~
~~~~~~~~~~
{

View file

@ -33,7 +33,9 @@ final class XHPASTNode extends AASTNode {
return $this->getChildByIndex(0)->evalStatic();
break;
case 'n_STRING_SCALAR':
return (string)$this->getStringLiteralValue();
return phutil_string_cast($this->getStringLiteralValue());
case 'n_HEREDOC':
return phutil_string_cast($this->getStringLiteralValue());
case 'n_NUMERIC_SCALAR':
$value = $this->getSemanticString();
if (preg_match('/^0x/i', $value)) {
@ -186,31 +188,51 @@ final class XHPASTNode extends AASTNode {
}
public function getStringLiteralValue() {
if ($this->getTypeName() != 'n_STRING_SCALAR') {
return null;
$type_name = $this->getTypeName();
if ($type_name === 'n_HEREDOC') {
$value = $this->getSemanticString();
$value = phutil_split_lines($value);
$value = array_slice($value, 1, -1);
$value = implode('', $value);
// Strip the final newline from value, this isn't part of the string
// literal.
$value = preg_replace('/(\r|\n|\r\n)\z/', '', $value);
return $this->newStringLiteralFromSemanticString($value);
}
$value = $this->getSemanticString();
$type = $value[0];
$value = preg_replace('/^b?[\'"]|[\'"]$/i', '', $value);
$esc = false;
$len = strlen($value);
$out = '';
if ($type_name === 'n_STRING_SCALAR') {
$value = $this->getSemanticString();
$type = $value[0];
$value = preg_replace('/^b?[\'"]|[\'"]$/i', '', $value);
if ($type == "'") {
// Single quoted strings treat everything as a literal except "\\" and
// "\'".
return str_replace(
array('\\\\', '\\\''),
array('\\', "'"),
$value);
if ($type == "'") {
// Single quoted strings treat everything as a literal except "\\" and
// "\'".
return str_replace(
array('\\\\', '\\\''),
array('\\', "'"),
$value);
}
return $this->newStringLiteralFromSemanticString($value);
}
return null;
}
private function newStringLiteralFromSemanticString($value) {
// Double quoted strings treat "\X" as a literal if X isn't specifically
// a character which needs to be escaped -- e.g., "\q" and "\'" are
// literally "\q" and "\'". stripcslashes() is too aggressive, so find
// all these under-escaped backslashes and escape them.
$len = strlen($value);
$esc = false;
$out = '';
for ($ii = 0; $ii < $len; $ii++) {
$c = $value[$ii];
if ($esc) {