Skip to content

Commit edfe931

Browse files
committed
Allow multiple new lines in union and intersection definition
1 parent 19ecfcb commit edfe931

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed

Diff for: src/Parser/TypeParser.php

+16
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,14 @@ private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast
271271

272272
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
273273
$types[] = $this->parseAtomic($tokens);
274+
$tokens->pushSavePoint();
275+
$tokens->skipNewLineTokens();
276+
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
277+
$tokens->rollback();
278+
break;
279+
}
280+
281+
$tokens->dropSavePoint();
274282
}
275283

276284
return new Ast\Type\UnionTypeNode($types);
@@ -299,6 +307,14 @@ private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $typ
299307

300308
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
301309
$types[] = $this->parseAtomic($tokens);
310+
$tokens->pushSavePoint();
311+
$tokens->skipNewLineTokens();
312+
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
313+
$tokens->rollback();
314+
break;
315+
}
316+
317+
$tokens->dropSavePoint();
302318
}
303319

304320
return new Ast\Type\IntersectionTypeNode($types);

Diff for: tests/PHPStan/Parser/PhpDocParserTest.php

+171
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
6161
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
6262
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
63+
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
6364
use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
6465
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
6566
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode;
@@ -4211,6 +4212,176 @@ public function provideMultiLinePhpDocData(): iterable
42114212
]),
42124213
];
42134214

