Skip to content

Commit

Permalink
Add basic support for new-in-initializer
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanvelzen committed May 7, 2024
1 parent 536889f commit cb43a7f
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 2 deletions.
34 changes: 34 additions & 0 deletions src/Ast/ConstExpr/ConstExprNewNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php declare(strict_types = 1);

namespace PHPStan\PhpDocParser\Ast\ConstExpr;

use PHPStan\PhpDocParser\Ast\NodeAttributes;
use function implode;

class ConstExprNewNode implements ConstExprNode
{

use NodeAttributes;

/** @var string */
public $class;

/** @var ConstExprNode[] */
public $arguments;

/**
* @param ConstExprNode[] $arguments
*/
public function __construct(string $class, array $arguments)
{
$this->class = $class;
$this->arguments = $arguments;
}


public function __toString(): string
{
return 'new ' . $this->class . '(' . implode(', ', $this->arguments) . ')';
}

}
26 changes: 26 additions & 0 deletions src/Parser/ConstExprParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions src/Parser/TypeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/Printer/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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' => ', ',
Expand Down
61 changes: 61 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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', []),
]
)
),
],
''
)
),
]),
];
}


Expand Down
26 changes: 26 additions & 0 deletions tests/PHPStan/Printer/PrinterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
];
}

/**
Expand Down

0 comments on commit cb43a7f

Please sign in to comment.