diff --git a/src/Ast/ConstExpr/ConstExprNewNode.php b/src/Ast/ConstExpr/ConstExprNewNode.php new file mode 100644 index 00000000..50ac064d --- /dev/null +++ b/src/Ast/ConstExpr/ConstExprNewNode.php @@ -0,0 +1,34 @@ +class = $class; + $this->arguments = $arguments; + } + + + public function __toString(): string + { + return 'new ' . $this->class . '(' . implode(', ', $this->arguments) . ')'; + } + +} diff --git a/src/Parser/ConstExprParser.php b/src/Parser/ConstExprParser.php index f6a7306e..091ea791 100644 --- a/src/Parser/ConstExprParser.php +++ b/src/Parser/ConstExprParser.php @@ -183,6 +183,8 @@ public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\Con case 'array': $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES, $startIndex); + case 'new': + return $this->parseNew($tokens, $startIndex); } if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { @@ -269,6 +271,30 @@ private function parseArray(TokenIterator $tokens, int $endToken, int $startInde } + private function parseNew(TokenIterator $tokens, int $startIndex): Ast\ConstExpr\ConstExprNewNode + { + $startLine = $tokens->currentTokenLine(); + + $class = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + $arguments = []; + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + do { + $arguments[] = $this->parse($tokens); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + } + + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprNewNode($class, $arguments), + $startLine, + $startIndex + ); + } + + /** * This method is supposed to be called with TokenIterator after reading TOKEN_DOUBLE_QUOTED_STRING and shifting * to the next token. diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index 5669fe45..536bd1cf 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -221,7 +221,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode try { $constExpr = $this->constExprParser->parse($tokens, true); - if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { + if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode || $constExpr instanceof Ast\ConstExpr\ConstExprNewNode) { throw new ParserException( $currentTokenValue, $currentTokenType, @@ -732,7 +732,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo try { $constExpr = $this->constExprParser->parse($tokens, true); - if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { + if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode || $constExpr instanceof Ast\ConstExpr\ConstExprNewNode) { throw new ParserException( $currentTokenValue, $currentTokenType, diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php index 044d07f8..2547f2af 100644 --- a/src/Printer/Printer.php +++ b/src/Printer/Printer.php @@ -5,6 +5,7 @@ use LogicException; use PHPStan\PhpDocParser\Ast\Attribute; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNewNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode; use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagMethodValueNode; @@ -105,6 +106,7 @@ final class Printer CallableTypeNode::class . '->templateTypes' => ', ', GenericTypeNode::class . '->genericTypes' => ', ', ConstExprArrayNode::class . '->items' => ', ', + ConstExprNewNode::class . '->arguments' => ', ', MethodTagValueNode::class . '->parameters' => ', ', DoctrineArray::class . '->items' => ', ', DoctrineAnnotation::class . '->arguments' => ', ', diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 8085ec64..7c0799b5 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -8,6 +8,7 @@ use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayItemNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNewNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\ConstExpr\DoctrineConstExprStringNode; @@ -2762,6 +2763,66 @@ public function provideMethodTagsData(): Iterator ), ]), ]; + + yield [ + 'OK static with parameter using new in initializer', + '/** @method static void myFunction(DateInterval $date = new DateInterval("P1Y")) */', + new PhpDocNode([ + new PhpDocTagNode( + '@method', + new MethodTagValueNode( + true, + new IdentifierTypeNode('void'), + 'myFunction', + [ + new MethodTagValueParameterNode( + new IdentifierTypeNode('DateInterval'), + false, + false, + '$date', + new ConstExprNewNode( + 'DateInterval', + [ + new ConstExprStringNode('"P1Y"'), + ] + ) + ), + ], + '' + ) + ), + ]), + ]; + + yield [ + 'OK static with parameter using new in initializer with nested new', + '/** @method static void myFunction(SomeClass $object = new SomeClass(new SomeClass)) */', + new PhpDocNode([ + new PhpDocTagNode( + '@method', + new MethodTagValueNode( + true, + new IdentifierTypeNode('void'), + 'myFunction', + [ + new MethodTagValueParameterNode( + new IdentifierTypeNode('SomeClass'), + false, + false, + '$object', + new ConstExprNewNode( + 'SomeClass', + [ + new ConstExprNewNode('SomeClass', []), + ] + ) + ), + ], + '' + ) + ), + ]), + ]; } diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php index d73481e2..cf9f0699 100644 --- a/tests/PHPStan/Printer/PrinterTest.php +++ b/tests/PHPStan/Printer/PrinterTest.php @@ -7,6 +7,7 @@ use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayItemNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNewNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\ConstExpr\QuoteAwareConstExprStringNode; use PHPStan\PhpDocParser\Ast\Node; @@ -1740,6 +1741,31 @@ public function enterNode(Node $node) }, ]; + + $addItemsToConstExprNewArguments = new class extends AbstractNodeVisitor { + + public function enterNode(Node $node) + { + if ($node instanceof ConstExprNewNode) { + $node->arguments[] = new ConstExprIntegerNode('123'); + } + + return $node; + } + + }; + + yield [ + '/** @method int doFoo(Foo $foo = new Foo) */', + '/** @method int doFoo(Foo $foo = new Foo(123)) */', + $addItemsToConstExprNewArguments, + ]; + + yield [ + '/** @method int doFoo(Foo $foo = new Foo(420)) */', + '/** @method int doFoo(Foo $foo = new Foo(420, 123)) */', + $addItemsToConstExprNewArguments, + ]; } /**