4215+
yield [
4216+
'Multiline PHPDoc with multiple new line within union type declaration',
4217+
'/**' . PHP_EOL .
4218+
' * @param array<string, array{' . PHP_EOL .
4219+
' * foo: int,' . PHP_EOL .
4220+
' * bar?: array<string,' . PHP_EOL .
4221+
' * array{foo1: int, bar1?: true}' . PHP_EOL .
4222+
' * | array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
4223+
' * | array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
4224+
' * | array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
4225+
' * >,' . PHP_EOL .
4226+
' * }> $a' . PHP_EOL .
4227+
' */',
4228+
new PhpDocNode([
4229+
new PhpDocTagNode('@param', new ParamTagValueNode(
4230+
new GenericTypeNode(
4231+
new IdentifierTypeNode('array'),
4232+
[
4233+
new IdentifierTypeNode('string'),
4234+
ArrayShapeNode::createSealed([
4235+
new ArrayShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
4236+
new ArrayShapeItemNode(new IdentifierTypeNode('bar'), true, new GenericTypeNode(
4237+
new IdentifierTypeNode('array'),
4238+
[
4239+
new IdentifierTypeNode('string'),
4240+
new UnionTypeNode([
4241+
ArrayShapeNode::createSealed([
4242+
new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
4243+
new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
4244+
]),
4245+
ArrayShapeNode::createSealed([
4246+
new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
4247+
new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('true')),
4248+
new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
4249+
]),
4250+
ArrayShapeNode::createSealed([
4251+
new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('int')),
4252+
new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('bool')),
4253+
new ArrayShapeItemNode(new IdentifierTypeNode('bar2'), true, new IdentifierTypeNode('true')),
4254+
]),
4255+
ArrayShapeNode::createSealed([
4256+
new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
4257+
new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('true')),
4258+
new ArrayShapeItemNode(new IdentifierTypeNode('bar3'), true, new IdentifierTypeNode('false')),
4259+
]),
4260+
]),
4261+
],
4262+
[
4263+
GenericTypeNode::VARIANCE_INVARIANT,
4264+
GenericTypeNode::VARIANCE_INVARIANT,
4265+
],
4266+
)),
4267+
]),
4268+
],
4269+
[
4270+
GenericTypeNode::VARIANCE_INVARIANT,
4271+
GenericTypeNode::VARIANCE_INVARIANT,
4272+
],
4273+
),
4274+
false,
4275+
'$a',
4276+
'',
4277+
false,
4278+
)),
4279+
]),
4280+
];
4281+
4282+
yield [
4283+
'Multiline PHPDoc with multiple new line within intersection type declaration',
4284+
'/**' . PHP_EOL .
4285+
' * @param array<string, array{' . PHP_EOL .
4286+
' * foo: int,' . PHP_EOL .
4287+
' * bar?: array<string,' . PHP_EOL .
4288+
' * array{foo1: int, bar1?: true}' . PHP_EOL .
4289+
' * & array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
4290+
' * & array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
4291+
' * & array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
4292+
' * >,' . PHP_EOL .
4293+
' * }> $a' . PHP_EOL .
4294+
' */',
4295+
new PhpDocNode([
4296+
new PhpDocTagNode('@param', new ParamTagValueNode(
4297+
new GenericTypeNode(
4298+
new IdentifierTypeNode('array'),
4299+
[
4300+
new IdentifierTypeNode('string'),
4301+
ArrayShapeNode::createSealed([
4302+
new ArrayShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
4303+
new ArrayShapeItemNode(new IdentifierTypeNode('bar'), true, new GenericTypeNode(
4304+
new IdentifierTypeNode('array'),
4305+
[
4306+
new IdentifierTypeNode('string'),
4307+
new IntersectionTypeNode([
4308+
ArrayShapeNode::createSealed([
4309+
new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
4310+
new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
4311+
]),
4312+
ArrayShapeNode::createSealed([
4313+
new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
4314+
new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('true')),
4315+
new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
4316+
]),
4317+
ArrayShapeNode::createSealed([
4318+
new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('int')),
4319+
new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('bool')),
4320+
new ArrayShapeItemNode(new IdentifierTypeNode('bar2'), true, new IdentifierTypeNode('true')),
4321+
]),
4322+
ArrayShapeNode::createSealed([
4323+
new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
4324+
new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('true')),
4325+
new ArrayShapeItemNode(new IdentifierTypeNode('bar3'), true, new IdentifierTypeNode('false')),
4326+
]),
4327+
]),
4328+
],
4329+
[
4330+
GenericTypeNode::VARIANCE_INVARIANT,
4331+
GenericTypeNode::VARIANCE_INVARIANT,
4332+
],
4333+
)),
4334+
]),
4335+
],
4336+
[
4337+
GenericTypeNode::VARIANCE_INVARIANT,
4338+
GenericTypeNode::VARIANCE_INVARIANT,
4339+
],
4340+
),
4341+
false,
4342+
'$a',
4343+
'',
4344+
false,
4345+
)),
4346+
]),
4347+
];
4348+
4349+
yield [
4350+
'Multiline PHPDoc with multiple new line being invalid due to union and intersection type declaration',
4351+
'/**' . PHP_EOL .
4352+
' * @param array<string, array{' . PHP_EOL .
4353+
' * foo: int,' . PHP_EOL .
4354+
' * bar?: array<string,' . PHP_EOL .
4355+
' * array{foo1: int, bar1?: true}' . PHP_EOL .
4356+
' * & array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
4357+
' * | array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
4358+
' * & array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
4359+
' * >,' . PHP_EOL .
4360+
' * }> $a' . PHP_EOL .
4361+
' */',
4362+
new PhpDocNode([
4363+
new PhpDocTagNode('@param', new InvalidTagValueNode(
4364+
'array<string, array{' . PHP_EOL .
4365+
' foo: int,' . PHP_EOL .
4366+
' bar?: array<string,' . PHP_EOL .
4367+
' array{foo1: int, bar1?: true}' . PHP_EOL .
4368+
' & array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
4369+
' | array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
4370+
' & array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
4371+
' >,' . PHP_EOL .
4372+
'}> $a',
4373+
new ParserException(
4374+
'?',
4375+
Lexer::TOKEN_NULLABLE,
4376+
62,
4377+
Lexer::TOKEN_CLOSE_CURLY_BRACKET,
4378+
null,
4379+
4,
4380+
),
4381+
)),
4382+
]),
4383+
];
4384+
42144385
/**
42154386
* @return object{
42164387
* a: int,

0 commit comments

Comments
 (0)