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