Only allow parsing unset() as a top-level statement

E.g. `unset($x)/2` is a syntax error.
Previously, this parser would not treat that as a syntax error.

This is similar to how `echo` can only be used as a top level statement.

- Tools using this parser may expect `unset` only as a top level statement

See
https://secure.php.net/manual/en/function.unset.php#refsect1-function.unset-notes
This commit is contained in:
Tyson Andre 2018-09-23 23:08:37 -04:00
Родитель 7ea773435b
Коммит c9a8793682
7 изменённых файлов: 249 добавлений и 2 удалений

Просмотреть файл

@ -571,6 +571,9 @@ class Parser {
case TokenKind::ScriptSectionEndTag:
return $this->parseInlineHtml($parentNode);
case TokenKind::UnsetKeyword:
return $this->parseUnsetStatement($parentNode);
}
$expressionStatement = new ExpressionStatement();
@ -948,8 +951,6 @@ class Parser {
// intrinsic-construct
case TokenKind::ListKeyword:
return $this->parseListIntrinsicExpression($parentNode);
case TokenKind::UnsetKeyword:
return $this->parseUnsetIntrinsicExpression($parentNode);
// intrinsic-operator
case TokenKind::EmptyKeyword:
@ -2210,6 +2211,19 @@ class Parser {
return $expressionStatement;
}
private function parseUnsetStatement($parentNode) {
$expressionStatement = new ExpressionStatement();
// TODO: Could flatten into UnsetStatement instead?
$unsetExpression = $this->parseUnsetIntrinsicExpression($expressionStatement);
$expressionStatement->parent = $parentNode;
$expressionStatement->expression = $unsetExpression;
$expressionStatement->semicolon = $this->eatSemicolonOrAbortStatement();
return $expressionStatement;
}
private function parseListIntrinsicExpression($parentNode) {
$listExpression = new ListIntrinsicExpression();
$listExpression->parent = $parentNode;

Просмотреть файл

@ -0,0 +1,3 @@
<?php
unset($x, $y)
/ comment_fragment();

Просмотреть файл

@ -0,0 +1,14 @@
[
{
"kind": 0,
"message": "';' expected.",
"start": 19,
"length": 0
},
{
"kind": 0,
"message": "Unexpected '\/'",
"start": 20,
"length": 1
}
]

Просмотреть файл

@ -0,0 +1,111 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"UnsetIntrinsicExpression": {
"unsetKeyword": {
"kind": "UnsetKeyword",
"textLength": 5
},
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"expressions": {
"ExpressionList": {
"children": [
{
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
{
"kind": "CommaToken",
"textLength": 1
},
{
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
},
"semicolon": {
"error": "MissingToken",
"kind": "SemicolonToken",
"textLength": 0
}
}
},
{
"error": "SkippedToken",
"kind": "SlashToken",
"textLength": 1
},
{
"ExpressionStatement": {
"expression": {
"CallExpression": {
"callableExpression": {
"QualifiedName": {
"globalSpecifier": null,
"relativeSpecifier": null,
"nameParts": [
{
"kind": "Name",
"textLength": 16
}
]
}
},
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"argumentExpressionList": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,2 @@
<?php
2 / unset($x);

Просмотреть файл

@ -0,0 +1,14 @@
[
{
"kind": 0,
"message": "'Expression' expected.",
"start": 9,
"length": 0
},
{
"kind": 0,
"message": "';' expected.",
"start": 9,
"length": 0
}
]

Просмотреть файл

@ -0,0 +1,89 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"BinaryExpression": {
"leftOperand": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
},
"operator": {
"kind": "SlashToken",
"textLength": 1
},
"rightOperand": {
"error": "MissingToken",
"kind": "Expression",
"textLength": 0
}
}
},
"semicolon": {
"error": "MissingToken",
"kind": "SemicolonToken",
"textLength": 0
}
}
},
{
"ExpressionStatement": {
"expression": {
"UnsetIntrinsicExpression": {
"unsetKeyword": {
"kind": "UnsetKeyword",
"textLength": 5
},
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"expressions": {
"ExpressionList": {
"children": [
{
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}