Merge pull request #245 from TysonAndre/fix-short-echo
Fixes #220 : Properly parse expression lists passed to `<?=`
This commit is contained in:
Коммит
bce97ae2fc
|
@ -10,9 +10,18 @@ use Microsoft\PhpParser\Node\Expression;
|
|||
use Microsoft\PhpParser\Node\DelimitedList\ExpressionList;
|
||||
use Microsoft\PhpParser\Token;
|
||||
|
||||
/**
|
||||
* This represents either a literal echo expression (`echo expr`)
|
||||
* or a short echo tag (`<?= expr...`)
|
||||
*
|
||||
* TODO: An echo statement cannot be used as an expression.
|
||||
* Consider refactoring this to become EchoStatement in a future backwards incompatible release.
|
||||
*/
|
||||
class EchoExpression extends Expression {
|
||||
|
||||
/** @var Token */
|
||||
/**
|
||||
* @var Token|null this is null if generated from `<?=`
|
||||
*/
|
||||
public $echoKeyword;
|
||||
|
||||
/** @var ExpressionList */
|
||||
|
|
|
@ -19,9 +19,23 @@ class InlineHtml extends StatementNode {
|
|||
/** @var Token|null */
|
||||
public $scriptSectionStartTag;
|
||||
|
||||
/**
|
||||
* @var ExpressionStatement|null used to represent the expression echoed by `<?=` while parsing.
|
||||
*
|
||||
* This should always be null in the returned AST,
|
||||
* and is deliberately excluded from CHILD_NAMES.
|
||||
*
|
||||
* This will be null under any of these conditions:
|
||||
*
|
||||
* - The scriptSectionStartTag isn't TokenKind::ScriptSectionStartWithEchoTag,
|
||||
* - The echoStatement was normalized and moved into a statement list.
|
||||
* If a caller doesn't do this, that's a bug.
|
||||
*/
|
||||
public $echoStatement;
|
||||
|
||||
const CHILD_NAMES = [
|
||||
'scriptSectionEndTag',
|
||||
'text',
|
||||
'scriptSectionStartTag'
|
||||
'scriptSectionStartTag',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -152,7 +152,13 @@ class Parser {
|
|||
$sourceFile->uri = $uri;
|
||||
$sourceFile->statementList = array();
|
||||
if ($this->getCurrentToken()->kind !== TokenKind::EndOfFileToken) {
|
||||
$sourceFile->statementList[] = $this->parseInlineHtml($sourceFile);
|
||||
$inlineHTML = $this->parseInlineHtml($sourceFile);
|
||||
$sourceFile->statementList[] = $inlineHTML;
|
||||
if ($inlineHTML->echoStatement) {
|
||||
$sourceFile->statementList[] = $inlineHTML->echoStatement;
|
||||
$inlineHTML->echoStatement->parent = $sourceFile;
|
||||
$inlineHTML->echoStatement = null;
|
||||
}
|
||||
}
|
||||
$sourceFile->statementList =
|
||||
\array_merge($sourceFile->statementList, $this->parseList($sourceFile, ParseContext::SourceElements));
|
||||
|
@ -192,6 +198,11 @@ class Parser {
|
|||
$element = $parseListElementFn($parentNode);
|
||||
if ($element instanceof Node) {
|
||||
$element->parent = $parentNode;
|
||||
if ($element instanceof InlineHtml && $element->echoStatement && $listParseContext === ParseContext::SourceElements) {
|
||||
$nodeArray[] = $element->echoStatement;
|
||||
$element->echoStatement->parent = $parentNode;
|
||||
$element->echoStatement = null;
|
||||
}
|
||||
}
|
||||
$nodeArray[] = $element;
|
||||
continue;
|
||||
|
@ -534,6 +545,9 @@ class Parser {
|
|||
case TokenKind::SemicolonToken:
|
||||
return $this->parseEmptyStatement($parentNode);
|
||||
|
||||
case TokenKind::EchoKeyword:
|
||||
return $this->parseEchoStatement($parentNode);
|
||||
|
||||
// trait-declaration
|
||||
case TokenKind::TraitKeyword:
|
||||
return $this->parseTraitDeclaration($parentNode);
|
||||
|
@ -931,8 +945,6 @@ class Parser {
|
|||
return $this->parseArrayCreationExpression($parentNode);
|
||||
|
||||
// intrinsic-construct
|
||||
case TokenKind::EchoKeyword:
|
||||
return $this->parseEchoExpression($parentNode);
|
||||
case TokenKind::ListKeyword:
|
||||
return $this->parseListIntrinsicExpression($parentNode);
|
||||
case TokenKind::UnsetKeyword:
|
||||
|
@ -2172,14 +2184,21 @@ class Parser {
|
|||
return $scriptInclusionExpression;
|
||||
}
|
||||
|
||||
private function parseEchoExpression($parentNode) {
|
||||
private function parseEchoStatement($parentNode) {
|
||||
$expressionStatement = new ExpressionStatement();
|
||||
|
||||
// TODO: Could flatten into EchoStatement instead?
|
||||
$echoExpression = new EchoExpression();
|
||||
$echoExpression->parent = $parentNode;
|
||||
$echoExpression->parent = $expressionStatement;
|
||||
$echoExpression->echoKeyword = $this->eat1(TokenKind::EchoKeyword);
|
||||
$echoExpression->expressions =
|
||||
$this->parseExpressionList($echoExpression);
|
||||
|
||||
return $echoExpression;
|
||||
$expressionStatement->parent = $parentNode;
|
||||
$expressionStatement->expression = $echoExpression;
|
||||
$expressionStatement->semicolon = $this->eatSemicolonOrAbortStatement();
|
||||
|
||||
return $expressionStatement;
|
||||
}
|
||||
|
||||
private function parseListIntrinsicExpression($parentNode) {
|
||||
|
@ -3202,7 +3221,24 @@ class Parser {
|
|||
$inlineHtml->parent = $parentNode;
|
||||
$inlineHtml->scriptSectionEndTag = $this->eatOptional1(TokenKind::ScriptSectionEndTag);
|
||||
$inlineHtml->text = $this->eatOptional1(TokenKind::InlineHtml);
|
||||
$inlineHtml->scriptSectionStartTag = $this->eatOptional1(TokenKind::ScriptSectionStartTag);
|
||||
$inlineHtml->scriptSectionStartTag = $this->eatOptional(TokenKind::ScriptSectionStartTag, TokenKind::ScriptSectionStartWithEchoTag);
|
||||
|
||||
// This is the easiest way to represent `<?= "expr", "other" `
|
||||
if (($inlineHtml->scriptSectionStartTag->kind ?? null) === TokenKind::ScriptSectionStartWithEchoTag) {
|
||||
$echoStatement = new ExpressionStatement();
|
||||
|
||||
$echoExpression = new EchoExpression();
|
||||
$expressionList = $this->parseExpressionList($echoExpression) ?? (new MissingToken(TokenKind::Expression, $this->token->fullStart));
|
||||
$echoExpression->expressions = $expressionList;
|
||||
$echoExpression->parent = $echoStatement;
|
||||
|
||||
$echoStatement->expression = $echoExpression;
|
||||
$echoStatement->semicolon = $this->eatSemicolonOrAbortStatement();
|
||||
$echoStatement->parent = $inlineHtml;
|
||||
// Deliberately leave echoKeyword as null instead of MissingToken
|
||||
|
||||
$inlineHtml->echoStatement = $echoStatement;
|
||||
}
|
||||
|
||||
return $inlineHtml;
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ class PhpTokenizer implements TokenStreamProviderInterface {
|
|||
T_DNUMBER => TokenKind::FloatingLiteralToken,
|
||||
|
||||
T_OPEN_TAG => TokenKind::ScriptSectionStartTag,
|
||||
T_OPEN_TAG_WITH_ECHO => TokenKind::ScriptSectionStartTag,
|
||||
T_OPEN_TAG_WITH_ECHO => TokenKind::ScriptSectionStartWithEchoTag,
|
||||
T_CLOSE_TAG => TokenKind::ScriptSectionEndTag,
|
||||
|
||||
T_INLINE_HTML => TokenKind::InlineHtml,
|
||||
|
|
|
@ -167,7 +167,7 @@ class TokenKind {
|
|||
|
||||
const ScriptSectionStartTag = 323;
|
||||
const ScriptSectionEndTag = 324;
|
||||
|
||||
const ScriptSectionStartWithEchoTag = 419;
|
||||
|
||||
// TODO how to handle incremental parsing w/ this?
|
||||
const ScriptSectionPrependedText = 325;
|
||||
|
|
|
@ -164,7 +164,7 @@ class TokenStringMaps {
|
|||
"<>" => TokenKind::LessThanGreaterThanToken,
|
||||
"..." => TokenKind::DotDotDotToken,
|
||||
"\\" => TokenKind::BackslashToken,
|
||||
"<?=" => TokenKind::ScriptSectionStartTag, // TODO, technically not an operator
|
||||
"<?=" => TokenKind::ScriptSectionStartWithEchoTag, // TODO, technically not an operator
|
||||
"<?php " => TokenKind::ScriptSectionStartTag, // TODO, technically not an operator
|
||||
"<?php\t" => TokenKind::ScriptSectionStartTag, // TODO add tests
|
||||
"<?php\n" => TokenKind::ScriptSectionStartTag,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"kind": "ScriptSectionStartWithEchoTag",
|
||||
"textLength": 3
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,24 @@
|
|||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"StringLiteral": {
|
||||
"startQuote": null,
|
||||
"children": {
|
||||
"kind": "StringLiteralToken",
|
||||
"textLength": 7
|
||||
},
|
||||
"endQuote": null
|
||||
"EchoExpression": {
|
||||
"echoKeyword": null,
|
||||
"expressions": {
|
||||
"ExpressionList": {
|
||||
"children": [
|
||||
{
|
||||
"StringLiteral": {
|
||||
"startQuote": null,
|
||||
"children": {
|
||||
"kind": "StringLiteralToken",
|
||||
"textLength": 7
|
||||
},
|
||||
"endQuote": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": null
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<?="hello","world"?>
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartWithEchoTag",
|
||||
"textLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"EchoExpression": {
|
||||
"echoKeyword": null,
|
||||
"expressions": {
|
||||
"ExpressionList": {
|
||||
"children": [
|
||||
{
|
||||
"StringLiteral": {
|
||||
"startQuote": null,
|
||||
"children": {
|
||||
"kind": "StringLiteralToken",
|
||||
"textLength": 7
|
||||
},
|
||||
"endQuote": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "CommaToken",
|
||||
"textLength": 1
|
||||
},
|
||||
{
|
||||
"StringLiteral": {
|
||||
"startQuote": null,
|
||||
"children": {
|
||||
"kind": "StringLiteralToken",
|
||||
"textLength": 7
|
||||
},
|
||||
"endQuote": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": {
|
||||
"kind": "ScriptSectionEndTag",
|
||||
"textLength": 2
|
||||
},
|
||||
"text": null,
|
||||
"scriptSectionStartTag": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<?= echo "hello"; ?>
|
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'Expression' expected.",
|
||||
"start": 3,
|
||||
"length": 0
|
||||
},
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "';' expected.",
|
||||
"start": 3,
|
||||
"length": 0
|
||||
}
|
||||
]
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartWithEchoTag",
|
||||
"textLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"EchoExpression": {
|
||||
"echoKeyword": null,
|
||||
"expressions": {
|
||||
"ExpressionList": {
|
||||
"children": [
|
||||
{
|
||||
"error": "MissingToken",
|
||||
"kind": "Expression",
|
||||
"textLength": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": {
|
||||
"error": "MissingToken",
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"EchoExpression": {
|
||||
"echoKeyword": {
|
||||
"kind": "EchoKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"expressions": {
|
||||
"ExpressionList": {
|
||||
"children": [
|
||||
{
|
||||
"StringLiteral": {
|
||||
"startQuote": null,
|
||||
"children": {
|
||||
"kind": "StringLiteralToken",
|
||||
"textLength": 7
|
||||
},
|
||||
"endQuote": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": {
|
||||
"kind": "ScriptSectionEndTag",
|
||||
"textLength": 2
|
||||
},
|
||||
"text": null,
|
||||
"scriptSectionStartTag": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
// FIXME the tolerant-php-parser has a bug, this code always echoes if executed.
|
||||
// (i.e. same as `if (false); echo "hello world"` with an implicit semicolon)
|
||||
// NOTE: If the inline HTML is surrounded by brackets, then this would never echo.
|
||||
if (false)?>hello world<?php
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"IfStatementNode": {
|
||||
"ifKeyword": {
|
||||
"kind": "IfKeyword",
|
||||
"textLength": 2
|
||||
},
|
||||
"openParen": {
|
||||
"kind": "OpenParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"expression": {
|
||||
"ReservedWord": {
|
||||
"children": {
|
||||
"kind": "FalseReservedWord",
|
||||
"textLength": 5
|
||||
}
|
||||
}
|
||||
},
|
||||
"closeParen": {
|
||||
"kind": "CloseParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"colon": null,
|
||||
"statements": {
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": {
|
||||
"kind": "ScriptSectionEndTag",
|
||||
"textLength": 2
|
||||
},
|
||||
"text": {
|
||||
"kind": "InlineHtml",
|
||||
"textLength": 11
|
||||
},
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
"elseIfClauses": [],
|
||||
"elseClause": null,
|
||||
"endifKeyword": null,
|
||||
"semicolon": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<?= "expression" // properly warns about missing ';'
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "';' expected.",
|
||||
"start": 16,
|
||||
"length": 0
|
||||
}
|
||||
]
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartWithEchoTag",
|
||||
"textLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"EchoExpression": {
|
||||
"echoKeyword": null,
|
||||
"expressions": {
|
||||
"ExpressionList": {
|
||||
"children": [
|
||||
{
|
||||
"StringLiteral": {
|
||||
"startQuote": null,
|
||||
"children": {
|
||||
"kind": "StringLiteralToken",
|
||||
"textLength": 12
|
||||
},
|
||||
"endQuote": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": {
|
||||
"error": "MissingToken",
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<?php echo $a ?>
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"SourceFileNode": {
|
||||
"statementList": [
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"textLength": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"EchoExpression": {
|
||||
"echoKeyword": {
|
||||
"kind": "EchoKeyword",
|
||||
"textLength": 4
|
||||
},
|
||||
"expressions": {
|
||||
"ExpressionList": {
|
||||
"children": [
|
||||
{
|
||||
"Variable": {
|
||||
"dollar": null,
|
||||
"name": {
|
||||
"kind": "VariableName",
|
||||
"textLength": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"InlineHtml": {
|
||||
"scriptSectionEndTag": {
|
||||
"kind": "ScriptSectionEndTag",
|
||||
"textLength": 3
|
||||
},
|
||||
"text": null,
|
||||
"scriptSectionStartTag": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": {
|
||||
"kind": "EndOfFileToken",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1,8 @@
|
|||
[]
|
||||
[
|
||||
{
|
||||
"kind": 0,
|
||||
"message": "'Expression' expected.",
|
||||
"start": 3,
|
||||
"length": 0
|
||||
}
|
||||
]
|
|
@ -6,13 +6,23 @@
|
|||
"scriptSectionEndTag": null,
|
||||
"text": null,
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"kind": "ScriptSectionStartWithEchoTag",
|
||||
"textLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"EmptyStatement": {
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"EchoExpression": {
|
||||
"echoKeyword": null,
|
||||
"expressions": {
|
||||
"error": "MissingToken",
|
||||
"kind": "Expression",
|
||||
"textLength": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"semicolon": {
|
||||
"kind": "SemicolonToken",
|
||||
"textLength": 1
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"textLength": 4
|
||||
},
|
||||
"scriptSectionStartTag": {
|
||||
"kind": "ScriptSectionStartTag",
|
||||
"kind": "ScriptSectionStartWithEchoTag",
|
||||
"textLength": 3
|
||||
}
|
||||
}
|
||||
|
@ -17,44 +17,55 @@
|
|||
{
|
||||
"ExpressionStatement": {
|
||||
"expression": {
|
||||
"AssignmentExpression": {
|
||||
"leftOperand": {
|
||||
"Variable": {
|
||||
"dollar": null,
|
||||
"name": {
|
||||
"kind": "VariableName",
|
||||
"textLength": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
"operator": {
|
||||
"kind": "EqualsToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"byRef": null,
|
||||
"rightOperand": {
|
||||
"CallExpression": {
|
||||
"callableExpression": {
|
||||
"QualifiedName": {
|
||||
"globalSpecifier": null,
|
||||
"relativeSpecifier": null,
|
||||
"nameParts": [
|
||||
{
|
||||
"kind": "Name",
|
||||
"EchoExpression": {
|
||||
"echoKeyword": null,
|
||||
"expressions": {
|
||||
"ExpressionList": {
|
||||
"children": [
|
||||
{
|
||||
"AssignmentExpression": {
|
||||
"leftOperand": {
|
||||
"Variable": {
|
||||
"dollar": null,
|
||||
"name": {
|
||||
"kind": "VariableName",
|
||||
"textLength": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
"operator": {
|
||||
"kind": "EqualsToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"byRef": null,
|
||||
"rightOperand": {
|
||||
"CallExpression": {
|
||||
"callableExpression": {
|
||||
"QualifiedName": {
|
||||
"globalSpecifier": null,
|
||||
"relativeSpecifier": null,
|
||||
"nameParts": [
|
||||
{
|
||||
"kind": "Name",
|
||||
"textLength": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"openParen": {
|
||||
"kind": "OpenParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"argumentExpressionList": null,
|
||||
"closeParen": {
|
||||
"kind": "CloseParenToken",
|
||||
"textLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"openParen": {
|
||||
"kind": "OpenParenToken",
|
||||
"textLength": 1
|
||||
},
|
||||
"argumentExpressionList": null,
|
||||
"closeParen": {
|
||||
"kind": "CloseParenToken",
|
||||
"textLength": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче