From 2894799bb4545f10339b1d88a8d50ac6db4d72bb Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 7 Sep 2024 14:44:03 +0200
Subject: [PATCH 01/45] Open 2.0.x

---
 .github/workflows/backward-compatibility.yml      |  2 +-
 .github/workflows/build.yml                       |  2 +-
 .../workflows/test-slevomat-coding-standard.yml   |  2 +-
 composer.json                                     | 12 ++++++------
 .../IntegrationPrinterWithPhpParserTest.php       | 15 ++++-----------
 5 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/.github/workflows/backward-compatibility.yml b/.github/workflows/backward-compatibility.yml
index ba1e3130..49af305c 100644
--- a/.github/workflows/backward-compatibility.yml
+++ b/.github/workflows/backward-compatibility.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "1.23.x"
+      - "2.0.x"
 
 jobs:
   backward-compatibility:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 01b49f52..712ee4bf 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "1.23.x"
+      - "2.0.x"
 
 jobs:
   lint:
diff --git a/.github/workflows/test-slevomat-coding-standard.yml b/.github/workflows/test-slevomat-coding-standard.yml
index 238fed25..389b4581 100644
--- a/.github/workflows/test-slevomat-coding-standard.yml
+++ b/.github/workflows/test-slevomat-coding-standard.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "1.23.x"
+      - "2.0.x"
 
 jobs:
   tests:
diff --git a/composer.json b/composer.json
index f1c648e5..bf3e95ec 100644
--- a/composer.json
+++ b/composer.json
@@ -3,17 +3,17 @@
 	"description": "PHPDoc parser with support for nullable, intersection and generic types",
 	"license": "MIT",
 	"require": {
-		"php": "^7.2 || ^8.0"
+		"php": "^7.4 || ^8.0"
 	},
 	"require-dev": {
 		"doctrine/annotations": "^2.0",
-		"nikic/php-parser": "^4.15",
+		"nikic/php-parser": "^5.1",
 		"php-parallel-lint/php-parallel-lint": "^1.2",
 		"phpstan/extension-installer": "^1.0",
-		"phpstan/phpstan": "^1.5",
-		"phpstan/phpstan-phpunit": "^1.1",
-		"phpstan/phpstan-strict-rules": "^1.0",
-		"phpunit/phpunit": "^9.5",
+		"phpstan/phpstan": "^2.0",
+		"phpstan/phpstan-phpunit": "^2.0",
+		"phpstan/phpstan-strict-rules": "^2.0",
+		"phpunit/phpunit": "^9.6",
 		"symfony/process": "^5.2"
 	},
 	"config": {
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index 74d05072..dcc0e3f8 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -3,12 +3,11 @@
 namespace PHPStan\PhpDocParser\Printer;
 
 use PhpParser\Comment\Doc;
-use PhpParser\Lexer\Emulative;
 use PhpParser\Node as PhpNode;
 use PhpParser\NodeTraverser as PhpParserNodeTraverser;
 use PhpParser\NodeVisitor\CloningVisitor as PhpParserCloningVisitor;
 use PhpParser\NodeVisitorAbstract;
-use PhpParser\Parser\Php7;
+use PhpParser\ParserFactory;
 use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
 use PHPStan\PhpDocParser\Ast\Node;
 use PHPStan\PhpDocParser\Ast\NodeTraverser;
@@ -66,14 +65,8 @@ public function enterNode(Node $node)
 	 */
 	public function testPrint(string $file, string $expectedFile, NodeVisitor $visitor): void
 	{
-		$lexer = new Emulative([
-			'usedAttributes' => [
-				'comments',
-				'startLine', 'endLine',
-				'startTokenPos', 'endTokenPos',
-			],
-		]);
-		$phpParser = new Php7($lexer);
+		$phpParserFactory = new ParserFactory();
+		$phpParser = $phpParserFactory->createForNewestSupportedVersion();
 		$phpTraverser = new PhpParserNodeTraverser();
 		$phpTraverser->addVisitor(new PhpParserCloningVisitor());
 
@@ -85,7 +78,7 @@ public function testPrint(string $file, string $expectedFile, NodeVisitor $visit
 
 		/** @var PhpNode[] $oldStmts */
 		$oldStmts = $phpParser->parse($fileContents);
-		$oldTokens = $lexer->getTokens();
+		$oldTokens = $phpParser->getTokens();
 
 		$phpTraverser2 = new PhpParserNodeTraverser();
 		$phpTraverser2->addVisitor(new class ($visitor) extends NodeVisitorAbstract {

From 87b7f7419add3feda8a8b6ea65d54b22cbb78e02 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 7 Sep 2024 14:47:05 +0200
Subject: [PATCH 02/45] Stop testing PHP 7.2 and 7.3

---
 .github/workflows/build.yml | 18 ------------------
 1 file changed, 18 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 712ee4bf..59e2b2ab 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,8 +16,6 @@ jobs:
     strategy:
       matrix:
         php-version:
-          - "7.2"
-          - "7.3"
           - "7.4"
           - "8.0"
           - "8.1"
@@ -41,10 +39,6 @@ jobs:
       - name: "Install dependencies"
         run: "composer install --no-interaction --no-progress"
 
-      - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.2' || matrix.php-version == '7.3'
-        run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies"
-
       - name: "Lint"
         run: "make lint"
 
@@ -95,8 +89,6 @@ jobs:
       matrix:
         operating-system: [ubuntu-latest, windows-latest]
         php-version:
-          - "7.2"
-          - "7.3"
           - "7.4"
           - "8.0"
           - "8.1"
@@ -125,10 +117,6 @@ jobs:
         if: ${{ matrix.dependencies == 'highest' }}
         run: "composer update --no-interaction --no-progress"
 
-      - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.2' || matrix.php-version == '7.3'
-        run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies"
-
       - name: "Tests"
         run: "make tests"
 
@@ -140,8 +128,6 @@ jobs:
       fail-fast: false
       matrix:
         php-version:
-          - "7.2"
-          - "7.3"
           - "7.4"
           - "8.0"
           - "8.1"
@@ -164,9 +150,5 @@ jobs:
       - name: "Install dependencies"
         run: "composer update --no-interaction --no-progress"
 
-      - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.2' || matrix.php-version == '7.3'
-        run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies"
-
       - name: "PHPStan"
         run: "make phpstan"

From a165178868756c076897078ab7fee3205236992b Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 1 Jun 2023 14:17:31 +0200
Subject: [PATCH 03/45] Parsing Doctrine annotations is no longer optional

---
 src/Lexer/Lexer.php                        | 21 ++-----
 src/Parser/PhpDocParser.php                | 72 ++++++++--------------
 tests/PHPStan/Parser/PhpDocParserTest.php  | 12 ++--
 tests/PHPStan/Parser/TokenIteratorTest.php |  2 +-
 tests/PHPStan/Printer/PrinterTest.php      |  5 +-
 5 files changed, 40 insertions(+), 72 deletions(-)

diff --git a/src/Lexer/Lexer.php b/src/Lexer/Lexer.php
index 32539faf..0b5f9ed5 100644
--- a/src/Lexer/Lexer.php
+++ b/src/Lexer/Lexer.php
@@ -94,17 +94,9 @@ class Lexer
 	public const TYPE_OFFSET = 1;
 	public const LINE_OFFSET = 2;
 
-	/** @var bool */
-	private $parseDoctrineAnnotations;
-
 	/** @var string|null */
 	private $regexp;
 
-	public function __construct(bool $parseDoctrineAnnotations = false)
-	{
-		$this->parseDoctrineAnnotations = $parseDoctrineAnnotations;
-	}
-
 	/**
 	 * @return list<array{string, int, int}>
 	 */
@@ -170,23 +162,20 @@ private function generateRegexp(): string
 			self::TOKEN_OPEN_PHPDOC => '/\\*\\*(?=\\s)\\x20?+',
 			self::TOKEN_CLOSE_PHPDOC => '\\*/',
 			self::TOKEN_PHPDOC_TAG => '@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+',
+			self::TOKEN_DOCTRINE_TAG => '@[a-z_\\\\][a-z0-9_\:\\\\]*[a-z_][a-z0-9_]*',
 			self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/)\\x20?+)?',
 
 			self::TOKEN_FLOAT => '[+\-]?(?:(?:[0-9]++(_[0-9]++)*\\.[0-9]*+(_[0-9]++)*(?:e[+\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e[+\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]++(_[0-9]++)*e[+\-]?[0-9]++(_[0-9]++)*))',
 			self::TOKEN_INTEGER => '[+\-]?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))',
 			self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'',
 			self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"',
+			self::TOKEN_DOCTRINE_ANNOTATION_STRING => '"(?:""|[^"])*+"',
 
 			self::TOKEN_WILDCARD => '\\*',
-		];
-
-		if ($this->parseDoctrineAnnotations) {
-			$patterns[self::TOKEN_DOCTRINE_TAG] = '@[a-z_\\\\][a-z0-9_\:\\\\]*[a-z_][a-z0-9_]*';
-			$patterns[self::TOKEN_DOCTRINE_ANNOTATION_STRING] = '"(?:""|[^"])*+"';
-		}
 
-		// anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL
-		$patterns[self::TOKEN_OTHER] = '(?:(?!\\*/)[^\\s])++';
+			// anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL
+			self::TOKEN_OTHER => '(?:(?!\\*/)[^\\s])++',
+		];
 
 		foreach ($patterns as $type => &$pattern) {
 			$pattern = '(?:' . $pattern . ')(*MARK:' . $type . ')';
diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index e46e0941..8a97cd98 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -44,9 +44,6 @@ class PhpDocParser
 	/** @var bool */
 	private $preserveTypeAliasesWithInvalidTypes;
 
-	/** @var bool */
-	private $parseDoctrineAnnotations;
-
 	/** @var bool */
 	private $useLinesAttributes;
 
@@ -65,7 +62,6 @@ public function __construct(
 		bool $requireWhitespaceBeforeDescription = false,
 		bool $preserveTypeAliasesWithInvalidTypes = false,
 		array $usedAttributes = [],
-		bool $parseDoctrineAnnotations = false,
 		bool $textBetweenTagsBelongsToDescription = false
 	)
 	{
@@ -74,7 +70,6 @@ public function __construct(
 		$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
 		$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
 		$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
-		$this->parseDoctrineAnnotations = $parseDoctrineAnnotations;
 		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
 		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
 		$this->textBetweenTagsBelongsToDescription = $textBetweenTagsBelongsToDescription;
@@ -88,44 +83,35 @@ public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
 
 		$children = [];
 
-		if ($this->parseDoctrineAnnotations) {
-			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
-				$lastChild = $this->parseChild($tokens);
-				$children[] = $lastChild;
-				while (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
-					if (
-						$lastChild instanceof Ast\PhpDoc\PhpDocTagNode
-						&& (
-							$lastChild->value instanceof Doctrine\DoctrineTagValueNode
-							|| $lastChild->value instanceof Ast\PhpDoc\GenericTagValueNode
-						)
-					) {
-						$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
-						if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
-							break;
-						}
-						$lastChild = $this->parseChild($tokens);
-						$children[] = $lastChild;
-						continue;
-					}
-
-					if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
-						break;
-					}
+		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+			$lastChild = $this->parseChild($tokens);
+			$children[] = $lastChild;
+			while (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+				if (
+					$lastChild instanceof Ast\PhpDoc\PhpDocTagNode
+					&& (
+						$lastChild->value instanceof Doctrine\DoctrineTagValueNode
+						|| $lastChild->value instanceof Ast\PhpDoc\GenericTagValueNode
+					)
+				) {
+					$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
 					if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
 						break;
 					}
-
 					$lastChild = $this->parseChild($tokens);
 					$children[] = $lastChild;
+					continue;
 				}
-			}
-		} else {
-			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
-				$children[] = $this->parseChild($tokens);
-				while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
-					$children[] = $this->parseChild($tokens);
+
+				if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+					break;
 				}
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+					break;
+				}
+
+				$lastChild = $this->parseChild($tokens);
+				$children[] = $lastChild;
 			}
 		}
 
@@ -539,17 +525,11 @@ function ($tokens) {
 					break;
 
 				default:
-					if ($this->parseDoctrineAnnotations) {
-						if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
-							$tagValue = $this->parseDoctrineTagValue($tokens, $tag);
-						} else {
-							$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescriptionAfterDoctrineTag($tokens));
-						}
-						break;
+					if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+						$tagValue = $this->parseDoctrineTagValue($tokens, $tag);
+					} else {
+						$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescriptionAfterDoctrineTag($tokens));
 					}
-
-					$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
-
 					break;
 			}
 
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 8085ec64..b512efae 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -87,12 +87,12 @@ class PhpDocParserTest extends TestCase
 	protected function setUp(): void
 	{
 		parent::setUp();
-		$this->lexer = new Lexer(true);
+		$this->lexer = new Lexer();
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
-		$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser, false, false, [], true);
-		$this->phpDocParserWithRequiredWhitespaceBeforeDescription = new PhpDocParser($typeParser, $constExprParser, true, false, [], true);
-		$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes = new PhpDocParser($typeParser, $constExprParser, true, true, [], true);
+		$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser, false, false, []);
+		$this->phpDocParserWithRequiredWhitespaceBeforeDescription = new PhpDocParser($typeParser, $constExprParser, true, false, []);
+		$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes = new PhpDocParser($typeParser, $constExprParser, true, true, []);
 	}
 
 
@@ -7143,7 +7143,7 @@ public function testDeepNodesLinesAndIndexes(string $phpDoc, array $nodeAttribut
 		];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes, true);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
 		$visitor = new NodeCollectingVisitor();
 		$traverser = new NodeTraverser([$visitor]);
 		$traverser->traverse([$phpDocParser->parse($tokens)]);
@@ -7559,7 +7559,7 @@ public function testTextBetweenTagsBelongsToDescription(
 	{
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, [], true, true);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, [], true, true);
 
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 		$actualPhpDocNode = $phpDocParser->parse($tokens);
diff --git a/tests/PHPStan/Parser/TokenIteratorTest.php b/tests/PHPStan/Parser/TokenIteratorTest.php
index 6f526029..8f2b4c9d 100644
--- a/tests/PHPStan/Parser/TokenIteratorTest.php
+++ b/tests/PHPStan/Parser/TokenIteratorTest.php
@@ -46,7 +46,7 @@ public function dataGetDetectedNewline(): iterable
 	 */
 	public function testGetDetectedNewline(string $phpDoc, ?string $expectedNewline): void
 	{
-		$lexer = new Lexer(true);
+		$lexer = new Lexer();
 		$tokens = new TokenIterator($lexer->tokenize($phpDoc));
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 24a28236..b17c1204 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -75,8 +75,7 @@ protected function setUp(): void
 			$constExprParser,
 			true,
 			true,
-			$usedAttributes,
-			true
+			$usedAttributes
 		);
 	}
 
@@ -1871,7 +1870,7 @@ public function enterNode(Node $node)
 	 */
 	public function testPrintFormatPreserving(string $phpDoc, string $expectedResult, NodeVisitor $visitor): void
 	{
-		$lexer = new Lexer(true);
+		$lexer = new Lexer();
 		$tokens = new TokenIterator($lexer->tokenize($phpDoc));
 		$phpDocNode = $this->phpDocParser->parse($tokens);
 		$cloningTraverser = new NodeTraverser([new NodeVisitor\CloningVisitor()]);

From 8d3e90fe05ae2be0cad99566d4910f6f4a4ad49f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 1 Jun 2023 17:14:58 +0200
Subject: [PATCH 04/45] PhpDocParser - always require whitespace before
 description

---
 src/Parser/PhpDocParser.php                   |   8 +-
 tests/PHPStan/Parser/PhpDocParserTest.php     | 115 ++----------------
 .../IntegrationPrinterWithPhpParserTest.php   |   1 -
 tests/PHPStan/Printer/PrinterTest.php         |   1 -
 4 files changed, 9 insertions(+), 116 deletions(-)

diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index 8a97cd98..0bdba979 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -38,9 +38,6 @@ class PhpDocParser
 	/** @var ConstExprParser */
 	private $doctrineConstantExprParser;
 
-	/** @var bool */
-	private $requireWhitespaceBeforeDescription;
-
 	/** @var bool */
 	private $preserveTypeAliasesWithInvalidTypes;
 
@@ -59,7 +56,6 @@ class PhpDocParser
 	public function __construct(
 		TypeParser $typeParser,
 		ConstExprParser $constantExprParser,
-		bool $requireWhitespaceBeforeDescription = false,
 		bool $preserveTypeAliasesWithInvalidTypes = false,
 		array $usedAttributes = [],
 		bool $textBetweenTagsBelongsToDescription = false
@@ -68,7 +64,6 @@ public function __construct(
 		$this->typeParser = $typeParser;
 		$this->constantExprParser = $constantExprParser;
 		$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
-		$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
 		$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
 		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
 		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
@@ -1255,8 +1250,7 @@ private function parseOptionalDescription(TokenIterator $tokens, bool $limitStar
 			}
 
 			if (
-				$this->requireWhitespaceBeforeDescription
-				&& !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END)
+				!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END)
 				&& !$tokens->isPrecededByHorizontalWhitespace()
 			) {
 				$tokens->consumeTokenType(Lexer::TOKEN_HORIZONTAL_WS); // will throw exception
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index b512efae..f8fbb34a 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -78,9 +78,6 @@ class PhpDocParserTest extends TestCase
 	/** @var PhpDocParser */
 	private $phpDocParser;
 
-	/** @var PhpDocParser */
-	private $phpDocParserWithRequiredWhitespaceBeforeDescription;
-
 	/** @var PhpDocParser */
 	private $phpDocParserWithPreserveTypeAliasesWithInvalidTypes;
 
@@ -90,9 +87,8 @@ protected function setUp(): void
 		$this->lexer = new Lexer();
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
-		$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser, false, false, []);
-		$this->phpDocParserWithRequiredWhitespaceBeforeDescription = new PhpDocParser($typeParser, $constExprParser, true, false, []);
-		$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes = new PhpDocParser($typeParser, $constExprParser, true, true, []);
+		$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser, false, []);
+		$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes = new PhpDocParser($typeParser, $constExprParser, true, []);
 	}
 
 
@@ -133,7 +129,6 @@ public function testParse(
 		string $label,
 		string $input,
 		PhpDocNode $expectedPhpDocNode,
-		?PhpDocNode $withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode = null,
 		?PhpDocNode $withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode = null
 	): void
 	{
@@ -144,18 +139,11 @@ public function testParse(
 			$expectedPhpDocNode
 		);
 
-		$this->executeTestParse(
-			$this->phpDocParserWithRequiredWhitespaceBeforeDescription,
-			$label,
-			$input,
-			$withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode ?? $expectedPhpDocNode
-		);
-
 		$this->executeTestParse(
 			$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes,
 			$label,
 			$input,
-			$withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode ?? $withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode ?? $expectedPhpDocNode
+			$withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode ?? $expectedPhpDocNode
 		);
 	}
 
@@ -933,16 +921,6 @@ public function provideVarTagsData(): Iterator
 					)
 				),
 			]),
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@var',
-					new VarTagValueNode(
-						new IdentifierTypeNode('Foo'),
-						'$foo',
-						'#desc'
-					)
-				),
-			]),
 		];
 
 		yield [
@@ -1135,7 +1113,6 @@ public function provideVarTagsData(): Iterator
 					)
 				),
 			]),
-			null,
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@psalm-type',
@@ -1807,15 +1784,6 @@ public function provideReturnTagsData(): Iterator
 		yield [
 			'invalid variadic callable',
 			'/** @return \Closure(...int, string): string */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@return',
-					new ReturnTagValueNode(
-						new IdentifierTypeNode('\Closure'),
-						'(...int, string): string'
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@return',
@@ -2911,16 +2879,6 @@ public function provideSingleLinePhpDocData(): Iterator
 		yield [
 			'callable with incomplete signature without return type',
 			'/** @var callable(int) */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@var',
-					new VarTagValueNode(
-						new IdentifierTypeNode('callable'),
-						'',
-						'(int)'
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@var',
@@ -4368,7 +4326,6 @@ public function provideTypeAliasTagsData(): Iterator
 					)
 				),
 			]),
-			null,
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4408,7 +4365,6 @@ public function provideTypeAliasTagsData(): Iterator
 					)
 				),
 			]),
-			null,
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4456,7 +4412,6 @@ public function provideTypeAliasTagsData(): Iterator
 					)
 				),
 			]),
-			null,
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4504,7 +4459,6 @@ public function provideTypeAliasTagsData(): Iterator
 					)
 				),
 			]),
-			null,
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4553,7 +4507,6 @@ public function provideTypeAliasTagsData(): Iterator
 					)
 				),
 			]),
-			null,
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -5663,23 +5616,6 @@ public function provideDescriptionWithOrWithoutHtml(): Iterator
 			'/**' . PHP_EOL .
 			' * @return Foo <strong>Important description' . PHP_EOL .
 			' */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@return',
-					new ReturnTagValueNode(
-						new GenericTypeNode(
-							new IdentifierTypeNode('Foo'),
-							[
-								new IdentifierTypeNode('strong'),
-							],
-							[
-								GenericTypeNode::VARIANCE_INVARIANT,
-							]
-						),
-						'Important description'
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@return',
@@ -5990,7 +5926,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6013,7 +5948,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[$xWithZ],
 		];
 
@@ -6034,7 +5968,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[$xWithZ],
 		];
 
@@ -6055,7 +5988,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[$xWithZ],
 		];
 
@@ -6076,7 +6008,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[$xWithZ],
 		];
 
@@ -6096,7 +6027,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6117,7 +6047,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTextNode('Content'),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6136,7 +6065,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6155,7 +6083,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6197,7 +6124,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[$x],
 		];
 
@@ -6235,7 +6161,6 @@ public function provideDoctrineData(): Iterator
 				),
 			]),
 			null,
-			null,
 			[$x],
 		];
 
@@ -6251,7 +6176,6 @@ public function provideDoctrineData(): Iterator
 				), 'test')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6268,7 +6192,6 @@ public function provideDoctrineData(): Iterator
 				), 'test' . PHP_EOL . 'test2')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6287,7 +6210,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTextNode('test2'),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6311,7 +6233,6 @@ public function provideDoctrineData(): Iterator
 				), '')),
 			]),
 			null,
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6324,7 +6245,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new GenericTagValueNode('test')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6338,7 +6258,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new GenericTagValueNode('test' . PHP_EOL . 'test2')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6354,7 +6273,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTextNode('test2'),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6372,7 +6290,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@Z', new GenericTagValueNode('')),
 			]),
 			null,
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6384,7 +6301,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation('@Z', []), '')),
 			]),
 			null,
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6396,7 +6312,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation('@Z', []), '')),
 			]),
 			null,
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6408,7 +6323,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation('@Z', []), '')),
 			]),
 			null,
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6419,7 +6333,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new GenericTagValueNode('@phpstan-param int $z')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6430,7 +6343,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new GenericTagValueNode('@phpstan-param |int $z')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6441,7 +6353,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation('@X', []), '@phpstan-param int $z')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6452,7 +6363,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation('@X', []), '@phpstan-param |int $z')),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6515,7 +6425,6 @@ public function provideDoctrineData(): Iterator
 				)),
 			]),
 			null,
-			null,
 			[$apiResource],
 		];
 
@@ -6533,7 +6442,6 @@ public function provideDoctrineData(): Iterator
 				)),
 			]),
 			null,
-			null,
 			[$xWithString],
 		];
 
@@ -6551,7 +6459,6 @@ public function provideDoctrineData(): Iterator
 				)),
 			]),
 			null,
-			null,
 			[$xWithString2],
 		];
 
@@ -6569,7 +6476,6 @@ public function provideDoctrineData(): Iterator
 				)),
 			]),
 			null,
-			null,
 			[$xWithString3],
 		];
 
@@ -6587,7 +6493,6 @@ public function provideDoctrineData(): Iterator
 				)),
 			]),
 			null,
-			null,
 			[$xWithString4],
 		];
 	}
@@ -6801,7 +6706,6 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 				))),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6820,7 +6724,6 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 				))),
 			]),
 			null,
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6905,7 +6808,6 @@ public function provideSpecializedTags(): Iterator
 	public function testParseTagValue(string $tag, string $phpDoc, Node $expectedPhpDocNode): void
 	{
 		$this->executeTestParseTagValue($this->phpDocParser, $tag, $phpDoc, $expectedPhpDocNode);
-		$this->executeTestParseTagValue($this->phpDocParserWithRequiredWhitespaceBeforeDescription, $tag, $phpDoc, $expectedPhpDocNode);
 	}
 
 	private function executeTestParseTagValue(PhpDocParser $phpDocParser, string $tag, string $phpDoc, Node $expectedPhpDocNode): void
@@ -7059,7 +6961,7 @@ public function testLinesAndIndexes(string $phpDoc, array $childrenLines): void
 		];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$children = $phpDocNode->children;
 		$this->assertCount(count($childrenLines), $children);
@@ -7143,7 +7045,7 @@ public function testDeepNodesLinesAndIndexes(string $phpDoc, array $nodeAttribut
 		];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
 		$visitor = new NodeCollectingVisitor();
 		$traverser = new NodeTraverser([$visitor]);
 		$traverser->traverse([$phpDocParser->parse($tokens)]);
@@ -7216,7 +7118,7 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
 		];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$returnTag = $phpDocNode->getReturnTagValues()[0];
 		$type = $returnTag->type;
@@ -7263,7 +7165,7 @@ public function testVerifyAttributes(string $label, string $input): void
 		$usedAttributes = ['lines' => true, 'indexes' => true];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 
 		$visitor = new NodeCollectingVisitor();
@@ -7286,7 +7188,6 @@ public function testDoctrine(
 		string $label,
 		string $input,
 		PhpDocNode $expectedPhpDocNode,
-		?PhpDocNode $withRequiredWhitespaceBeforeDescriptionExpectedPhpDocNode = null,
 		?PhpDocNode $withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode = null,
 		array $expectedAnnotations = []
 	): void
@@ -7559,7 +7460,7 @@ public function testTextBetweenTagsBelongsToDescription(
 	{
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, [], true, true);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, [], true, true);
 
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 		$actualPhpDocNode = $phpDocParser->parse($tokens);
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index dcc0e3f8..cc21861f 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -105,7 +105,6 @@ public function enterNode(PhpNode $phpNode)
 					new TypeParser($constExprParser, true, $usedAttributes),
 					$constExprParser,
 					true,
-					true,
 					$usedAttributes
 				);
 				$lexer = new Lexer();
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index b17c1204..6ef45ad7 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -74,7 +74,6 @@ protected function setUp(): void
 			$this->typeParser,
 			$constExprParser,
 			true,
-			true,
 			$usedAttributes
 		);
 	}

From 2deb7fce911de64ee0e65f9479a793d854b4d220 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 1 Jun 2023 17:25:44 +0200
Subject: [PATCH 05/45] PhpDocParser - always preserve type aliases with
 invalid types

---
 src/Parser/PhpDocParser.php                   |  55 +++---
 tests/PHPStan/Parser/PhpDocParserTest.php     | 163 +-----------------
 .../IntegrationPrinterWithPhpParserTest.php   |   1 -
 tests/PHPStan/Printer/PrinterTest.php         |   1 -
 4 files changed, 29 insertions(+), 191 deletions(-)

diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index 0bdba979..cdf31ec9 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -38,9 +38,6 @@ class PhpDocParser
 	/** @var ConstExprParser */
 	private $doctrineConstantExprParser;
 
-	/** @var bool */
-	private $preserveTypeAliasesWithInvalidTypes;
-
 	/** @var bool */
 	private $useLinesAttributes;
 
@@ -56,7 +53,6 @@ class PhpDocParser
 	public function __construct(
 		TypeParser $typeParser,
 		ConstExprParser $constantExprParser,
-		bool $preserveTypeAliasesWithInvalidTypes = false,
 		array $usedAttributes = [],
 		bool $textBetweenTagsBelongsToDescription = false
 	)
@@ -64,7 +60,6 @@ public function __construct(
 		$this->typeParser = $typeParser;
 		$this->constantExprParser = $constantExprParser;
 		$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
-		$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
 		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
 		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
 		$this->textBetweenTagsBelongsToDescription = $textBetweenTagsBelongsToDescription;
@@ -1085,37 +1080,31 @@ private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeA
 		// support phan-type/psalm-type syntax
 		$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
 
-		if ($this->preserveTypeAliasesWithInvalidTypes) {
-			$startLine = $tokens->currentTokenLine();
-			$startIndex = $tokens->currentTokenIndex();
-			try {
-				$type = $this->typeParser->parse($tokens);
-				if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
-					if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
-						throw new ParserException(
-							$tokens->currentTokenValue(),
-							$tokens->currentTokenType(),
-							$tokens->currentTokenOffset(),
-							Lexer::TOKEN_PHPDOC_EOL,
-							null,
-							$tokens->currentTokenLine()
-						);
-					}
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		try {
+			$type = $this->typeParser->parse($tokens);
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+				if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+					throw new ParserException(
+						$tokens->currentTokenValue(),
+						$tokens->currentTokenType(),
+						$tokens->currentTokenOffset(),
+						Lexer::TOKEN_PHPDOC_EOL,
+						null,
+						$tokens->currentTokenLine()
+					);
 				}
-
-				return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
-			} catch (ParserException $e) {
-				$this->parseOptionalDescription($tokens);
-				return new Ast\PhpDoc\TypeAliasTagValueNode(
-					$alias,
-					$this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex)
-				);
 			}
-		}
 
-		$type = $this->typeParser->parse($tokens);
-
-		return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
+			return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
+		} catch (ParserException $e) {
+			$this->parseOptionalDescription($tokens);
+			return new Ast\PhpDoc\TypeAliasTagValueNode(
+				$alias,
+				$this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex)
+			);
+		}
 	}
 
 	private function parseTypeAliasImportTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeAliasImportTagValueNode
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index f8fbb34a..8a19da1b 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -78,17 +78,13 @@ class PhpDocParserTest extends TestCase
 	/** @var PhpDocParser */
 	private $phpDocParser;
 
-	/** @var PhpDocParser */
-	private $phpDocParserWithPreserveTypeAliasesWithInvalidTypes;
-
 	protected function setUp(): void
 	{
 		parent::setUp();
 		$this->lexer = new Lexer();
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
-		$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser, false, []);
-		$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes = new PhpDocParser($typeParser, $constExprParser, true, []);
+		$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser, []);
 	}
 
 
@@ -128,8 +124,7 @@ protected function setUp(): void
 	public function testParse(
 		string $label,
 		string $input,
-		PhpDocNode $expectedPhpDocNode,
-		?PhpDocNode $withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode = null
+		PhpDocNode $expectedPhpDocNode
 	): void
 	{
 		$this->executeTestParse(
@@ -138,13 +133,6 @@ public function testParse(
 			$input,
 			$expectedPhpDocNode
 		);
-
-		$this->executeTestParse(
-			$this->phpDocParserWithPreserveTypeAliasesWithInvalidTypes,
-			$label,
-			$input,
-			$withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode ?? $expectedPhpDocNode
-		);
 	}
 
 
@@ -1097,22 +1085,6 @@ public function provideVarTagsData(): Iterator
 		yield [
 			'invalid object shape',
 			'/** @psalm-type PARTSTRUCTURE_PARAM = objecttt{attribute:string, value?:string} */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@psalm-type',
-					new InvalidTagValueNode(
-						'Unexpected token "{", expected \'*/\' at offset 46 on line 1',
-						new ParserException(
-							'{',
-							Lexer::TOKEN_OPEN_CURLY_BRACKET,
-							46,
-							Lexer::TOKEN_CLOSE_PHPDOC,
-							null,
-							1
-						)
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@psalm-type',
@@ -4310,22 +4282,6 @@ public function provideTypeAliasTagsData(): Iterator
 		yield [
 			'invalid without type',
 			'/** @phpstan-type TypeAlias */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@phpstan-type',
-					new InvalidTagValueNode(
-						'TypeAlias',
-						new ParserException(
-							'*/',
-							Lexer::TOKEN_CLOSE_PHPDOC,
-							28,
-							Lexer::TOKEN_IDENTIFIER,
-							null,
-							1
-						)
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4349,22 +4305,6 @@ public function provideTypeAliasTagsData(): Iterator
 			'/**
 			  * @phpstan-type TypeAlias
 			  */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@phpstan-type',
-					new InvalidTagValueNode(
-						'TypeAlias',
-						new ParserException(
-							"\n\t\t\t  ",
-							Lexer::TOKEN_PHPDOC_EOL,
-							34,
-							Lexer::TOKEN_IDENTIFIER,
-							null,
-							2
-						)
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4389,29 +4329,6 @@ public function provideTypeAliasTagsData(): Iterator
 			  * @phpstan-type TypeAlias
 			  * @mixin T
 			  */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@phpstan-type',
-					new InvalidTagValueNode(
-						'TypeAlias',
-						new ParserException(
-							"\n\t\t\t  * ",
-							Lexer::TOKEN_PHPDOC_EOL,
-							34,
-							Lexer::TOKEN_IDENTIFIER,
-							null,
-							2
-						)
-					)
-				),
-				new PhpDocTagNode(
-					'@mixin',
-					new MixinTagValueNode(
-						new IdentifierTypeNode('T'),
-						''
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4443,22 +4360,6 @@ public function provideTypeAliasTagsData(): Iterator
  * @phpstan-type Foo array{}
  * @phpstan-type InvalidFoo what{}
  */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@phpstan-type',
-					new InvalidTagValueNode(
-						"Unexpected token \"{\", expected '*/' at offset 65 on line 3",
-						new ParserException(
-							'{',
-							Lexer::TOKEN_OPEN_CURLY_BRACKET,
-							65,
-							Lexer::TOKEN_CLOSE_PHPDOC,
-							null,
-							3
-						)
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4491,22 +4392,6 @@ public function provideTypeAliasTagsData(): Iterator
  * @phpstan-type InvalidFoo what{}
  * @phpstan-type Bar array{}
  */',
-			new PhpDocNode([
-				new PhpDocTagNode(
-					'@phpstan-type',
-					new InvalidTagValueNode(
-						"Unexpected token \"{\", expected '*/' at offset 65 on line 3",
-						new ParserException(
-							'{',
-							Lexer::TOKEN_OPEN_CURLY_BRACKET,
-							65,
-							Lexer::TOKEN_CLOSE_PHPDOC,
-							null,
-							3
-						)
-					)
-				),
-			]),
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -5925,7 +5810,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -5947,7 +5831,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[$xWithZ],
 		];
 
@@ -5967,7 +5850,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[$xWithZ],
 		];
 
@@ -5987,7 +5869,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[$xWithZ],
 		];
 
@@ -6007,7 +5888,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[$xWithZ],
 		];
 
@@ -6026,7 +5906,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6046,7 +5925,6 @@ public function provideDoctrineData(): Iterator
 				),
 				new PhpDocTextNode('Content'),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6064,7 +5942,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6082,7 +5959,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6123,7 +5999,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[$x],
 		];
 
@@ -6160,7 +6035,6 @@ public function provideDoctrineData(): Iterator
 					)
 				),
 			]),
-			null,
 			[$x],
 		];
 
@@ -6175,7 +6049,6 @@ public function provideDoctrineData(): Iterator
 					[]
 				), 'test')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6191,7 +6064,6 @@ public function provideDoctrineData(): Iterator
 					[]
 				), 'test' . PHP_EOL . 'test2')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6209,7 +6081,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTextNode(''),
 				new PhpDocTextNode('test2'),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6232,7 +6103,6 @@ public function provideDoctrineData(): Iterator
 					[]
 				), '')),
 			]),
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6244,7 +6114,6 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new GenericTagValueNode('test')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6257,7 +6126,6 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new GenericTagValueNode('test' . PHP_EOL . 'test2')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6272,7 +6140,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTextNode(''),
 				new PhpDocTextNode('test2'),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 		yield [
@@ -6289,7 +6156,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTextNode('test2'),
 				new PhpDocTagNode('@Z', new GenericTagValueNode('')),
 			]),
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6300,7 +6166,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation('@X', []), '')),
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation('@Z', []), '')),
 			]),
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6311,7 +6176,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation('@X', []), 'test')),
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation('@Z', []), '')),
 			]),
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6322,7 +6186,6 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new GenericTagValueNode('test')),
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation('@Z', []), '')),
 			]),
-			null,
 			[new Doctrine\X(), new Doctrine\Z()],
 		];
 
@@ -6332,7 +6195,6 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new GenericTagValueNode('@phpstan-param int $z')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6342,7 +6204,6 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new GenericTagValueNode('@phpstan-param |int $z')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6352,7 +6213,6 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation('@X', []), '@phpstan-param int $z')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6362,7 +6222,6 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation('@X', []), '@phpstan-param |int $z')),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6424,7 +6283,6 @@ public function provideDoctrineData(): Iterator
 					''
 				)),
 			]),
-			null,
 			[$apiResource],
 		];
 
@@ -6441,7 +6299,6 @@ public function provideDoctrineData(): Iterator
 					''
 				)),
 			]),
-			null,
 			[$xWithString],
 		];
 
@@ -6458,7 +6315,6 @@ public function provideDoctrineData(): Iterator
 					''
 				)),
 			]),
-			null,
 			[$xWithString2],
 		];
 
@@ -6475,7 +6331,6 @@ public function provideDoctrineData(): Iterator
 					''
 				)),
 			]),
-			null,
 			[$xWithString3],
 		];
 
@@ -6492,7 +6347,6 @@ public function provideDoctrineData(): Iterator
 					''
 				)),
 			]),
-			null,
 			[$xWithString4],
 		];
 	}
@@ -6705,7 +6559,6 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					1
 				))),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6723,7 +6576,6 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					1
 				))),
 			]),
-			null,
 			[new Doctrine\X()],
 		];
 
@@ -6961,7 +6813,7 @@ public function testLinesAndIndexes(string $phpDoc, array $childrenLines): void
 		];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$children = $phpDocNode->children;
 		$this->assertCount(count($childrenLines), $children);
@@ -7045,7 +6897,7 @@ public function testDeepNodesLinesAndIndexes(string $phpDoc, array $nodeAttribut
 		];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$visitor = new NodeCollectingVisitor();
 		$traverser = new NodeTraverser([$visitor]);
 		$traverser->traverse([$phpDocParser->parse($tokens)]);
@@ -7118,7 +6970,7 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
 		];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$returnTag = $phpDocNode->getReturnTagValues()[0];
 		$type = $returnTag->type;
@@ -7165,7 +7017,7 @@ public function testVerifyAttributes(string $label, string $input): void
 		$usedAttributes = ['lines' => true, 'indexes' => true];
 		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
 		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, $usedAttributes);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 
 		$visitor = new NodeCollectingVisitor();
@@ -7188,7 +7040,6 @@ public function testDoctrine(
 		string $label,
 		string $input,
 		PhpDocNode $expectedPhpDocNode,
-		?PhpDocNode $withPreserveTypeAliasesWithInvalidTypesExpectedPhpDocNode = null,
 		array $expectedAnnotations = []
 	): void
 	{
@@ -7460,7 +7311,7 @@ public function testTextBetweenTagsBelongsToDescription(
 	{
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, [], true, true);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, [], true);
 
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 		$actualPhpDocNode = $phpDocParser->parse($tokens);
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index cc21861f..6fa8e9c4 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -104,7 +104,6 @@ public function enterNode(PhpNode $phpNode)
 				$phpDocParser = new PhpDocParser(
 					new TypeParser($constExprParser, true, $usedAttributes),
 					$constExprParser,
-					true,
 					$usedAttributes
 				);
 				$lexer = new Lexer();
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 6ef45ad7..ee7717de 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -73,7 +73,6 @@ protected function setUp(): void
 		$this->phpDocParser = new PhpDocParser(
 			$this->typeParser,
 			$constExprParser,
-			true,
 			$usedAttributes
 		);
 	}

From e141cce19074880bb68fca58a534684cf71e2042 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 1 Jun 2023 17:38:06 +0200
Subject: [PATCH 06/45] QuoteAwareConstExprStringNode becomes the new
 ConstExprStringNode

---
 phpstan-baseline.neon                         |  4 +-
 src/Ast/ConstExpr/ConstExprStringNode.php     | 59 +++++++++++++-
 .../ConstExpr/DoctrineConstExprStringNode.php |  2 +-
 .../QuoteAwareConstExprStringNode.php         | 78 -------------------
 src/Parser/ConstExprParser.php                | 46 +++--------
 src/Parser/PhpDocParser.php                   | 10 ++-
 src/Parser/TypeParser.php                     | 34 ++------
 tests/PHPStan/Ast/NodeTraverserTest.php       |  2 +-
 .../Ast/ToString/ConstExprToStringTest.php    | 11 +--
 .../Ast/ToString/PhpDocToStringTest.php       | 12 +--
 .../PHPStan/Ast/ToString/TypeToStringTest.php |  6 +-
 tests/PHPStan/Parser/ConstExprParserTest.php  | 70 ++++-------------
 tests/PHPStan/Parser/PhpDocParserTest.php     | 28 +++----
 tests/PHPStan/Parser/TypeParserTest.php       | 32 ++++----
 .../IntegrationPrinterWithPhpParserTest.php   |  4 +-
 tests/PHPStan/Printer/PrinterTest.php         |  8 +-
 16 files changed, 150 insertions(+), 256 deletions(-)
 delete mode 100644 src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php

diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 4596cc77..d772636c 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,9 +1,9 @@
 parameters:
 	ignoreErrors:
 		-
-			message: "#^Method PHPStan\\\\PhpDocParser\\\\Ast\\\\ConstExpr\\\\QuoteAwareConstExprStringNode\\:\\:escapeDoubleQuotedString\\(\\) should return string but returns string\\|null\\.$#"
+			message: "#^Method PHPStan\\\\PhpDocParser\\\\Ast\\\\ConstExpr\\\\ConstExprStringNode\\:\\:escapeDoubleQuotedString\\(\\) should return string but returns string\\|null\\.$#"
 			count: 1
-			path: src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php
+			path: src/Ast/ConstExpr/ConstExprStringNode.php
 
 		-
 			message: "#^Cannot use array destructuring on array\\<int, array\\<PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\>\\|int\\|string\\>\\|null\\.$#"
diff --git a/src/Ast/ConstExpr/ConstExprStringNode.php b/src/Ast/ConstExpr/ConstExprStringNode.php
index fa44c262..c2d60c79 100644
--- a/src/Ast/ConstExpr/ConstExprStringNode.php
+++ b/src/Ast/ConstExpr/ConstExprStringNode.php
@@ -3,24 +3,79 @@
 namespace PHPStan\PhpDocParser\Ast\ConstExpr;
 
 use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function addcslashes;
+use function assert;
+use function dechex;
+use function ord;
+use function preg_replace_callback;
+use function sprintf;
+use function str_pad;
+use function strlen;
+use const STR_PAD_LEFT;
 
 class ConstExprStringNode implements ConstExprNode
 {
 
+	public const SINGLE_QUOTED = 1;
+	public const DOUBLE_QUOTED = 2;
+
 	use NodeAttributes;
 
 	/** @var string */
 	public $value;
 
-	public function __construct(string $value)
+	/** @var self::SINGLE_QUOTED|self::DOUBLE_QUOTED */
+	public $quoteType;
+
+	/**
+	 * @param self::SINGLE_QUOTED|self::DOUBLE_QUOTED $quoteType
+	 */
+	public function __construct(string $value, int $quoteType)
 	{
 		$this->value = $value;
+		$this->quoteType = $quoteType;
 	}
 
 
 	public function __toString(): string
 	{
-		return $this->value;
+		if ($this->quoteType === self::SINGLE_QUOTED) {
+			// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1007
+			return sprintf("'%s'", addcslashes($this->value, '\'\\'));
+		}
+
+		// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040
+		return sprintf('"%s"', $this->escapeDoubleQuotedString());
+	}
+
+	private function escapeDoubleQuotedString(): string
+	{
+		$quote = '"';
+		$escaped = addcslashes($this->value, "\n\r\t\f\v$" . $quote . '\\');
+
+		// Escape control characters and non-UTF-8 characters.
+		// Regex based on https://stackoverflow.com/a/11709412/385378.
+		$regex = '/(
+              [\x00-\x08\x0E-\x1F] # Control characters
+            | [\xC0-\xC1] # Invalid UTF-8 Bytes
+            | [\xF5-\xFF] # Invalid UTF-8 Bytes
+            | \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
+            | \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point
+            | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
+            | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
+            | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
+            | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
+            | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
+            | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
+            | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
+            | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
+        )/x';
+		return preg_replace_callback($regex, static function ($matches) {
+			assert(strlen($matches[0]) === 1);
+			$hex = dechex(ord($matches[0]));
+
+			return '\\x' . str_pad($hex, 2, '0', STR_PAD_LEFT);
+		}, $escaped);
 	}
 
 }
diff --git a/src/Ast/ConstExpr/DoctrineConstExprStringNode.php b/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
index a503937b..ac28efb3 100644
--- a/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
+++ b/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
@@ -18,7 +18,7 @@ class DoctrineConstExprStringNode extends ConstExprStringNode
 
 	public function __construct(string $value)
 	{
-		parent::__construct($value);
+		parent::__construct($value, self::DOUBLE_QUOTED);
 		$this->value = $value;
 	}
 
diff --git a/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php b/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php
deleted file mode 100644
index f2792b1b..00000000
--- a/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\PhpDocParser\Ast\ConstExpr;
-
-use PHPStan\PhpDocParser\Ast\NodeAttributes;
-use function addcslashes;
-use function assert;
-use function dechex;
-use function ord;
-use function preg_replace_callback;
-use function sprintf;
-use function str_pad;
-use function strlen;
-use const STR_PAD_LEFT;
-
-class QuoteAwareConstExprStringNode extends ConstExprStringNode implements ConstExprNode
-{
-
-	public const SINGLE_QUOTED = 1;
-	public const DOUBLE_QUOTED = 2;
-
-	use NodeAttributes;
-
-	/** @var self::SINGLE_QUOTED|self::DOUBLE_QUOTED */
-	public $quoteType;
-
-	/**
-	 * @param self::SINGLE_QUOTED|self::DOUBLE_QUOTED $quoteType
-	 */
-	public function __construct(string $value, int $quoteType)
-	{
-		parent::__construct($value);
-		$this->quoteType = $quoteType;
-	}
-
-
-	public function __toString(): string
-	{
-		if ($this->quoteType === self::SINGLE_QUOTED) {
-			// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1007
-			return sprintf("'%s'", addcslashes($this->value, '\'\\'));
-		}
-
-		// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040
-		return sprintf('"%s"', $this->escapeDoubleQuotedString());
-	}
-
-	private function escapeDoubleQuotedString(): string
-	{
-		$quote = '"';
-		$escaped = addcslashes($this->value, "\n\r\t\f\v$" . $quote . '\\');
-
-		// Escape control characters and non-UTF-8 characters.
-		// Regex based on https://stackoverflow.com/a/11709412/385378.
-		$regex = '/(
-              [\x00-\x08\x0E-\x1F] # Control characters
-            | [\xC0-\xC1] # Invalid UTF-8 Bytes
-            | [\xF5-\xFF] # Invalid UTF-8 Bytes
-            | \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
-            | \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point
-            | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
-            | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
-            | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
-            | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
-            | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
-            | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
-            | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
-            | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
-        )/x';
-		return preg_replace_callback($regex, static function ($matches) {
-			assert(strlen($matches[0]) === 1);
-			$hex = dechex(ord($matches[0]));
-
-			return '\\x' . str_pad($hex, 2, '0', STR_PAD_LEFT);
-		}, $escaped);
-	}
-
-}
diff --git a/src/Parser/ConstExprParser.php b/src/Parser/ConstExprParser.php
index f6a7306e..07e9fb1d 100644
--- a/src/Parser/ConstExprParser.php
+++ b/src/Parser/ConstExprParser.php
@@ -6,17 +6,10 @@
 use PHPStan\PhpDocParser\Lexer\Lexer;
 use function str_replace;
 use function strtolower;
-use function substr;
 
 class ConstExprParser
 {
 
-	/** @var bool */
-	private $unescapeStrings;
-
-	/** @var bool */
-	private $quoteAwareConstExprString;
-
 	/** @var bool */
 	private $useLinesAttributes;
 
@@ -30,13 +23,9 @@ class ConstExprParser
 	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
 	 */
 	public function __construct(
-		bool $unescapeStrings = false,
-		bool $quoteAwareConstExprString = false,
 		array $usedAttributes = []
 	)
 	{
-		$this->unescapeStrings = $unescapeStrings;
-		$this->quoteAwareConstExprString = $quoteAwareConstExprString;
 		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
 		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
 		$this->parseDoctrineStrings = false;
@@ -48,8 +37,6 @@ public function __construct(
 	public function toDoctrine(): self
 	{
 		$self = new self(
-			$this->unescapeStrings,
-			$this->quoteAwareConstExprString,
 			[
 				'lines' => $this->useLinesAttributes,
 				'indexes' => $this->useIndexAttributes,
@@ -59,7 +46,7 @@ public function toDoctrine(): self
 		return $self;
 	}
 
-	public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode
+	public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 	{
 		$startLine = $tokens->currentTokenLine();
 		$startIndex = $tokens->currentTokenIndex();
@@ -122,34 +109,19 @@ public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\Con
 					$startIndex
 				);
 			}
-			$value = $tokens->currentTokenValue();
+
+			$value = StringUnescaper::unescapeString($tokens->currentTokenValue());
 			$type = $tokens->currentTokenType();
-			if ($trimStrings) {
-				if ($this->unescapeStrings) {
-					$value = StringUnescaper::unescapeString($value);
-				} else {
-					$value = substr($value, 1, -1);
-				}
-			}
 			$tokens->next();
 
-			if ($this->quoteAwareConstExprString) {
-				return $this->enrichWithAttributes(
-					$tokens,
-					new Ast\ConstExpr\QuoteAwareConstExprStringNode(
-						$value,
-						$type === Lexer::TOKEN_SINGLE_QUOTED_STRING
-							? Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED
-							: Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED
-					),
-					$startLine,
-					$startIndex
-				);
-			}
-
 			return $this->enrichWithAttributes(
 				$tokens,
-				new Ast\ConstExpr\ConstExprStringNode($value),
+				new Ast\ConstExpr\ConstExprStringNode(
+					$value,
+					$type === Lexer::TOKEN_SINGLE_QUOTED_STRING
+						? Ast\ConstExpr\ConstExprStringNode::SINGLE_QUOTED
+						: Ast\ConstExpr\ConstExprStringNode::DOUBLE_QUOTED
+				),
 				$startLine,
 				$startIndex
 			);
diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index cdf31ec9..d8bd5081 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -704,7 +704,7 @@ private function parseDoctrineArgumentValue(TokenIterator $tokens)
 		$currentTokenLine = $tokens->currentTokenLine();
 
 		try {
-			$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
+			$constExpr = $this->doctrineConstantExprParser->parse($tokens);
 			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
 				throw new ParserException(
 					$currentTokenValue,
@@ -781,10 +781,14 @@ private function parseDoctrineArrayKey(TokenIterator $tokens)
 			$tokens->next();
 
 		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
-			$key = new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($tokens->currentTokenValue()));
+			$key = $this->doctrineConstantExprParser->parseDoctrineString($tokens->currentTokenValue(), $tokens);
 
 			$tokens->next();
 
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
+			$key = new Ast\ConstExpr\ConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\ConstExprStringNode::SINGLE_QUOTED);
+			$tokens->next();
+
 		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
 			$value = $tokens->currentTokenValue();
 			$tokens->next();
@@ -817,7 +821,7 @@ private function parseDoctrineArrayKey(TokenIterator $tokens)
 			}
 
 			$tokens->rollback();
-			$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
+			$constExpr = $this->doctrineConstantExprParser->parse($tokens);
 			if (!$constExpr instanceof Ast\ConstExpr\ConstFetchNode) {
 				throw new ParserException(
 					$tokens->currentTokenValue(),
diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index c47ba10f..6382ada2 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -11,7 +11,6 @@
 use function strlen;
 use function strpos;
 use function substr_compare;
-use function trim;
 
 class TypeParser
 {
@@ -19,9 +18,6 @@ class TypeParser
 	/** @var ConstExprParser|null */
 	private $constExprParser;
 
-	/** @var bool */
-	private $quoteAwareConstExprString;
-
 	/** @var bool */
 	private $useLinesAttributes;
 
@@ -33,12 +29,10 @@ class TypeParser
 	 */
 	public function __construct(
 		?ConstExprParser $constExprParser = null,
-		bool $quoteAwareConstExprString = false,
 		array $usedAttributes = []
 	)
 	{
 		$this->constExprParser = $constExprParser;
-		$this->quoteAwareConstExprString = $quoteAwareConstExprString;
 		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
 		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
 	}
@@ -220,7 +214,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 		}
 
 		try {
-			$constExpr = $this->constExprParser->parse($tokens, true);
+			$constExpr = $this->constExprParser->parse($tokens);
 			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
 				throw new ParserException(
 					$currentTokenValue,
@@ -735,7 +729,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 		}
 
 		try {
-			$constExpr = $this->constExprParser->parse($tokens, true);
+			$constExpr = $this->constExprParser->parse($tokens);
 			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
 				throw new ParserException(
 					$currentTokenValue,
@@ -932,19 +926,11 @@ private function parseArrayShapeKey(TokenIterator $tokens)
 			$tokens->next();
 
 		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
-			if ($this->quoteAwareConstExprString) {
-				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED);
-			} else {
-				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'"));
-			}
+			$key = new Ast\ConstExpr\ConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\ConstExprStringNode::SINGLE_QUOTED);
 			$tokens->next();
 
 		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
-			if ($this->quoteAwareConstExprString) {
-				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED);
-			} else {
-				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"'));
-			}
+			$key = new Ast\ConstExpr\ConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\ConstExprStringNode::DOUBLE_QUOTED);
 
 			$tokens->next();
 
@@ -1069,19 +1055,11 @@ private function parseObjectShapeKey(TokenIterator $tokens)
 		$startIndex = $tokens->currentTokenIndex();
 
 		if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
-			if ($this->quoteAwareConstExprString) {
-				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED);
-			} else {
-				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'"));
-			}
+			$key = new Ast\ConstExpr\ConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\ConstExprStringNode::SINGLE_QUOTED);
 			$tokens->next();
 
 		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
-			if ($this->quoteAwareConstExprString) {
-				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED);
-			} else {
-				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"'));
-			}
+			$key = new Ast\ConstExpr\ConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\ConstExprStringNode::DOUBLE_QUOTED);
 			$tokens->next();
 
 		} else {
diff --git a/tests/PHPStan/Ast/NodeTraverserTest.php b/tests/PHPStan/Ast/NodeTraverserTest.php
index 74089ed2..10ae5655 100644
--- a/tests/PHPStan/Ast/NodeTraverserTest.php
+++ b/tests/PHPStan/Ast/NodeTraverserTest.php
@@ -393,7 +393,7 @@ public function provideTestInvalidReturn(): array
 			['enterNode', $expr, new ConstExprIntegerNode('42')],
 		]);
 		$visitor8 = new NodeVisitorForTesting([
-			['enterNode', $num, new ReturnTagValueNode(new ConstTypeNode(new ConstExprStringNode('foo')), '')],
+			['enterNode', $num, new ReturnTagValueNode(new ConstTypeNode(new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED)), '')],
 		]);
 
 		return [
diff --git a/tests/PHPStan/Ast/ToString/ConstExprToStringTest.php b/tests/PHPStan/Ast/ToString/ConstExprToStringTest.php
index fe8be67c..b7568879 100644
--- a/tests/PHPStan/Ast/ToString/ConstExprToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/ConstExprToStringTest.php
@@ -44,16 +44,17 @@ public static function provideConstExprCases(): Generator
 			['false', new ConstExprFalseNode()],
 			['8', new ConstExprIntegerNode('8')],
 			['21.37', new ConstExprFloatNode('21.37')],
-			['foo', new ConstExprStringNode('foo')],
+			['\'foo\'', new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED)],
+			['"foo"', new ConstExprStringNode('foo', ConstExprStringNode::DOUBLE_QUOTED)],
 			['FooBar', new ConstFetchNode('', 'FooBar')],
 			['Foo\\Bar::Baz', new ConstFetchNode('Foo\\Bar', 'Baz')],
 			['[]', new ConstExprArrayNode([])],
 			[
-				'[foo, 4 => foo, bar => baz]',
+				"['foo', 4 => 'foo', 'bar' => 'baz']",
 				new ConstExprArrayNode([
-					new ConstExprArrayItemNode(null, new ConstExprStringNode('foo')),
-					new ConstExprArrayItemNode(new ConstExprIntegerNode('4'), new ConstExprStringNode('foo')),
-					new ConstExprArrayItemNode(new ConstExprStringNode('bar'), new ConstExprStringNode('baz')),
+					new ConstExprArrayItemNode(null, new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED)),
+					new ConstExprArrayItemNode(new ConstExprIntegerNode('4'), new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED)),
+					new ConstExprArrayItemNode(new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED), new ConstExprStringNode('baz', ConstExprStringNode::SINGLE_QUOTED)),
 				]),
 			],
 		];
diff --git a/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php b/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
index b57b7db6..f3b029bd 100644
--- a/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
@@ -280,8 +280,8 @@ public static function provideClassCases(): Generator
 				new MethodTagValueParameterNode($string, true, false, 'foo', null),
 			],
 			[
-				'string &foo = bar',
-				new MethodTagValueParameterNode($string, true, false, 'foo', new ConstExprStringNode('bar')),
+				'string &foo = \'bar\'',
+				new MethodTagValueParameterNode($string, true, false, 'foo', new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED)),
 			],
 			[
 				'&...foo',
@@ -408,10 +408,10 @@ public static function provideDoctrineCases(): iterable
 		];
 
 		yield [
-			'{1, a=2}',
+			'{1, \'a\'=2}',
 			new DoctrineArray([
 				new DoctrineArrayItem(null, new ConstExprIntegerNode('1')),
-				new DoctrineArrayItem(new ConstExprStringNode('a'), new ConstExprIntegerNode('2')),
+				new DoctrineArrayItem(new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED), new ConstExprIntegerNode('2')),
 			]),
 		];
 
@@ -421,8 +421,8 @@ public static function provideDoctrineCases(): iterable
 		];
 
 		yield [
-			'a=2',
-			new DoctrineArrayItem(new ConstExprStringNode('a'), new ConstExprIntegerNode('2')),
+			'\'a\'=2',
+			new DoctrineArrayItem(new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED), new ConstExprIntegerNode('2')),
 		];
 
 		yield [
diff --git a/tests/PHPStan/Ast/ToString/TypeToStringTest.php b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
index 9cf7ebf6..1eba0e7d 100644
--- a/tests/PHPStan/Ast/ToString/TypeToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
@@ -68,10 +68,10 @@ public static function provideArrayCases(): Generator
 				], false),
 			],
 			[
-				'array{foo: Foo, bar?: Bar, 1: Baz}',
+				'array{\'foo\': Foo, \'bar\'?: Bar, 1: Baz}',
 				new ArrayShapeNode([
-					new ArrayShapeItemNode(new ConstExprStringNode('foo'), false, new IdentifierTypeNode('Foo')),
-					new ArrayShapeItemNode(new ConstExprStringNode('bar'), true, new IdentifierTypeNode('Bar')),
+					new ArrayShapeItemNode(new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED), false, new IdentifierTypeNode('Foo')),
+					new ArrayShapeItemNode(new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED), true, new IdentifierTypeNode('Bar')),
 					new ArrayShapeItemNode(new ConstExprIntegerNode('1'), false, new IdentifierTypeNode('Baz')),
 				]),
 			],
diff --git a/tests/PHPStan/Parser/ConstExprParserTest.php b/tests/PHPStan/Parser/ConstExprParserTest.php
index f3427431..bb25af8f 100644
--- a/tests/PHPStan/Parser/ConstExprParserTest.php
+++ b/tests/PHPStan/Parser/ConstExprParserTest.php
@@ -31,7 +31,7 @@ protected function setUp(): void
 	{
 		parent::setUp();
 		$this->lexer = new Lexer();
-		$this->constExprParser = new ConstExprParser(true);
+		$this->constExprParser = new ConstExprParser();
 	}
 
 
@@ -65,13 +65,11 @@ public function testParse(string $input, ConstExprNode $expectedExpr, int $nextT
 	 * @dataProvider provideStringNodeParseData
 	 * @dataProvider provideArrayNodeParseData
 	 * @dataProvider provideFetchNodeParseData
-	 *
-	 * @dataProvider provideWithTrimStringsStringNodeParseData
 	 */
 	public function testVerifyAttributes(string $input): void
 	{
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
-		$constExprParser = new ConstExprParser(true, true, [
+		$constExprParser = new ConstExprParser([
 			'lines' => true,
 			'indexes' => true,
 		]);
@@ -312,22 +310,32 @@ public function provideStringNodeParseData(): Iterator
 	{
 		yield [
 			'"foo"',
-			new ConstExprStringNode('"foo"'),
+			new ConstExprStringNode('foo', ConstExprStringNode::DOUBLE_QUOTED),
 		];
 
 		yield [
 			'"Foo \\n\\"\\r Bar"',
-			new ConstExprStringNode('"Foo \\n\\"\\r Bar"'),
+			new ConstExprStringNode("Foo \n\"\r Bar", ConstExprStringNode::DOUBLE_QUOTED),
 		];
 
 		yield [
 			'\'bar\'',
-			new ConstExprStringNode('\'bar\''),
+			new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED),
 		];
 
 		yield [
 			'\'Foo \\\' Bar\'',
-			new ConstExprStringNode('\'Foo \\\' Bar\''),
+			new ConstExprStringNode('Foo \' Bar', ConstExprStringNode::SINGLE_QUOTED),
+		];
+
+		yield [
+			'"\u{1f601}"',
+			new ConstExprStringNode("\u{1f601}", ConstExprStringNode::DOUBLE_QUOTED),
+		];
+
+		yield [
+			'"\u{ffffffff}"',
+			new ConstExprStringNode("\u{fffd}", ConstExprStringNode::DOUBLE_QUOTED),
 		];
 	}
 
@@ -457,50 +465,4 @@ public function provideFetchNodeParseData(): Iterator
 		];
 	}
 
-	/**
-	 * @dataProvider provideWithTrimStringsStringNodeParseData
-	 */
-	public function testParseWithTrimStrings(string $input, ConstExprNode $expectedExpr, int $nextTokenType = Lexer::TOKEN_END): void
-	{
-		$tokens = new TokenIterator($this->lexer->tokenize($input));
-		$exprNode = $this->constExprParser->parse($tokens, true);
-
-		$this->assertSame((string) $expectedExpr, (string) $exprNode);
-		$this->assertEquals($expectedExpr, $exprNode);
-		$this->assertSame($nextTokenType, $tokens->currentTokenType());
-	}
-
-	public function provideWithTrimStringsStringNodeParseData(): Iterator
-	{
-		yield [
-			'"foo"',
-			new ConstExprStringNode('foo'),
-		];
-
-		yield [
-			'"Foo \\n\\"\\r Bar"',
-			new ConstExprStringNode("Foo \n\"\r Bar"),
-		];
-
-		yield [
-			'\'bar\'',
-			new ConstExprStringNode('bar'),
-		];
-
-		yield [
-			'\'Foo \\\' Bar\'',
-			new ConstExprStringNode('Foo \' Bar'),
-		];
-
-		yield [
-			'"\u{1f601}"',
-			new ConstExprStringNode("\u{1f601}"),
-		];
-
-		yield [
-			'"\u{ffffffff}"',
-			new ConstExprStringNode("\u{fffd}"),
-		];
-	}
-
 }
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 8a19da1b..588d8a8a 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -2616,11 +2616,11 @@ public function provideMethodTagsData(): Iterator
 								new ConstExprArrayNode([
 									new ConstExprArrayItemNode(
 										null,
-										new ConstExprStringNode('\'a\'')
+										new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED)
 									),
 									new ConstExprArrayItemNode(
 										null,
-										new ConstExprStringNode('\'b\'')
+										new ConstExprStringNode('b', ConstExprStringNode::SINGLE_QUOTED)
 									),
 								])
 							),
@@ -4019,7 +4019,7 @@ public function provideTemplateTagsData(): Iterator
 						'T',
 						new IdentifierTypeNode('string'),
 						'the value type',
-						new ConstTypeNode(new ConstExprStringNode(''))
+						new ConstTypeNode(new ConstExprStringNode('', ConstExprStringNode::SINGLE_QUOTED))
 					)
 				),
 			]),
@@ -5066,8 +5066,8 @@ public function provideRealWorldExampleData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new UnionTypeNode([
-							new ConstTypeNode(new ConstExprStringNode('foo')),
-							new ConstTypeNode(new ConstExprStringNode('bar')),
+							new ConstTypeNode(new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED)),
+							new ConstTypeNode(new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED)),
 						]),
 						''
 					)
@@ -5352,7 +5352,7 @@ public function provideRealWorldExampleData(): Iterator
 											),
 										]),
 										new ArrayShapeNode([
-											new ArrayShapeItemNode(null, false, new ConstTypeNode(new ConstExprStringNode(''))),
+											new ArrayShapeItemNode(null, false, new ConstTypeNode(new ConstExprStringNode('', ConstExprStringNode::SINGLE_QUOTED))),
 											new ArrayShapeItemNode(null, false, new ConstTypeNode(new ConstExprIntegerNode('-1'))),
 										]),
 									]),
@@ -6811,8 +6811,8 @@ public function testLinesAndIndexes(string $phpDoc, array $childrenLines): void
 			'lines' => true,
 			'indexes' => true,
 		];
-		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
-		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
+		$constExprParser = new ConstExprParser($usedAttributes);
+		$typeParser = new TypeParser($constExprParser, $usedAttributes);
 		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$children = $phpDocNode->children;
@@ -6895,8 +6895,8 @@ public function testDeepNodesLinesAndIndexes(string $phpDoc, array $nodeAttribut
 			'lines' => true,
 			'indexes' => true,
 		];
-		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
-		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
+		$constExprParser = new ConstExprParser($usedAttributes);
+		$typeParser = new TypeParser($constExprParser, $usedAttributes);
 		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$visitor = new NodeCollectingVisitor();
 		$traverser = new NodeTraverser([$visitor]);
@@ -6968,8 +6968,8 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
 			'lines' => true,
 			'indexes' => true,
 		];
-		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
-		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
+		$constExprParser = new ConstExprParser($usedAttributes);
+		$typeParser = new TypeParser($constExprParser, $usedAttributes);
 		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$returnTag = $phpDocNode->getReturnTagValues()[0];
@@ -7015,8 +7015,8 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
 	public function testVerifyAttributes(string $label, string $input): void
 	{
 		$usedAttributes = ['lines' => true, 'indexes' => true];
-		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
-		$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
+		$constExprParser = new ConstExprParser($usedAttributes);
+		$typeParser = new TypeParser($constExprParser, $usedAttributes);
 		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index 3610fa52..c226df84 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -6,8 +6,8 @@
 use PHPStan\PhpDocParser\Ast\Attribute;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
-use PHPStan\PhpDocParser\Ast\ConstExpr\QuoteAwareConstExprStringNode;
 use PHPStan\PhpDocParser\Ast\Node;
 use PHPStan\PhpDocParser\Ast\NodeTraverser;
 use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
@@ -50,7 +50,7 @@ protected function setUp(): void
 	{
 		parent::setUp();
 		$this->lexer = new Lexer();
-		$this->typeParser = new TypeParser(new ConstExprParser(true, true), true);
+		$this->typeParser = new TypeParser(new ConstExprParser());
 	}
 
 
@@ -118,7 +118,7 @@ public function testVerifyAttributes(string $input, $expectedResult): void
 		}
 
 		$usedAttributes = ['lines' => true, 'indexes' => true];
-		$typeParser = new TypeParser(new ConstExprParser(true, true, $usedAttributes), true, $usedAttributes);
+		$typeParser = new TypeParser(new ConstExprParser($usedAttributes), $usedAttributes);
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 
 		$visitor = new NodeCollectingVisitor();
@@ -550,7 +550,7 @@ public function provideParseData(): array
 				'array{"a": int}',
 				new ArrayShapeNode([
 					new ArrayShapeItemNode(
-						new QuoteAwareConstExprStringNode('a', QuoteAwareConstExprStringNode::DOUBLE_QUOTED),
+						new ConstExprStringNode('a', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -560,7 +560,7 @@ public function provideParseData(): array
 				'array{\'a\': int}',
 				new ArrayShapeNode([
 					new ArrayShapeItemNode(
-						new QuoteAwareConstExprStringNode('a', QuoteAwareConstExprStringNode::SINGLE_QUOTED),
+						new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -570,7 +570,7 @@ public function provideParseData(): array
 				'array{\'$ref\': int}',
 				new ArrayShapeNode([
 					new ArrayShapeItemNode(
-						new QuoteAwareConstExprStringNode('$ref', QuoteAwareConstExprStringNode::SINGLE_QUOTED),
+						new ConstExprStringNode('$ref', ConstExprStringNode::SINGLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -580,7 +580,7 @@ public function provideParseData(): array
 				'array{"$ref": int}',
 				new ArrayShapeNode([
 					new ArrayShapeItemNode(
-						new QuoteAwareConstExprStringNode('$ref', QuoteAwareConstExprStringNode::DOUBLE_QUOTED),
+						new ConstExprStringNode('$ref', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -1582,8 +1582,8 @@ public function provideParseData(): array
 			[
 				"'foo'|'bar'",
 				new UnionTypeNode([
-					new ConstTypeNode(new QuoteAwareConstExprStringNode('foo', QuoteAwareConstExprStringNode::SINGLE_QUOTED)),
-					new ConstTypeNode(new QuoteAwareConstExprStringNode('bar', QuoteAwareConstExprStringNode::SINGLE_QUOTED)),
+					new ConstTypeNode(new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED)),
+					new ConstTypeNode(new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED)),
 				]),
 			],
 			[
@@ -1632,7 +1632,7 @@ public function provideParseData(): array
 			],
 			[
 				'"bar"',
-				new ConstTypeNode(new QuoteAwareConstExprStringNode('bar', QuoteAwareConstExprStringNode::DOUBLE_QUOTED)),
+				new ConstTypeNode(new ConstExprStringNode('bar', ConstExprStringNode::DOUBLE_QUOTED)),
 			],
 			[
 				'Foo::FOO_*',
@@ -1670,7 +1670,7 @@ public function provideParseData(): array
 			[
 				'( "foo" | Foo::FOO_* )',
 				new UnionTypeNode([
-					new ConstTypeNode(new QuoteAwareConstExprStringNode('foo', QuoteAwareConstExprStringNode::DOUBLE_QUOTED)),
+					new ConstTypeNode(new ConstExprStringNode('foo', ConstExprStringNode::DOUBLE_QUOTED)),
 					new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')),
 				]),
 			],
@@ -2336,7 +2336,7 @@ public function provideParseData(): array
 				'object{"a": int}',
 				new ObjectShapeNode([
 					new ObjectShapeItemNode(
-						new QuoteAwareConstExprStringNode('a', QuoteAwareConstExprStringNode::DOUBLE_QUOTED),
+						new ConstExprStringNode('a', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -2346,7 +2346,7 @@ public function provideParseData(): array
 				'object{\'a\': int}',
 				new ObjectShapeNode([
 					new ObjectShapeItemNode(
-						new QuoteAwareConstExprStringNode('a', QuoteAwareConstExprStringNode::SINGLE_QUOTED),
+						new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -2356,7 +2356,7 @@ public function provideParseData(): array
 				'object{\'$ref\': int}',
 				new ObjectShapeNode([
 					new ObjectShapeItemNode(
-						new QuoteAwareConstExprStringNode('$ref', QuoteAwareConstExprStringNode::SINGLE_QUOTED),
+						new ConstExprStringNode('$ref', ConstExprStringNode::SINGLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -2366,7 +2366,7 @@ public function provideParseData(): array
 				'object{"$ref": int}',
 				new ObjectShapeNode([
 					new ObjectShapeItemNode(
-						new QuoteAwareConstExprStringNode('$ref', QuoteAwareConstExprStringNode::DOUBLE_QUOTED),
+						new ConstExprStringNode('$ref', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
 						new IdentifierTypeNode('int')
 					),
@@ -3123,7 +3123,7 @@ public function testLinesAndIndexes(string $input, array $assertions): void
 			'lines' => true,
 			'indexes' => true,
 		];
-		$typeParser = new TypeParser(new ConstExprParser(true, true), true, $usedAttributes);
+		$typeParser = new TypeParser(new ConstExprParser(), $usedAttributes);
 		$typeNode = $typeParser->parse($tokens);
 
 		foreach ($assertions as [$callable, $expectedContent, $startLine, $endLine, $startIndex, $endIndex]) {
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index 6fa8e9c4..52fab1d1 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -100,9 +100,9 @@ public function enterNode(PhpNode $phpNode)
 				$phpDoc = $phpNode->getDocComment()->getText();
 
 				$usedAttributes = ['lines' => true, 'indexes' => true];
-				$constExprParser = new ConstExprParser(true, true, $usedAttributes);
+				$constExprParser = new ConstExprParser($usedAttributes);
 				$phpDocParser = new PhpDocParser(
-					new TypeParser($constExprParser, true, $usedAttributes),
+					new TypeParser($constExprParser, $usedAttributes),
 					$constExprParser,
 					$usedAttributes
 				);
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index ee7717de..1c4f4cd1 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -7,8 +7,8 @@
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayItemNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
-use PHPStan\PhpDocParser\Ast\ConstExpr\QuoteAwareConstExprStringNode;
 use PHPStan\PhpDocParser\Ast\Node;
 use PHPStan\PhpDocParser\Ast\NodeTraverser;
 use PHPStan\PhpDocParser\Ast\NodeVisitor;
@@ -68,8 +68,8 @@ class PrinterTest extends TestCase
 	protected function setUp(): void
 	{
 		$usedAttributes = ['lines' => true, 'indexes' => true];
-		$constExprParser = new ConstExprParser(true, true, $usedAttributes);
-		$this->typeParser = new TypeParser($constExprParser, true, $usedAttributes);
+		$constExprParser = new ConstExprParser($usedAttributes);
+		$this->typeParser = new TypeParser($constExprParser, $usedAttributes);
 		$this->phpDocParser = new PhpDocParser(
 			$this->typeParser,
 			$constExprParser,
@@ -968,7 +968,7 @@ public function enterNode(Node $node)
 			public function enterNode(Node $node)
 			{
 				if ($node instanceof ArrayShapeItemNode) {
-					$node->keyName = new QuoteAwareConstExprStringNode('test', QuoteAwareConstExprStringNode::SINGLE_QUOTED);
+					$node->keyName = new ConstExprStringNode('test', ConstExprStringNode::SINGLE_QUOTED);
 				}
 
 				return $node;

From 2c7df218aef40da28beda3e28486839196a26634 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 1 Jun 2023 18:10:17 +0200
Subject: [PATCH 07/45] ConstExprParser is required in TypeParser

---
 src/Parser/TypeParser.php | 15 ++-------------
 1 file changed, 2 insertions(+), 13 deletions(-)

diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index 6382ada2..0ad89956 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -15,7 +15,7 @@
 class TypeParser
 {
 
-	/** @var ConstExprParser|null */
+	/** @var ConstExprParser */
 	private $constExprParser;
 
 	/** @var bool */
@@ -28,7 +28,7 @@ class TypeParser
 	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
 	 */
 	public function __construct(
-		?ConstExprParser $constExprParser = null,
+		ConstExprParser $constExprParser,
 		array $usedAttributes = []
 	)
 	{
@@ -202,17 +202,6 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 		$currentTokenOffset = $tokens->currentTokenOffset();
 		$currentTokenLine = $tokens->currentTokenLine();
 
-		if ($this->constExprParser === null) {
-			throw new ParserException(
-				$currentTokenValue,
-				$currentTokenType,
-				$currentTokenOffset,
-				Lexer::TOKEN_IDENTIFIER,
-				null,
-				$currentTokenLine
-			);
-		}
-
 		try {
 			$constExpr = $this->constExprParser->parse($tokens);
 			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {

From bdfec7cf652e96dedf8bb121980fef837e35d07f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 7 Sep 2024 14:52:06 +0200
Subject: [PATCH 08/45] Update build-cs

---
 .github/workflows/build.yml                   |    2 +-
 Makefile                                      |    2 +-
 src/Ast/ConstExpr/ConstExprArrayItemNode.php  |    6 +-
 src/Ast/ConstExpr/ConstExprArrayNode.php      |    2 +-
 src/Ast/ConstExpr/ConstExprFloatNode.php      |    3 +-
 src/Ast/ConstExpr/ConstExprIntegerNode.php    |    3 +-
 src/Ast/ConstExpr/ConstExprStringNode.php     |    3 +-
 src/Ast/ConstExpr/ConstFetchNode.php          |    5 +-
 .../ConstExpr/DoctrineConstExprStringNode.php |    3 +-
 src/Ast/NodeAttributes.php                    |    2 +-
 src/Ast/NodeTraverser.php                     |   14 +-
 src/Ast/PhpDoc/AssertTagMethodValueNode.php   |   17 +-
 src/Ast/PhpDoc/AssertTagPropertyValueNode.php |   17 +-
 src/Ast/PhpDoc/AssertTagValueNode.php         |   14 +-
 src/Ast/PhpDoc/DeprecatedTagValueNode.php     |    2 +-
 .../PhpDoc/Doctrine/DoctrineAnnotation.php    |    5 +-
 src/Ast/PhpDoc/Doctrine/DoctrineArgument.php  |    3 +-
 src/Ast/PhpDoc/Doctrine/DoctrineArray.php     |    2 +-
 .../PhpDoc/Doctrine/DoctrineTagValueNode.php  |    5 +-
 src/Ast/PhpDoc/ExtendsTagValueNode.php        |    5 +-
 src/Ast/PhpDoc/GenericTagValueNode.php        |    2 +-
 src/Ast/PhpDoc/ImplementsTagValueNode.php     |    5 +-
 src/Ast/PhpDoc/InvalidTagValueNode.php        |    4 +-
 src/Ast/PhpDoc/MethodTagValueNode.php         |   15 +-
 .../PhpDoc/MethodTagValueParameterNode.php    |   15 +-
 src/Ast/PhpDoc/MixinTagValueNode.php          |    5 +-
 .../PhpDoc/ParamClosureThisTagValueNode.php   |    8 +-
 ...ImmediatelyInvokedCallableTagValueNode.php |    5 +-
 .../ParamLaterInvokedCallableTagValueNode.php |    5 +-
 src/Ast/PhpDoc/ParamOutTagValueNode.php       |    8 +-
 src/Ast/PhpDoc/ParamTagValueNode.php          |   14 +-
 src/Ast/PhpDoc/PhpDocNode.php                 |  120 +-
 src/Ast/PhpDoc/PhpDocTagNode.php              |    6 +-
 src/Ast/PhpDoc/PhpDocTextNode.php             |    3 +-
 src/Ast/PhpDoc/PropertyTagValueNode.php       |    8 +-
 src/Ast/PhpDoc/RequireExtendsTagValueNode.php |    5 +-
 .../PhpDoc/RequireImplementsTagValueNode.php  |    5 +-
 src/Ast/PhpDoc/ReturnTagValueNode.php         |    5 +-
 src/Ast/PhpDoc/SelfOutTagValueNode.php        |    5 +-
 src/Ast/PhpDoc/TemplateTagValueNode.php       |   10 +-
 src/Ast/PhpDoc/ThrowsTagValueNode.php         |    5 +-
 .../PhpDoc/TypeAliasImportTagValueNode.php    |   11 +-
 src/Ast/PhpDoc/TypeAliasTagValueNode.php      |    6 +-
 src/Ast/PhpDoc/TypelessParamTagValueNode.php  |   11 +-
 src/Ast/PhpDoc/UsesTagValueNode.php           |    5 +-
 src/Ast/PhpDoc/VarTagValueNode.php            |    7 +-
 src/Ast/Type/ArrayShapeItemNode.php           |    8 +-
 src/Ast/Type/ArrayShapeNode.php               |    8 +-
 src/Ast/Type/ArrayShapeUnsealedTypeNode.php   |    6 +-
 src/Ast/Type/ArrayTypeNode.php                |    3 +-
 src/Ast/Type/CallableTypeNode.php             |   10 +-
 src/Ast/Type/CallableTypeParameterNode.php    |   14 +-
 .../Type/ConditionalTypeForParameterNode.php  |   17 +-
 src/Ast/Type/ConditionalTypeNode.php          |   17 +-
 src/Ast/Type/ConstTypeNode.php                |    3 +-
 src/Ast/Type/GenericTypeNode.php              |    7 +-
 src/Ast/Type/IdentifierTypeNode.php           |    3 +-
 src/Ast/Type/IntersectionTypeNode.php         |    2 +-
 src/Ast/Type/InvalidTypeNode.php              |    2 +-
 src/Ast/Type/NullableTypeNode.php             |    3 +-
 src/Ast/Type/ObjectShapeItemNode.php          |    8 +-
 src/Ast/Type/ObjectShapeNode.php              |    2 +-
 src/Ast/Type/OffsetAccessTypeNode.php         |    6 +-
 src/Ast/Type/UnionTypeNode.php                |    2 +-
 src/Lexer/Lexer.php                           |    3 +-
 src/Parser/ConstExprParser.php                |   41 +-
 src/Parser/ParserException.php                |   20 +-
 src/Parser/PhpDocParser.php                   |   74 +-
 src/Parser/StringUnescaper.php                |    4 +-
 src/Parser/TokenIterator.php                  |   14 +-
 src/Parser/TypeParser.php                     |   66 +-
 src/Printer/Differ.php                        |    2 +-
 src/Printer/Printer.php                       |   50 +-
 .../PHPStan/Ast/Attributes/AttributesTest.php |    3 +-
 tests/PHPStan/Ast/NodeTraverserTest.php       |    2 +-
 tests/PHPStan/Ast/NodeVisitorForTesting.php   |    7 +-
 .../Ast/ToString/PhpDocToStringTest.php       |    8 +-
 .../PHPStan/Ast/ToString/TypeToStringTest.php |   12 +-
 tests/PHPStan/Parser/ConstExprParserTest.php  |   34 +-
 tests/PHPStan/Parser/Doctrine/ApiResource.php |   18 +-
 tests/PHPStan/Parser/FuzzyTest.php            |   13 +-
 .../PHPStan/Parser/NodeCollectingVisitor.php  |    2 +-
 tests/PHPStan/Parser/PhpDocParserTest.php     | 1544 ++++++++---------
 tests/PHPStan/Parser/TypeParserTest.php       |  636 ++++---
 tests/PHPStan/Printer/DifferTest.php          |    8 +-
 .../IntegrationPrinterWithPhpParserTest.php   |    7 +-
 tests/PHPStan/Printer/PhpPrinter.php          |    6 +-
 .../PhpPrinterIndentationDetectorVisitor.php  |    9 +-
 tests/PHPStan/Printer/PrinterTest.php         |   97 +-
 89 files changed, 1463 insertions(+), 1751 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 59e2b2ab..0c05365c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -56,7 +56,7 @@ jobs:
         with:
           repository: "phpstan/build-cs"
           path: "build-cs"
-          ref: "1.x"
+          ref: "2.x"
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
diff --git a/Makefile b/Makefile
index 4399bed1..294ad0c5 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ lint:
 .PHONY: cs-install
 cs-install:
 	git clone https://github.com/phpstan/build-cs.git || true
-	git -C build-cs fetch origin && git -C build-cs reset --hard origin/1.x
+	git -C build-cs fetch origin && git -C build-cs reset --hard origin/2.x
 	composer install --working-dir build-cs
 
 .PHONY: cs
diff --git a/src/Ast/ConstExpr/ConstExprArrayItemNode.php b/src/Ast/ConstExpr/ConstExprArrayItemNode.php
index ef144521..cd067cac 100644
--- a/src/Ast/ConstExpr/ConstExprArrayItemNode.php
+++ b/src/Ast/ConstExpr/ConstExprArrayItemNode.php
@@ -10,11 +10,9 @@ class ConstExprArrayItemNode implements ConstExprNode
 
 	use NodeAttributes;
 
-	/** @var ConstExprNode|null */
-	public $key;
+	public ?ConstExprNode $key = null;
 
-	/** @var ConstExprNode */
-	public $value;
+	public ConstExprNode $value;
 
 	public function __construct(?ConstExprNode $key, ConstExprNode $value)
 	{
diff --git a/src/Ast/ConstExpr/ConstExprArrayNode.php b/src/Ast/ConstExpr/ConstExprArrayNode.php
index 1f9def37..dc7ad4a9 100644
--- a/src/Ast/ConstExpr/ConstExprArrayNode.php
+++ b/src/Ast/ConstExpr/ConstExprArrayNode.php
@@ -11,7 +11,7 @@ class ConstExprArrayNode implements ConstExprNode
 	use NodeAttributes;
 
 	/** @var ConstExprArrayItemNode[] */
-	public $items;
+	public array $items;
 
 	/**
 	 * @param ConstExprArrayItemNode[] $items
diff --git a/src/Ast/ConstExpr/ConstExprFloatNode.php b/src/Ast/ConstExpr/ConstExprFloatNode.php
index a4192fba..30ab41de 100644
--- a/src/Ast/ConstExpr/ConstExprFloatNode.php
+++ b/src/Ast/ConstExpr/ConstExprFloatNode.php
@@ -9,8 +9,7 @@ class ConstExprFloatNode implements ConstExprNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $value;
+	public string $value;
 
 	public function __construct(string $value)
 	{
diff --git a/src/Ast/ConstExpr/ConstExprIntegerNode.php b/src/Ast/ConstExpr/ConstExprIntegerNode.php
index 5339bb5a..9f0285f3 100644
--- a/src/Ast/ConstExpr/ConstExprIntegerNode.php
+++ b/src/Ast/ConstExpr/ConstExprIntegerNode.php
@@ -9,8 +9,7 @@ class ConstExprIntegerNode implements ConstExprNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $value;
+	public string $value;
 
 	public function __construct(string $value)
 	{
diff --git a/src/Ast/ConstExpr/ConstExprStringNode.php b/src/Ast/ConstExpr/ConstExprStringNode.php
index c2d60c79..26e5ef4a 100644
--- a/src/Ast/ConstExpr/ConstExprStringNode.php
+++ b/src/Ast/ConstExpr/ConstExprStringNode.php
@@ -21,8 +21,7 @@ class ConstExprStringNode implements ConstExprNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $value;
+	public string $value;
 
 	/** @var self::SINGLE_QUOTED|self::DOUBLE_QUOTED */
 	public $quoteType;
diff --git a/src/Ast/ConstExpr/ConstFetchNode.php b/src/Ast/ConstExpr/ConstFetchNode.php
index 3f20363b..51c33989 100644
--- a/src/Ast/ConstExpr/ConstFetchNode.php
+++ b/src/Ast/ConstExpr/ConstFetchNode.php
@@ -10,10 +10,9 @@ class ConstFetchNode implements ConstExprNode
 	use NodeAttributes;
 
 	/** @var string class name for class constants or empty string for non-class constants */
-	public $className;
+	public string $className;
 
-	/** @var string */
-	public $name;
+	public string $name;
 
 	public function __construct(string $className, string $name)
 	{
diff --git a/src/Ast/ConstExpr/DoctrineConstExprStringNode.php b/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
index ac28efb3..4515e892 100644
--- a/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
+++ b/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
@@ -13,8 +13,7 @@ class DoctrineConstExprStringNode extends ConstExprStringNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $value;
+	public string $value;
 
 	public function __construct(string $value)
 	{
diff --git a/src/Ast/NodeAttributes.php b/src/Ast/NodeAttributes.php
index 3da1779d..a7ddec39 100644
--- a/src/Ast/NodeAttributes.php
+++ b/src/Ast/NodeAttributes.php
@@ -8,7 +8,7 @@ trait NodeAttributes
 {
 
 	/** @var array<string, mixed> */
-	private $attributes = [];
+	private array $attributes = [];
 
 	/**
 	 * @param mixed $value
diff --git a/src/Ast/NodeTraverser.php b/src/Ast/NodeTraverser.php
index 63b25c37..76c88f0c 100644
--- a/src/Ast/NodeTraverser.php
+++ b/src/Ast/NodeTraverser.php
@@ -62,10 +62,10 @@ final class NodeTraverser
 	public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
 
 	/** @var list<NodeVisitor> Visitors */
-	private $visitors = [];
+	private array $visitors = [];
 
 	/** @var bool Whether traversal should be stopped */
-	private $stopTraversal;
+	private bool $stopTraversal;
 
 	/**
 	 * @param list<NodeVisitor> $visitors
@@ -151,7 +151,7 @@ private function traverseNode(Node $node): Node
 						break 2;
 					} else {
 						throw new LogicException(
-							'enterNode() returned invalid value of type ' . gettype($return)
+							'enterNode() returned invalid value of type ' . gettype($return),
 						);
 					}
 				}
@@ -176,11 +176,11 @@ private function traverseNode(Node $node): Node
 						} elseif (is_array($return)) {
 							throw new LogicException(
 								'leaveNode() may only return an array ' .
-								'if the parent structure is an array'
+								'if the parent structure is an array',
 							);
 						} else {
 							throw new LogicException(
-								'leaveNode() returned invalid value of type ' . gettype($return)
+								'leaveNode() returned invalid value of type ' . gettype($return),
 							);
 						}
 					}
@@ -237,7 +237,7 @@ private function traverseArray(array $nodes): array
 						break 2;
 					} else {
 						throw new LogicException(
-							'enterNode() returned invalid value of type ' . gettype($return)
+							'enterNode() returned invalid value of type ' . gettype($return),
 						);
 					}
 				}
@@ -267,7 +267,7 @@ private function traverseArray(array $nodes): array
 							break 2;
 						} else {
 							throw new LogicException(
-								'leaveNode() returned invalid value of type ' . gettype($return)
+								'leaveNode() returned invalid value of type ' . gettype($return),
 							);
 						}
 					}
diff --git a/src/Ast/PhpDoc/AssertTagMethodValueNode.php b/src/Ast/PhpDoc/AssertTagMethodValueNode.php
index cf4f5563..2bd64631 100644
--- a/src/Ast/PhpDoc/AssertTagMethodValueNode.php
+++ b/src/Ast/PhpDoc/AssertTagMethodValueNode.php
@@ -11,23 +11,18 @@ class AssertTagMethodValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var string */
-	public $parameter;
+	public string $parameter;
 
-	/** @var string */
-	public $method;
+	public string $method;
 
-	/** @var bool */
-	public $isNegated;
+	public bool $isNegated;
 
-	/** @var bool */
-	public $isEquality;
+	public bool $isEquality;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $parameter, string $method, bool $isNegated, string $description, bool $isEquality = false)
 	{
diff --git a/src/Ast/PhpDoc/AssertTagPropertyValueNode.php b/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
index 4fb31807..44e7c244 100644
--- a/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
+++ b/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
@@ -11,23 +11,18 @@ class AssertTagPropertyValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var string */
-	public $parameter;
+	public string $parameter;
 
-	/** @var string */
-	public $property;
+	public string $property;
 
-	/** @var bool */
-	public $isNegated;
+	public bool $isNegated;
 
-	/** @var bool */
-	public $isEquality;
+	public bool $isEquality;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $parameter, string $property, bool $isNegated, string $description, bool $isEquality = false)
 	{
diff --git a/src/Ast/PhpDoc/AssertTagValueNode.php b/src/Ast/PhpDoc/AssertTagValueNode.php
index d6423f50..9f2be9f0 100644
--- a/src/Ast/PhpDoc/AssertTagValueNode.php
+++ b/src/Ast/PhpDoc/AssertTagValueNode.php
@@ -11,20 +11,16 @@ class AssertTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var string */
-	public $parameter;
+	public string $parameter;
 
-	/** @var bool */
-	public $isNegated;
+	public bool $isNegated;
 
-	/** @var bool */
-	public $isEquality;
+	public bool $isEquality;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $parameter, bool $isNegated, string $description, bool $isEquality = false)
 	{
diff --git a/src/Ast/PhpDoc/DeprecatedTagValueNode.php b/src/Ast/PhpDoc/DeprecatedTagValueNode.php
index abf2f1a6..facdd2b4 100644
--- a/src/Ast/PhpDoc/DeprecatedTagValueNode.php
+++ b/src/Ast/PhpDoc/DeprecatedTagValueNode.php
@@ -11,7 +11,7 @@ class DeprecatedTagValueNode implements PhpDocTagValueNode
 	use NodeAttributes;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(string $description)
 	{
diff --git a/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php b/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php
index 3a93f5aa..778b21fa 100644
--- a/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php
+++ b/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php
@@ -11,11 +11,10 @@ class DoctrineAnnotation implements Node
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $name;
+	public string $name;
 
 	/** @var list<DoctrineArgument> */
-	public $arguments;
+	public array $arguments;
 
 	/**
 	 * @param list<DoctrineArgument> $arguments
diff --git a/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php b/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php
index f30812cf..30fe9879 100644
--- a/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php
+++ b/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php
@@ -15,8 +15,7 @@ class DoctrineArgument implements Node
 
 	use NodeAttributes;
 
-	/** @var IdentifierTypeNode|null */
-	public $key;
+	public ?IdentifierTypeNode $key = null;
 
 	/** @var ValueType */
 	public $value;
diff --git a/src/Ast/PhpDoc/Doctrine/DoctrineArray.php b/src/Ast/PhpDoc/Doctrine/DoctrineArray.php
index e740567d..06686a52 100644
--- a/src/Ast/PhpDoc/Doctrine/DoctrineArray.php
+++ b/src/Ast/PhpDoc/Doctrine/DoctrineArray.php
@@ -12,7 +12,7 @@ class DoctrineArray implements Node
 	use NodeAttributes;
 
 	/** @var list<DoctrineArrayItem> */
-	public $items;
+	public array $items;
 
 	/**
 	 * @param list<DoctrineArrayItem> $items
diff --git a/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php b/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php
index 84f7b18b..3940e24f 100644
--- a/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php
+++ b/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php
@@ -11,11 +11,10 @@ class DoctrineTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var DoctrineAnnotation */
-	public $annotation;
+	public DoctrineAnnotation $annotation;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 
 	public function __construct(
diff --git a/src/Ast/PhpDoc/ExtendsTagValueNode.php b/src/Ast/PhpDoc/ExtendsTagValueNode.php
index 3bf53e13..d9bbeec5 100644
--- a/src/Ast/PhpDoc/ExtendsTagValueNode.php
+++ b/src/Ast/PhpDoc/ExtendsTagValueNode.php
@@ -11,11 +11,10 @@ class ExtendsTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var GenericTypeNode */
-	public $type;
+	public GenericTypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(GenericTypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/GenericTagValueNode.php b/src/Ast/PhpDoc/GenericTagValueNode.php
index 026aa153..de77051a 100644
--- a/src/Ast/PhpDoc/GenericTagValueNode.php
+++ b/src/Ast/PhpDoc/GenericTagValueNode.php
@@ -10,7 +10,7 @@ class GenericTagValueNode implements PhpDocTagValueNode
 	use NodeAttributes;
 
 	/** @var string (may be empty) */
-	public $value;
+	public string $value;
 
 	public function __construct(string $value)
 	{
diff --git a/src/Ast/PhpDoc/ImplementsTagValueNode.php b/src/Ast/PhpDoc/ImplementsTagValueNode.php
index 99043d91..34a30b0d 100644
--- a/src/Ast/PhpDoc/ImplementsTagValueNode.php
+++ b/src/Ast/PhpDoc/ImplementsTagValueNode.php
@@ -11,11 +11,10 @@ class ImplementsTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var GenericTypeNode */
-	public $type;
+	public GenericTypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(GenericTypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/InvalidTagValueNode.php b/src/Ast/PhpDoc/InvalidTagValueNode.php
index ca7b4f20..7bb20b22 100644
--- a/src/Ast/PhpDoc/InvalidTagValueNode.php
+++ b/src/Ast/PhpDoc/InvalidTagValueNode.php
@@ -17,10 +17,10 @@ class InvalidTagValueNode implements PhpDocTagValueNode
 	use NodeAttributes;
 
 	/** @var string (may be empty) */
-	public $value;
+	public string $value;
 
 	/** @var mixed[] */
-	private $exceptionArgs;
+	private array $exceptionArgs;
 
 	public function __construct(string $value, ParserException $exception)
 	{
diff --git a/src/Ast/PhpDoc/MethodTagValueNode.php b/src/Ast/PhpDoc/MethodTagValueNode.php
index 211510be..467f19bb 100644
--- a/src/Ast/PhpDoc/MethodTagValueNode.php
+++ b/src/Ast/PhpDoc/MethodTagValueNode.php
@@ -12,23 +12,20 @@ class MethodTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var bool */
-	public $isStatic;
+	public bool $isStatic;
 
-	/** @var TypeNode|null */
-	public $returnType;
+	public ?TypeNode $returnType = null;
 
-	/** @var string */
-	public $methodName;
+	public string $methodName;
 
 	/** @var TemplateTagValueNode[] */
-	public $templateTypes;
+	public array $templateTypes;
 
 	/** @var MethodTagValueParameterNode[] */
-	public $parameters;
+	public array $parameters;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	/**
 	 * @param MethodTagValueParameterNode[] $parameters
diff --git a/src/Ast/PhpDoc/MethodTagValueParameterNode.php b/src/Ast/PhpDoc/MethodTagValueParameterNode.php
index 7c17e44c..ebf33e32 100644
--- a/src/Ast/PhpDoc/MethodTagValueParameterNode.php
+++ b/src/Ast/PhpDoc/MethodTagValueParameterNode.php
@@ -12,20 +12,15 @@ class MethodTagValueParameterNode implements Node
 
 	use NodeAttributes;
 
-	/** @var TypeNode|null */
-	public $type;
+	public ?TypeNode $type = null;
 
-	/** @var bool */
-	public $isReference;
+	public bool $isReference;
 
-	/** @var bool */
-	public $isVariadic;
+	public bool $isVariadic;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
-	/** @var ConstExprNode|null */
-	public $defaultValue;
+	public ?ConstExprNode $defaultValue = null;
 
 	public function __construct(?TypeNode $type, bool $isReference, bool $isVariadic, string $parameterName, ?ConstExprNode $defaultValue)
 	{
diff --git a/src/Ast/PhpDoc/MixinTagValueNode.php b/src/Ast/PhpDoc/MixinTagValueNode.php
index d9b7d78a..7a4e43ea 100644
--- a/src/Ast/PhpDoc/MixinTagValueNode.php
+++ b/src/Ast/PhpDoc/MixinTagValueNode.php
@@ -11,11 +11,10 @@ class MixinTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php b/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php
index 0ac2131a..54feff9e 100644
--- a/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php
+++ b/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php
@@ -11,14 +11,12 @@ class ParamClosureThisTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $parameterName, string $description)
 	{
diff --git a/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php b/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php
index 0f480f7a..9a6761f7 100644
--- a/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php
+++ b/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php
@@ -10,11 +10,10 @@ class ParamImmediatelyInvokedCallableTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(string $parameterName, string $description)
 	{
diff --git a/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php b/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php
index eab353f9..84db67a9 100644
--- a/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php
+++ b/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php
@@ -10,11 +10,10 @@ class ParamLaterInvokedCallableTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(string $parameterName, string $description)
 	{
diff --git a/src/Ast/PhpDoc/ParamOutTagValueNode.php b/src/Ast/PhpDoc/ParamOutTagValueNode.php
index 9f374bf1..3e89f9d7 100644
--- a/src/Ast/PhpDoc/ParamOutTagValueNode.php
+++ b/src/Ast/PhpDoc/ParamOutTagValueNode.php
@@ -11,14 +11,12 @@ class ParamOutTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $parameterName, string $description)
 	{
diff --git a/src/Ast/PhpDoc/ParamTagValueNode.php b/src/Ast/PhpDoc/ParamTagValueNode.php
index f93af0ea..6e4089e4 100644
--- a/src/Ast/PhpDoc/ParamTagValueNode.php
+++ b/src/Ast/PhpDoc/ParamTagValueNode.php
@@ -11,20 +11,16 @@ class ParamTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var bool */
-	public $isReference;
+	public bool $isReference;
 
-	/** @var bool */
-	public $isVariadic;
+	public bool $isVariadic;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
 	{
diff --git a/src/Ast/PhpDoc/PhpDocNode.php b/src/Ast/PhpDoc/PhpDocNode.php
index ade55b78..11a7f033 100644
--- a/src/Ast/PhpDoc/PhpDocNode.php
+++ b/src/Ast/PhpDoc/PhpDocNode.php
@@ -15,7 +15,7 @@ class PhpDocNode implements Node
 	use NodeAttributes;
 
 	/** @var PhpDocChildNode[] */
-	public $children;
+	public array $children;
 
 	/**
 	 * @param PhpDocChildNode[] $children
@@ -31,9 +31,7 @@ public function __construct(array $children)
 	 */
 	public function getTags(): array
 	{
-		return array_filter($this->children, static function (PhpDocChildNode $child): bool {
-			return $child instanceof PhpDocTagNode;
-		});
+		return array_filter($this->children, static fn (PhpDocChildNode $child): bool => $child instanceof PhpDocTagNode);
 	}
 
 
@@ -42,9 +40,7 @@ public function getTags(): array
 	 */
 	public function getTagsByName(string $tagName): array
 	{
-		return array_filter($this->getTags(), static function (PhpDocTagNode $tag) use ($tagName): bool {
-			return $tag->name === $tagName;
-		});
+		return array_filter($this->getTags(), static fn (PhpDocTagNode $tag): bool => $tag->name === $tagName);
 	}
 
 
@@ -55,9 +51,7 @@ public function getVarTagValues(string $tagName = '@var'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof VarTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof VarTagValueNode,
 		);
 	}
 
@@ -69,9 +63,7 @@ public function getParamTagValues(string $tagName = '@param'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ParamTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ParamTagValueNode,
 		);
 	}
 
@@ -83,9 +75,7 @@ public function getTypelessParamTagValues(string $tagName = '@param'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof TypelessParamTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof TypelessParamTagValueNode,
 		);
 	}
 
@@ -97,9 +87,7 @@ public function getParamImmediatelyInvokedCallableTagValues(string $tagName = '@
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ParamImmediatelyInvokedCallableTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ParamImmediatelyInvokedCallableTagValueNode,
 		);
 	}
 
@@ -111,9 +99,7 @@ public function getParamLaterInvokedCallableTagValues(string $tagName = '@param-
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ParamLaterInvokedCallableTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ParamLaterInvokedCallableTagValueNode,
 		);
 	}
 
@@ -125,9 +111,7 @@ public function getParamClosureThisTagValues(string $tagName = '@param-closure-t
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ParamClosureThisTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ParamClosureThisTagValueNode,
 		);
 	}
 
@@ -139,9 +123,7 @@ public function getTemplateTagValues(string $tagName = '@template'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof TemplateTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof TemplateTagValueNode,
 		);
 	}
 
@@ -153,9 +135,7 @@ public function getExtendsTagValues(string $tagName = '@extends'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ExtendsTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ExtendsTagValueNode,
 		);
 	}
 
@@ -167,9 +147,7 @@ public function getImplementsTagValues(string $tagName = '@implements'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ImplementsTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ImplementsTagValueNode,
 		);
 	}
 
@@ -181,9 +159,7 @@ public function getUsesTagValues(string $tagName = '@use'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof UsesTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof UsesTagValueNode,
 		);
 	}
 
@@ -195,9 +171,7 @@ public function getReturnTagValues(string $tagName = '@return'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ReturnTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ReturnTagValueNode,
 		);
 	}
 
@@ -209,9 +183,7 @@ public function getThrowsTagValues(string $tagName = '@throws'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ThrowsTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ThrowsTagValueNode,
 		);
 	}
 
@@ -223,9 +195,7 @@ public function getMixinTagValues(string $tagName = '@mixin'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof MixinTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof MixinTagValueNode,
 		);
 	}
 
@@ -236,9 +206,7 @@ public function getRequireExtendsTagValues(string $tagName = '@phpstan-require-e
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof RequireExtendsTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof RequireExtendsTagValueNode,
 		);
 	}
 
@@ -249,9 +217,7 @@ public function getRequireImplementsTagValues(string $tagName = '@phpstan-requir
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof RequireImplementsTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof RequireImplementsTagValueNode,
 		);
 	}
 
@@ -262,9 +228,7 @@ public function getDeprecatedTagValues(): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName('@deprecated'), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof DeprecatedTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof DeprecatedTagValueNode,
 		);
 	}
 
@@ -276,9 +240,7 @@ public function getPropertyTagValues(string $tagName = '@property'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof PropertyTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof PropertyTagValueNode,
 		);
 	}
 
@@ -290,9 +252,7 @@ public function getPropertyReadTagValues(string $tagName = '@property-read'): ar
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof PropertyTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof PropertyTagValueNode,
 		);
 	}
 
@@ -304,9 +264,7 @@ public function getPropertyWriteTagValues(string $tagName = '@property-write'):
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof PropertyTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof PropertyTagValueNode,
 		);
 	}
 
@@ -318,9 +276,7 @@ public function getMethodTagValues(string $tagName = '@method'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof MethodTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof MethodTagValueNode,
 		);
 	}
 
@@ -332,9 +288,7 @@ public function getTypeAliasTagValues(string $tagName = '@phpstan-type'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof TypeAliasTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof TypeAliasTagValueNode,
 		);
 	}
 
@@ -346,9 +300,7 @@ public function getTypeAliasImportTagValues(string $tagName = '@phpstan-import-t
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof TypeAliasImportTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof TypeAliasImportTagValueNode,
 		);
 	}
 
@@ -360,9 +312,7 @@ public function getAssertTagValues(string $tagName = '@phpstan-assert'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof AssertTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof AssertTagValueNode,
 		);
 	}
 
@@ -374,9 +324,7 @@ public function getAssertPropertyTagValues(string $tagName = '@phpstan-assert'):
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof AssertTagPropertyValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof AssertTagPropertyValueNode,
 		);
 	}
 
@@ -388,9 +336,7 @@ public function getAssertMethodTagValues(string $tagName = '@phpstan-assert'): a
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof AssertTagMethodValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof AssertTagMethodValueNode,
 		);
 	}
 
@@ -402,9 +348,7 @@ public function getSelfOutTypeTagValues(string $tagName = '@phpstan-this-out'):
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof SelfOutTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof SelfOutTagValueNode,
 		);
 	}
 
@@ -416,9 +360,7 @@ public function getParamOutTypeTagValues(string $tagName = '@param-out'): array
 	{
 		return array_filter(
 			array_column($this->getTagsByName($tagName), 'value'),
-			static function (PhpDocTagValueNode $value): bool {
-				return $value instanceof ParamOutTagValueNode;
-			}
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof ParamOutTagValueNode,
 		);
 	}
 
@@ -430,7 +372,7 @@ static function (PhpDocChildNode $child): string {
 				$s = (string) $child;
 				return $s === '' ? '' : ' ' . $s;
 			},
-			$this->children
+			$this->children,
 		);
 		return "/**\n *" . implode("\n *", $children) . "\n */";
 	}
diff --git a/src/Ast/PhpDoc/PhpDocTagNode.php b/src/Ast/PhpDoc/PhpDocTagNode.php
index d20746fc..cac2feeb 100644
--- a/src/Ast/PhpDoc/PhpDocTagNode.php
+++ b/src/Ast/PhpDoc/PhpDocTagNode.php
@@ -11,11 +11,9 @@ class PhpDocTagNode implements PhpDocChildNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $name;
+	public string $name;
 
-	/** @var PhpDocTagValueNode */
-	public $value;
+	public PhpDocTagValueNode $value;
 
 	public function __construct(string $name, PhpDocTagValueNode $value)
 	{
diff --git a/src/Ast/PhpDoc/PhpDocTextNode.php b/src/Ast/PhpDoc/PhpDocTextNode.php
index 0bca3c99..97a96894 100644
--- a/src/Ast/PhpDoc/PhpDocTextNode.php
+++ b/src/Ast/PhpDoc/PhpDocTextNode.php
@@ -9,8 +9,7 @@ class PhpDocTextNode implements PhpDocChildNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $text;
+	public string $text;
 
 	public function __construct(string $text)
 	{
diff --git a/src/Ast/PhpDoc/PropertyTagValueNode.php b/src/Ast/PhpDoc/PropertyTagValueNode.php
index 046003d1..cbf622f8 100644
--- a/src/Ast/PhpDoc/PropertyTagValueNode.php
+++ b/src/Ast/PhpDoc/PropertyTagValueNode.php
@@ -11,14 +11,12 @@ class PropertyTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var string */
-	public $propertyName;
+	public string $propertyName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $propertyName, string $description)
 	{
diff --git a/src/Ast/PhpDoc/RequireExtendsTagValueNode.php b/src/Ast/PhpDoc/RequireExtendsTagValueNode.php
index 91c26892..5d8e1d22 100644
--- a/src/Ast/PhpDoc/RequireExtendsTagValueNode.php
+++ b/src/Ast/PhpDoc/RequireExtendsTagValueNode.php
@@ -11,11 +11,10 @@ class RequireExtendsTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/RequireImplementsTagValueNode.php b/src/Ast/PhpDoc/RequireImplementsTagValueNode.php
index 65c9213f..6b7f7bae 100644
--- a/src/Ast/PhpDoc/RequireImplementsTagValueNode.php
+++ b/src/Ast/PhpDoc/RequireImplementsTagValueNode.php
@@ -11,11 +11,10 @@ class RequireImplementsTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/ReturnTagValueNode.php b/src/Ast/PhpDoc/ReturnTagValueNode.php
index d53c8c75..c063bf48 100644
--- a/src/Ast/PhpDoc/ReturnTagValueNode.php
+++ b/src/Ast/PhpDoc/ReturnTagValueNode.php
@@ -11,11 +11,10 @@ class ReturnTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/SelfOutTagValueNode.php b/src/Ast/PhpDoc/SelfOutTagValueNode.php
index 83169aff..d2377620 100644
--- a/src/Ast/PhpDoc/SelfOutTagValueNode.php
+++ b/src/Ast/PhpDoc/SelfOutTagValueNode.php
@@ -11,11 +11,10 @@ class SelfOutTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/TemplateTagValueNode.php b/src/Ast/PhpDoc/TemplateTagValueNode.php
index 78b311ee..eeb79487 100644
--- a/src/Ast/PhpDoc/TemplateTagValueNode.php
+++ b/src/Ast/PhpDoc/TemplateTagValueNode.php
@@ -12,16 +12,14 @@ class TemplateTagValueNode implements PhpDocTagValueNode
 	use NodeAttributes;
 
 	/** @var non-empty-string */
-	public $name;
+	public string $name;
 
-	/** @var TypeNode|null */
-	public $bound;
+	public ?TypeNode $bound = null;
 
-	/** @var TypeNode|null */
-	public $default;
+	public ?TypeNode $default = null;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	/**
 	 * @param non-empty-string $name
diff --git a/src/Ast/PhpDoc/ThrowsTagValueNode.php b/src/Ast/PhpDoc/ThrowsTagValueNode.php
index 62d2aed3..dc5521b4 100644
--- a/src/Ast/PhpDoc/ThrowsTagValueNode.php
+++ b/src/Ast/PhpDoc/ThrowsTagValueNode.php
@@ -11,11 +11,10 @@ class ThrowsTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php b/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php
index ad6b85a5..d0f945d2 100644
--- a/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php
+++ b/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php
@@ -11,14 +11,11 @@ class TypeAliasImportTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $importedAlias;
+	public string $importedAlias;
 
-	/** @var IdentifierTypeNode */
-	public $importedFrom;
+	public IdentifierTypeNode $importedFrom;
 
-	/** @var string|null */
-	public $importedAs;
+	public ?string $importedAs = null;
 
 	public function __construct(string $importedAlias, IdentifierTypeNode $importedFrom, ?string $importedAs)
 	{
@@ -31,7 +28,7 @@ public function __toString(): string
 	{
 		return trim(
 			"{$this->importedAlias} from {$this->importedFrom}"
-			. ($this->importedAs !== null ? " as {$this->importedAs}" : '')
+			. ($this->importedAs !== null ? " as {$this->importedAs}" : ''),
 		);
 	}
 
diff --git a/src/Ast/PhpDoc/TypeAliasTagValueNode.php b/src/Ast/PhpDoc/TypeAliasTagValueNode.php
index 4ccaaac4..ae366b50 100644
--- a/src/Ast/PhpDoc/TypeAliasTagValueNode.php
+++ b/src/Ast/PhpDoc/TypeAliasTagValueNode.php
@@ -11,11 +11,9 @@ class TypeAliasTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $alias;
+	public string $alias;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	public function __construct(string $alias, TypeNode $type)
 	{
diff --git a/src/Ast/PhpDoc/TypelessParamTagValueNode.php b/src/Ast/PhpDoc/TypelessParamTagValueNode.php
index 8b982954..778972aa 100644
--- a/src/Ast/PhpDoc/TypelessParamTagValueNode.php
+++ b/src/Ast/PhpDoc/TypelessParamTagValueNode.php
@@ -10,17 +10,14 @@ class TypelessParamTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var bool */
-	public $isReference;
+	public bool $isReference;
 
-	/** @var bool */
-	public $isVariadic;
+	public bool $isVariadic;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
 	{
diff --git a/src/Ast/PhpDoc/UsesTagValueNode.php b/src/Ast/PhpDoc/UsesTagValueNode.php
index cd573d97..b33fff60 100644
--- a/src/Ast/PhpDoc/UsesTagValueNode.php
+++ b/src/Ast/PhpDoc/UsesTagValueNode.php
@@ -11,11 +11,10 @@ class UsesTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var GenericTypeNode */
-	public $type;
+	public GenericTypeNode $type;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(GenericTypeNode $type, string $description)
 	{
diff --git a/src/Ast/PhpDoc/VarTagValueNode.php b/src/Ast/PhpDoc/VarTagValueNode.php
index afb941a8..5b0538c8 100644
--- a/src/Ast/PhpDoc/VarTagValueNode.php
+++ b/src/Ast/PhpDoc/VarTagValueNode.php
@@ -11,14 +11,13 @@ class VarTagValueNode implements PhpDocTagValueNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	/** @var string (may be empty) */
-	public $variableName;
+	public string $variableName;
 
 	/** @var string (may be empty) */
-	public $description;
+	public string $description;
 
 	public function __construct(TypeNode $type, string $variableName, string $description)
 	{
diff --git a/src/Ast/Type/ArrayShapeItemNode.php b/src/Ast/Type/ArrayShapeItemNode.php
index 660c6c9d..2c47eda2 100644
--- a/src/Ast/Type/ArrayShapeItemNode.php
+++ b/src/Ast/Type/ArrayShapeItemNode.php
@@ -15,11 +15,9 @@ class ArrayShapeItemNode implements TypeNode
 	/** @var ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null */
 	public $keyName;
 
-	/** @var bool */
-	public $optional;
+	public bool $optional;
 
-	/** @var TypeNode */
-	public $valueType;
+	public TypeNode $valueType;
 
 	/**
 	 * @param ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null $keyName
@@ -39,7 +37,7 @@ public function __toString(): string
 				'%s%s: %s',
 				(string) $this->keyName,
 				$this->optional ? '?' : '',
-				(string) $this->valueType
+				(string) $this->valueType,
 			);
 		}
 
diff --git a/src/Ast/Type/ArrayShapeNode.php b/src/Ast/Type/ArrayShapeNode.php
index 1f4ed4a9..e99ad321 100644
--- a/src/Ast/Type/ArrayShapeNode.php
+++ b/src/Ast/Type/ArrayShapeNode.php
@@ -14,16 +14,14 @@ class ArrayShapeNode implements TypeNode
 	use NodeAttributes;
 
 	/** @var ArrayShapeItemNode[] */
-	public $items;
+	public array $items;
 
-	/** @var bool */
-	public $sealed;
+	public bool $sealed;
 
 	/** @var self::KIND_* */
 	public $kind;
 
-	/** @var ArrayShapeUnsealedTypeNode|null */
-	public $unsealedType;
+	public ?ArrayShapeUnsealedTypeNode $unsealedType = null;
 
 	/**
 	 * @param ArrayShapeItemNode[] $items
diff --git a/src/Ast/Type/ArrayShapeUnsealedTypeNode.php b/src/Ast/Type/ArrayShapeUnsealedTypeNode.php
index 7ffdf1d2..68d6b36f 100644
--- a/src/Ast/Type/ArrayShapeUnsealedTypeNode.php
+++ b/src/Ast/Type/ArrayShapeUnsealedTypeNode.php
@@ -11,11 +11,9 @@ class ArrayShapeUnsealedTypeNode implements Node
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $valueType;
+	public TypeNode $valueType;
 
-	/** @var TypeNode|null */
-	public $keyType;
+	public ?TypeNode $keyType = null;
 
 	public function __construct(TypeNode $valueType, ?TypeNode $keyType)
 	{
diff --git a/src/Ast/Type/ArrayTypeNode.php b/src/Ast/Type/ArrayTypeNode.php
index d2031032..95c020d8 100644
--- a/src/Ast/Type/ArrayTypeNode.php
+++ b/src/Ast/Type/ArrayTypeNode.php
@@ -9,8 +9,7 @@ class ArrayTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	public function __construct(TypeNode $type)
 	{
diff --git a/src/Ast/Type/CallableTypeNode.php b/src/Ast/Type/CallableTypeNode.php
index 4c913198..2f0f6361 100644
--- a/src/Ast/Type/CallableTypeNode.php
+++ b/src/Ast/Type/CallableTypeNode.php
@@ -11,17 +11,15 @@ class CallableTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var IdentifierTypeNode */
-	public $identifier;
+	public IdentifierTypeNode $identifier;
 
 	/** @var TemplateTagValueNode[] */
-	public $templateTypes;
+	public array $templateTypes;
 
 	/** @var CallableTypeParameterNode[] */
-	public $parameters;
+	public array $parameters;
 
-	/** @var TypeNode */
-	public $returnType;
+	public TypeNode $returnType;
 
 	/**
 	 * @param CallableTypeParameterNode[] $parameters
diff --git a/src/Ast/Type/CallableTypeParameterNode.php b/src/Ast/Type/CallableTypeParameterNode.php
index c78d4c7b..08119670 100644
--- a/src/Ast/Type/CallableTypeParameterNode.php
+++ b/src/Ast/Type/CallableTypeParameterNode.php
@@ -11,20 +11,16 @@ class CallableTypeParameterNode implements Node
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var bool */
-	public $isReference;
+	public bool $isReference;
 
-	/** @var bool */
-	public $isVariadic;
+	public bool $isVariadic;
 
 	/** @var string (may be empty) */
-	public $parameterName;
+	public string $parameterName;
 
-	/** @var bool */
-	public $isOptional;
+	public bool $isOptional;
 
 	public function __construct(TypeNode $type, bool $isReference, bool $isVariadic, string $parameterName, bool $isOptional)
 	{
diff --git a/src/Ast/Type/ConditionalTypeForParameterNode.php b/src/Ast/Type/ConditionalTypeForParameterNode.php
index fbfcae95..4c120d22 100644
--- a/src/Ast/Type/ConditionalTypeForParameterNode.php
+++ b/src/Ast/Type/ConditionalTypeForParameterNode.php
@@ -10,20 +10,15 @@ class ConditionalTypeForParameterNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $parameterName;
+	public string $parameterName;
 
-	/** @var TypeNode */
-	public $targetType;
+	public TypeNode $targetType;
 
-	/** @var TypeNode */
-	public $if;
+	public TypeNode $if;
 
-	/** @var TypeNode */
-	public $else;
+	public TypeNode $else;
 
-	/** @var bool */
-	public $negated;
+	public bool $negated;
 
 	public function __construct(string $parameterName, TypeNode $targetType, TypeNode $if, TypeNode $else, bool $negated)
 	{
@@ -42,7 +37,7 @@ public function __toString(): string
 			$this->negated ? 'is not' : 'is',
 			$this->targetType,
 			$this->if,
-			$this->else
+			$this->else,
 		);
 	}
 
diff --git a/src/Ast/Type/ConditionalTypeNode.php b/src/Ast/Type/ConditionalTypeNode.php
index bfdb0db1..89c1c63f 100644
--- a/src/Ast/Type/ConditionalTypeNode.php
+++ b/src/Ast/Type/ConditionalTypeNode.php
@@ -10,20 +10,15 @@ class ConditionalTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $subjectType;
+	public TypeNode $subjectType;
 
-	/** @var TypeNode */
-	public $targetType;
+	public TypeNode $targetType;
 
-	/** @var TypeNode */
-	public $if;
+	public TypeNode $if;
 
-	/** @var TypeNode */
-	public $else;
+	public TypeNode $else;
 
-	/** @var bool */
-	public $negated;
+	public bool $negated;
 
 	public function __construct(TypeNode $subjectType, TypeNode $targetType, TypeNode $if, TypeNode $else, bool $negated)
 	{
@@ -42,7 +37,7 @@ public function __toString(): string
 			$this->negated ? 'is not' : 'is',
 			$this->targetType,
 			$this->if,
-			$this->else
+			$this->else,
 		);
 	}
 
diff --git a/src/Ast/Type/ConstTypeNode.php b/src/Ast/Type/ConstTypeNode.php
index 0096055b..22823e5b 100644
--- a/src/Ast/Type/ConstTypeNode.php
+++ b/src/Ast/Type/ConstTypeNode.php
@@ -10,8 +10,7 @@ class ConstTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var ConstExprNode */
-	public $constExpr;
+	public ConstExprNode $constExpr;
 
 	public function __construct(ConstExprNode $constExpr)
 	{
diff --git a/src/Ast/Type/GenericTypeNode.php b/src/Ast/Type/GenericTypeNode.php
index 44e1d16d..4e52c00a 100644
--- a/src/Ast/Type/GenericTypeNode.php
+++ b/src/Ast/Type/GenericTypeNode.php
@@ -16,14 +16,13 @@ class GenericTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var IdentifierTypeNode */
-	public $type;
+	public IdentifierTypeNode $type;
 
 	/** @var TypeNode[] */
-	public $genericTypes;
+	public array $genericTypes;
 
 	/** @var (self::VARIANCE_*)[] */
-	public $variances;
+	public array $variances;
 
 	/**
 	 * @param TypeNode[] $genericTypes
diff --git a/src/Ast/Type/IdentifierTypeNode.php b/src/Ast/Type/IdentifierTypeNode.php
index 29bac308..df93fa86 100644
--- a/src/Ast/Type/IdentifierTypeNode.php
+++ b/src/Ast/Type/IdentifierTypeNode.php
@@ -9,8 +9,7 @@ class IdentifierTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var string */
-	public $name;
+	public string $name;
 
 	public function __construct(string $name)
 	{
diff --git a/src/Ast/Type/IntersectionTypeNode.php b/src/Ast/Type/IntersectionTypeNode.php
index fd761cf7..b3059cf5 100644
--- a/src/Ast/Type/IntersectionTypeNode.php
+++ b/src/Ast/Type/IntersectionTypeNode.php
@@ -12,7 +12,7 @@ class IntersectionTypeNode implements TypeNode
 	use NodeAttributes;
 
 	/** @var TypeNode[] */
-	public $types;
+	public array $types;
 
 	/**
 	 * @param TypeNode[] $types
diff --git a/src/Ast/Type/InvalidTypeNode.php b/src/Ast/Type/InvalidTypeNode.php
index 1ec47cf6..318176e7 100644
--- a/src/Ast/Type/InvalidTypeNode.php
+++ b/src/Ast/Type/InvalidTypeNode.php
@@ -11,7 +11,7 @@ class InvalidTypeNode implements TypeNode
 	use NodeAttributes;
 
 	/** @var mixed[] */
-	private $exceptionArgs;
+	private array $exceptionArgs;
 
 	public function __construct(ParserException $exception)
 	{
diff --git a/src/Ast/Type/NullableTypeNode.php b/src/Ast/Type/NullableTypeNode.php
index 73f438cd..080a13f2 100644
--- a/src/Ast/Type/NullableTypeNode.php
+++ b/src/Ast/Type/NullableTypeNode.php
@@ -9,8 +9,7 @@ class NullableTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
 	public function __construct(TypeNode $type)
 	{
diff --git a/src/Ast/Type/ObjectShapeItemNode.php b/src/Ast/Type/ObjectShapeItemNode.php
index 2f012406..d64b92e1 100644
--- a/src/Ast/Type/ObjectShapeItemNode.php
+++ b/src/Ast/Type/ObjectShapeItemNode.php
@@ -14,11 +14,9 @@ class ObjectShapeItemNode implements TypeNode
 	/** @var ConstExprStringNode|IdentifierTypeNode */
 	public $keyName;
 
-	/** @var bool */
-	public $optional;
+	public bool $optional;
 
-	/** @var TypeNode */
-	public $valueType;
+	public TypeNode $valueType;
 
 	/**
 	 * @param ConstExprStringNode|IdentifierTypeNode $keyName
@@ -38,7 +36,7 @@ public function __toString(): string
 				'%s%s: %s',
 				(string) $this->keyName,
 				$this->optional ? '?' : '',
-				(string) $this->valueType
+				(string) $this->valueType,
 			);
 		}
 
diff --git a/src/Ast/Type/ObjectShapeNode.php b/src/Ast/Type/ObjectShapeNode.php
index f418bc30..41dc68c3 100644
--- a/src/Ast/Type/ObjectShapeNode.php
+++ b/src/Ast/Type/ObjectShapeNode.php
@@ -11,7 +11,7 @@ class ObjectShapeNode implements TypeNode
 	use NodeAttributes;
 
 	/** @var ObjectShapeItemNode[] */
-	public $items;
+	public array $items;
 
 	/**
 	 * @param ObjectShapeItemNode[] $items
diff --git a/src/Ast/Type/OffsetAccessTypeNode.php b/src/Ast/Type/OffsetAccessTypeNode.php
index c27ec0a3..4bd67d8d 100644
--- a/src/Ast/Type/OffsetAccessTypeNode.php
+++ b/src/Ast/Type/OffsetAccessTypeNode.php
@@ -9,11 +9,9 @@ class OffsetAccessTypeNode implements TypeNode
 
 	use NodeAttributes;
 
-	/** @var TypeNode */
-	public $type;
+	public TypeNode $type;
 
-	/** @var TypeNode */
-	public $offset;
+	public TypeNode $offset;
 
 	public function __construct(TypeNode $type, TypeNode $offset)
 	{
diff --git a/src/Ast/Type/UnionTypeNode.php b/src/Ast/Type/UnionTypeNode.php
index c552dab5..602cb3dd 100644
--- a/src/Ast/Type/UnionTypeNode.php
+++ b/src/Ast/Type/UnionTypeNode.php
@@ -12,7 +12,7 @@ class UnionTypeNode implements TypeNode
 	use NodeAttributes;
 
 	/** @var TypeNode[] */
-	public $types;
+	public array $types;
 
 	/**
 	 * @param TypeNode[] $types
diff --git a/src/Lexer/Lexer.php b/src/Lexer/Lexer.php
index 0b5f9ed5..c090e62a 100644
--- a/src/Lexer/Lexer.php
+++ b/src/Lexer/Lexer.php
@@ -94,8 +94,7 @@ class Lexer
 	public const TYPE_OFFSET = 1;
 	public const LINE_OFFSET = 2;
 
-	/** @var string|null */
-	private $regexp;
+	private ?string $regexp = null;
 
 	/**
 	 * @return list<array{string, int, int}>
diff --git a/src/Parser/ConstExprParser.php b/src/Parser/ConstExprParser.php
index 07e9fb1d..de476406 100644
--- a/src/Parser/ConstExprParser.php
+++ b/src/Parser/ConstExprParser.php
@@ -10,14 +10,11 @@
 class ConstExprParser
 {
 
-	/** @var bool */
-	private $useLinesAttributes;
+	private bool $useLinesAttributes;
 
-	/** @var bool */
-	private $useIndexAttributes;
+	private bool $useIndexAttributes;
 
-	/** @var bool */
-	private $parseDoctrineStrings;
+	private bool $parseDoctrineStrings;
 
 	/**
 	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
@@ -40,7 +37,7 @@ public function toDoctrine(): self
 			[
 				'lines' => $this->useLinesAttributes,
 				'indexes' => $this->useIndexAttributes,
-			]
+			],
 		);
 		$self->parseDoctrineStrings = true;
 		return $self;
@@ -58,7 +55,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 				$tokens,
 				new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 
@@ -70,7 +67,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 				$tokens,
 				new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 
@@ -82,7 +79,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 				$tokens,
 				new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($value)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 
@@ -95,7 +92,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 						$tokens->currentTokenOffset(),
 						Lexer::TOKEN_DOUBLE_QUOTED_STRING,
 						null,
-						$tokens->currentTokenLine()
+						$tokens->currentTokenLine(),
 					);
 				}
 
@@ -106,7 +103,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 					$tokens,
 					$this->parseDoctrineString($value, $tokens),
 					$startLine,
-					$startIndex
+					$startIndex,
 				);
 			}
 
@@ -120,10 +117,10 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 					$value,
 					$type === Lexer::TOKEN_SINGLE_QUOTED_STRING
 						? Ast\ConstExpr\ConstExprStringNode::SINGLE_QUOTED
-						: Ast\ConstExpr\ConstExprStringNode::DOUBLE_QUOTED
+						: Ast\ConstExpr\ConstExprStringNode::DOUBLE_QUOTED,
 				),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 
 		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
@@ -136,21 +133,21 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 						$tokens,
 						new Ast\ConstExpr\ConstExprTrueNode(),
 						$startLine,
-						$startIndex
+						$startIndex,
 					);
 				case 'false':
 					return $this->enrichWithAttributes(
 						$tokens,
 						new Ast\ConstExpr\ConstExprFalseNode(),
 						$startLine,
-						$startIndex
+						$startIndex,
 					);
 				case 'null':
 					return $this->enrichWithAttributes(
 						$tokens,
 						new Ast\ConstExpr\ConstExprNullNode(),
 						$startLine,
-						$startIndex
+						$startIndex,
 					);
 				case 'array':
 					$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
@@ -192,7 +189,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 					$tokens,
 					new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName),
 					$startLine,
-					$startIndex
+					$startIndex,
 				);
 
 			}
@@ -201,7 +198,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 				$tokens,
 				new Ast\ConstExpr\ConstFetchNode('', $identifier),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 
 		} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
@@ -214,7 +211,7 @@ public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
 			$tokens->currentTokenOffset(),
 			Lexer::TOKEN_IDENTIFIER,
 			null,
-			$tokens->currentTokenLine()
+			$tokens->currentTokenLine(),
 		);
 	}
 
@@ -236,7 +233,7 @@ private function parseArray(TokenIterator $tokens, int $endToken, int $startInde
 			$tokens,
 			new Ast\ConstExpr\ConstExprArrayNode($items),
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
@@ -278,7 +275,7 @@ private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprA
 			$tokens,
 			new Ast\ConstExpr\ConstExprArrayItemNode($key, $value),
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
diff --git a/src/Parser/ParserException.php b/src/Parser/ParserException.php
index 6ab5cc07..67715e74 100644
--- a/src/Parser/ParserException.php
+++ b/src/Parser/ParserException.php
@@ -14,23 +14,17 @@
 class ParserException extends Exception
 {
 
-	/** @var string */
-	private $currentTokenValue;
+	private string $currentTokenValue;
 
-	/** @var int */
-	private $currentTokenType;
+	private int $currentTokenType;
 
-	/** @var int */
-	private $currentOffset;
+	private int $currentOffset;
 
-	/** @var int */
-	private $expectedTokenType;
+	private int $expectedTokenType;
 
-	/** @var string|null */
-	private $expectedTokenValue;
+	private ?string $expectedTokenValue = null;
 
-	/** @var int|null */
-	private $currentTokenLine;
+	private ?int $currentTokenLine = null;
 
 	public function __construct(
 		string $currentTokenValue,
@@ -54,7 +48,7 @@ public function __construct(
 			Lexer::TOKEN_LABELS[$expectedTokenType],
 			$expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '',
 			$currentOffset,
-			$currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine)
+			$currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine),
 		));
 	}
 
diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index d8bd5081..2b9053b0 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -29,23 +29,17 @@ class PhpDocParser
 		Lexer::TOKEN_INTERSECTION,
 	];
 
-	/** @var TypeParser */
-	private $typeParser;
+	private TypeParser $typeParser;
 
-	/** @var ConstExprParser */
-	private $constantExprParser;
+	private ConstExprParser $constantExprParser;
 
-	/** @var ConstExprParser */
-	private $doctrineConstantExprParser;
+	private ConstExprParser $doctrineConstantExprParser;
 
-	/** @var bool */
-	private $useLinesAttributes;
+	private bool $useLinesAttributes;
 
-	/** @var bool */
-	private $useIndexAttributes;
+	private bool $useIndexAttributes;
 
-	/** @var bool */
-	private $textBetweenTagsBelongsToDescription;
+	private bool $textBetweenTagsBelongsToDescription;
 
 	/**
 	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
@@ -126,8 +120,8 @@ public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
 					$tokens,
 					new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e),
 					$startLine,
-					$startIndex
-				)
+					$startIndex,
+				),
 			);
 
 			$tokens->forwardToTheEnd();
@@ -163,8 +157,8 @@ private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
 					$tokens,
 					$this->parseDoctrineTagValue($tokens, $tag),
 					$tagStartLine,
-					$tagStartIndex
-				)
+					$tagStartIndex,
+				),
 			), $startLine, $startIndex);
 		}
 
@@ -452,9 +446,7 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
 				case '@psalm-template-contravariant':
 					$tagValue = $this->typeParser->parseTemplateTagValue(
 						$tokens,
-						function ($tokens) {
-							return $this->parseOptionalDescription($tokens);
-						}
+						fn ($tokens) => $this->parseOptionalDescription($tokens),
 					);
 					break;
 
@@ -544,9 +536,9 @@ private function parseDoctrineTagValue(TokenIterator $tokens, string $tag): Ast\
 				$tokens,
 				new Doctrine\DoctrineAnnotation($tag, $this->parseDoctrineArguments($tokens, false)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			),
-			$this->parseOptionalDescriptionAfterDoctrineTag($tokens)
+			$this->parseOptionalDescriptionAfterDoctrineTag($tokens),
 		);
 	}
 
@@ -597,7 +589,7 @@ private function parseDoctrineArgument(TokenIterator $tokens): Doctrine\Doctrine
 				$tokens,
 				new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 
@@ -613,7 +605,7 @@ private function parseDoctrineArgument(TokenIterator $tokens): Doctrine\Doctrine
 				$tokens,
 				new IdentifierTypeNode($currentValue),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 			$tokens->consumeTokenType(Lexer::TOKEN_EQUAL);
 
@@ -625,7 +617,7 @@ private function parseDoctrineArgument(TokenIterator $tokens): Doctrine\Doctrine
 				$tokens,
 				new Doctrine\DoctrineArgument($key, $value),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		} catch (ParserException $e) {
 			$tokens->rollback();
@@ -634,7 +626,7 @@ private function parseDoctrineArgument(TokenIterator $tokens): Doctrine\Doctrine
 				$tokens,
 				new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 	}
@@ -656,7 +648,7 @@ private function parseDoctrineArgumentValue(TokenIterator $tokens)
 				$tokens,
 				new Doctrine\DoctrineAnnotation($name, $this->parseDoctrineArguments($tokens, true)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 
@@ -675,7 +667,7 @@ private function parseDoctrineArgumentValue(TokenIterator $tokens)
 				$tokens,
 				new Doctrine\DoctrineArray($items),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 
@@ -686,7 +678,7 @@ private function parseDoctrineArgumentValue(TokenIterator $tokens)
 				$tokens,
 				new Ast\Type\IdentifierTypeNode($currentTokenValue),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
 				$tokens->dropSavePoint();
@@ -712,7 +704,7 @@ private function parseDoctrineArgumentValue(TokenIterator $tokens)
 					$currentTokenOffset,
 					Lexer::TOKEN_IDENTIFIER,
 					null,
-					$currentTokenLine
+					$currentTokenLine,
 				);
 			}
 
@@ -724,7 +716,7 @@ private function parseDoctrineArgumentValue(TokenIterator $tokens)
 				$currentTokenOffset,
 				Lexer::TOKEN_IDENTIFIER,
 				null,
-				$currentTokenLine
+				$currentTokenLine,
 			);
 		}
 	}
@@ -753,7 +745,7 @@ private function parseDoctrineArrayItem(TokenIterator $tokens): Doctrine\Doctrin
 				$tokens,
 				new Doctrine\DoctrineArrayItem($key, $value),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		} catch (ParserException $e) {
 			$tokens->rollback();
@@ -762,7 +754,7 @@ private function parseDoctrineArrayItem(TokenIterator $tokens): Doctrine\Doctrin
 				$tokens,
 				new Doctrine\DoctrineArrayItem(null, $this->parseDoctrineArgumentValue($tokens)),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 	}
@@ -805,7 +797,7 @@ private function parseDoctrineArrayKey(TokenIterator $tokens)
 					$tokens->currentTokenOffset(),
 					Lexer::TOKEN_IDENTIFIER,
 					null,
-					$tokens->currentTokenLine()
+					$tokens->currentTokenLine(),
 				);
 			}
 
@@ -816,7 +808,7 @@ private function parseDoctrineArrayKey(TokenIterator $tokens)
 					$tokens,
 					new IdentifierTypeNode($currentTokenValue),
 					$startLine,
-					$startIndex
+					$startIndex,
 				);
 			}
 
@@ -829,7 +821,7 @@ private function parseDoctrineArrayKey(TokenIterator $tokens)
 					$tokens->currentTokenOffset(),
 					Lexer::TOKEN_IDENTIFIER,
 					null,
-					$tokens->currentTokenLine()
+					$tokens->currentTokenLine(),
 				);
 			}
 
@@ -993,7 +985,7 @@ private function parseMethodTagValue(TokenIterator $tokens): Ast\PhpDoc\MethodTa
 					$tokens,
 					$this->typeParser->parseTemplateTagValue($tokens),
 					$startLine,
-					$startIndex
+					$startIndex,
 				);
 			} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
 			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
@@ -1046,7 +1038,7 @@ private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc
 			$tokens,
 			new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue),
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
@@ -1059,7 +1051,7 @@ private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): A
 
 		$type = $this->typeParser->parseGeneric(
 			$tokens,
-			$this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex)
+			$this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex),
 		);
 
 		$description = $this->parseOptionalDescription($tokens);
@@ -1096,7 +1088,7 @@ private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeA
 						$tokens->currentTokenOffset(),
 						Lexer::TOKEN_PHPDOC_EOL,
 						null,
-						$tokens->currentTokenLine()
+						$tokens->currentTokenLine(),
 					);
 				}
 			}
@@ -1106,7 +1098,7 @@ private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeA
 			$this->parseOptionalDescription($tokens);
 			return new Ast\PhpDoc\TypeAliasTagValueNode(
 				$alias,
-				$this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex)
+				$this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex),
 			);
 		}
 	}
@@ -1126,7 +1118,7 @@ private function parseTypeAliasImportTagValue(TokenIterator $tokens): Ast\PhpDoc
 			$tokens,
 			new IdentifierTypeNode($importedFrom),
 			$identifierStartLine,
-			$identifierStartIndex
+			$identifierStartIndex,
 		);
 
 		$importedAs = null;
diff --git a/src/Parser/StringUnescaper.php b/src/Parser/StringUnescaper.php
index a3bbeedd..e8e0a3d6 100644
--- a/src/Parser/StringUnescaper.php
+++ b/src/Parser/StringUnescaper.php
@@ -31,7 +31,7 @@ public static function unescapeString(string $string): string
 			return str_replace(
 				['\\\\', '\\\''],
 				['\\', '\''],
-				substr($string, 1, -1)
+				substr($string, 1, -1),
 			);
 		}
 
@@ -65,7 +65,7 @@ static function ($matches) {
 
 				return chr((int) octdec($str));
 			},
-			$str
+			$str,
 		);
 	}
 
diff --git a/src/Parser/TokenIterator.php b/src/Parser/TokenIterator.php
index 9be7593d..87ded21a 100644
--- a/src/Parser/TokenIterator.php
+++ b/src/Parser/TokenIterator.php
@@ -15,19 +15,17 @@ class TokenIterator
 {
 
 	/** @var list<array{string, int, int}> */
-	private $tokens;
+	private array $tokens;
 
-	/** @var int */
-	private $index;
+	private int $index;
 
 	/** @var int[] */
-	private $savePoints = [];
+	private array $savePoints = [];
 
 	/** @var list<int> */
-	private $skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
+	private array $skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
 
-	/** @var string|null */
-	private $newline = null;
+	private ?string $newline = null;
 
 	/**
 	 * @param list<array{string, int, int}> $tokens
@@ -311,7 +309,7 @@ private function throwError(int $expectedTokenType, ?string $expectedTokenValue
 			$this->currentTokenOffset(),
 			$expectedTokenType,
 			$expectedTokenValue,
-			$this->currentTokenLine()
+			$this->currentTokenLine(),
 		);
 	}
 
diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index 0ad89956..a9e58833 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -15,14 +15,11 @@
 class TypeParser
 {
 
-	/** @var ConstExprParser */
-	private $constExprParser;
+	private ConstExprParser $constExprParser;
 
-	/** @var bool */
-	private $useLinesAttributes;
+	private bool $useLinesAttributes;
 
-	/** @var bool */
-	private $useIndexAttributes;
+	private bool $useIndexAttributes;
 
 	/**
 	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
@@ -184,7 +181,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 					if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
 						$type = $this->tryParseArrayOrOffsetAccess(
 							$tokens,
-							$this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)
+							$this->enrichWithAttributes($tokens, $type, $startLine, $startIndex),
 						);
 					}
 				}
@@ -211,7 +208,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 					$currentTokenOffset,
 					Lexer::TOKEN_IDENTIFIER,
 					null,
-					$currentTokenLine
+					$currentTokenLine,
 				);
 			}
 
@@ -219,7 +216,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 				$tokens,
 				new Ast\Type\ConstTypeNode($constExpr),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
 				$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
@@ -233,7 +230,7 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 				$currentTokenOffset,
 				Lexer::TOKEN_IDENTIFIER,
 				null,
-				$currentTokenLine
+				$currentTokenLine,
 			);
 		}
 	}
@@ -576,7 +573,7 @@ private function parseCallableTemplateArgument(TokenIterator $tokens): Ast\PhpDo
 			$tokens,
 			$this->parseTemplateTagValue($tokens),
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
@@ -603,7 +600,7 @@ private function parseCallableParameter(TokenIterator $tokens): Ast\Type\Callabl
 			$tokens,
 			new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional),
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
@@ -631,7 +628,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 					$tokens,
 					$type,
 					$startLine,
-					$startIndex
+					$startIndex,
 				));
 			}
 
@@ -650,15 +647,15 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 								$tokens,
 								$type,
 								$startLine,
-								$startIndex
-							)
+								$startIndex,
+							),
 						);
 						if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
 							$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
 								$tokens,
 								$type,
 								$startLine,
-								$startIndex
+								$startIndex,
 							));
 						}
 
@@ -667,7 +664,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 							$tokens,
 							$type,
 							$startLine,
-							$startIndex
+							$startIndex,
 						));
 
 					} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
@@ -678,7 +675,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 								$tokens,
 								$type,
 								$startLine,
-								$startIndex
+								$startIndex,
 							), $type->name);
 						}
 
@@ -687,7 +684,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 								$tokens,
 								$type,
 								$startLine,
-								$startIndex
+								$startIndex,
 							));
 						}
 					}
@@ -706,17 +703,6 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 		$currentTokenOffset = $tokens->currentTokenOffset();
 		$currentTokenLine = $tokens->currentTokenLine();
 
-		if ($this->constExprParser === null) {
-			throw new ParserException(
-				$currentTokenValue,
-				$currentTokenType,
-				$currentTokenOffset,
-				Lexer::TOKEN_IDENTIFIER,
-				null,
-				$currentTokenLine
-			);
-		}
-
 		try {
 			$constExpr = $this->constExprParser->parse($tokens);
 			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
@@ -726,7 +712,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 					$currentTokenOffset,
 					Lexer::TOKEN_IDENTIFIER,
 					null,
-					$currentTokenLine
+					$currentTokenLine,
 				);
 			}
 
@@ -734,7 +720,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 				$tokens,
 				new Ast\Type\ConstTypeNode($constExpr),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
 				$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
@@ -748,7 +734,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo
 				$currentTokenOffset,
 				Lexer::TOKEN_IDENTIFIER,
 				null,
-				$currentTokenLine
+				$currentTokenLine,
 			);
 		}
 	}
@@ -794,7 +780,7 @@ private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\Typ
 							$tokens,
 							$type,
 							$startLine,
-							$startIndex
+							$startIndex,
 						);
 					}
 				} else {
@@ -807,7 +793,7 @@ private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\Typ
 							$tokens,
 							$type,
 							$startLine,
-							$startIndex
+							$startIndex,
 						);
 					}
 				}
@@ -886,7 +872,7 @@ private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShape
 				$tokens,
 				new Ast\Type\ArrayShapeItemNode($key, $optional, $value),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		} catch (ParserException $e) {
 			$tokens->rollback();
@@ -896,7 +882,7 @@ private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShape
 				$tokens,
 				new Ast\Type\ArrayShapeItemNode(null, false, $value),
 				$startLine,
-				$startIndex
+				$startIndex,
 			);
 		}
 	}
@@ -932,7 +918,7 @@ private function parseArrayShapeKey(TokenIterator $tokens)
 			$tokens,
 			$key,
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
@@ -965,7 +951,7 @@ private function parseArrayShapeUnsealedType(TokenIterator $tokens): Ast\Type\Ar
 			$tokens,
 			new Ast\Type\ArrayShapeUnsealedTypeNode($valueType, $keyType),
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
@@ -989,7 +975,7 @@ private function parseListShapeUnsealedType(TokenIterator $tokens): Ast\Type\Arr
 			$tokens,
 			new Ast\Type\ArrayShapeUnsealedTypeNode($valueType, null),
 			$startLine,
-			$startIndex
+			$startIndex,
 		);
 	}
 
diff --git a/src/Printer/Differ.php b/src/Printer/Differ.php
index ab10be59..c60fd4ad 100644
--- a/src/Printer/Differ.php
+++ b/src/Printer/Differ.php
@@ -180,7 +180,7 @@ private function coalesceReplacements(array $diff): array
 					$newDiff[] = new DiffElem(
 						DiffElem::TYPE_REPLACE,
 						$diff[$i + $n]->old,
-						$diff[$j + $n]->new
+						$diff[$j + $n]->new,
 					);
 				}
 			} else {
diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php
index c4b9c356..915ab39b 100644
--- a/src/Printer/Printer.php
+++ b/src/Printer/Printer.php
@@ -88,7 +88,7 @@ final class Printer
 {
 
 	/** @var Differ<Node> */
-	private $differ;
+	private Differ $differ;
 
 	/**
 	 * Map From "{$class}->{$subNode}" to string that should be inserted
@@ -96,7 +96,7 @@ final class Printer
 	 *
 	 * @var array<string, string>
 	 */
-	private $listInsertionMap = [
+	private array $listInsertionMap = [
 		PhpDocNode::class . '->children' => "\n * ",
 		UnionTypeNode::class . '->types' => '|',
 		IntersectionTypeNode::class . '->types' => '&',
@@ -116,7 +116,7 @@ final class Printer
 	 *
 	 * @var array<string, array{string|null, string, string}>
 	 */
-	private $emptyListInsertionMap = [
+	private array $emptyListInsertionMap = [
 		CallableTypeNode::class . '->parameters' => ['(', '', ''],
 		ArrayShapeNode::class . '->items' => ['{', '', ''],
 		ObjectShapeNode::class . '->items' => ['{', '', ''],
@@ -125,7 +125,7 @@ final class Printer
 	];
 
 	/** @var array<string, list<class-string<TypeNode>>> */
-	private $parenthesesMap = [
+	private array $parenthesesMap = [
 		CallableTypeNode::class . '->returnType' => [
 			CallableTypeNode::class,
 			UnionTypeNode::class,
@@ -147,7 +147,7 @@ final class Printer
 	];
 
 	/** @var array<string, list<class-string<TypeNode>>> */
-	private $parenthesesListMap = [
+	private array $parenthesesListMap = [
 		IntersectionTypeNode::class . '->types' => [
 			IntersectionTypeNode::class,
 			UnionTypeNode::class,
@@ -177,7 +177,7 @@ public function printFormatPreserving(PhpDocNode $node, PhpDocNode $originalNode
 			$originalTokens,
 			$tokenIndex,
 			PhpDocNode::class,
-			'children'
+			'children',
 		);
 		if ($result !== null) {
 			return $result . $originalTokens->getContentBetween($tokenIndex, $originalTokens->getTokenCount());
@@ -194,7 +194,7 @@ function (PhpDocChildNode $child): string {
 					$s = $this->print($child);
 					return $s === '' ? '' : ' ' . $s;
 				},
-				$node->children
+				$node->children,
 			)) . "\n */";
 		}
 		if ($node instanceof PhpDocTextNode) {
@@ -282,13 +282,9 @@ private function printTagValue(PhpDocTagValueNode $node): string
 		if ($node instanceof MethodTagValueNode) {
 			$static = $node->isStatic ? 'static ' : '';
 			$returnType = $node->returnType !== null ? $this->printType($node->returnType) . ' ' : '';
-			$parameters = implode(', ', array_map(function (MethodTagValueParameterNode $parameter): string {
-				return $this->print($parameter);
-			}, $node->parameters));
+			$parameters = implode(', ', array_map(fn (MethodTagValueParameterNode $parameter): string => $this->print($parameter), $node->parameters));
 			$description = $node->description !== '' ? " {$node->description}" : '';
-			$templateTypes = count($node->templateTypes) > 0 ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateTag): string {
-				return $this->print($templateTag);
-			}, $node->templateTypes)) . '>' : '';
+			$templateTypes = count($node->templateTypes) > 0 ? '<' . implode(', ', array_map(fn (TemplateTagValueNode $templateTag): string => $this->print($templateTag), $node->templateTypes)) . '>' : '';
 			return "{$static}{$returnType}{$node->methodName}{$templateTypes}({$parameters}){$description}";
 		}
 		if ($node instanceof MixinTagValueNode) {
@@ -346,7 +342,7 @@ private function printTagValue(PhpDocTagValueNode $node): string
 		if ($node instanceof TypeAliasImportTagValueNode) {
 			return trim(
 				"{$node->importedAlias} from " . $this->printType($node->importedFrom)
-				. ($node->importedAs !== null ? " as {$node->importedAs}" : '')
+				. ($node->importedAs !== null ? " as {$node->importedAs}" : ''),
 			);
 		}
 		if ($node instanceof TypeAliasTagValueNode) {
@@ -368,9 +364,7 @@ private function printTagValue(PhpDocTagValueNode $node): string
 	private function printType(TypeNode $node): string
 	{
 		if ($node instanceof ArrayShapeNode) {
-			$items = array_map(function (ArrayShapeItemNode $item): string {
-				return $this->printType($item);
-			}, $node->items);
+			$items = array_map(fn (ArrayShapeItemNode $item): string => $this->printType($item), $node->items);
 
 			if (! $node->sealed) {
 				$items[] = '...' . ($node->unsealedType === null ? '' : $this->print($node->unsealedType));
@@ -384,7 +378,7 @@ private function printType(TypeNode $node): string
 					'%s%s: %s',
 					$this->print($node->keyName),
 					$node->optional ? '?' : '',
-					$this->printType($node->valueType)
+					$this->printType($node->valueType),
 				);
 			}
 
@@ -400,13 +394,9 @@ private function printType(TypeNode $node): string
 				$returnType = $this->printType($node->returnType);
 			}
 			$template = $node->templateTypes !== []
-				? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateNode): string {
-					return $this->print($templateNode);
-				}, $node->templateTypes)) . '>'
+				? '<' . implode(', ', array_map(fn (TemplateTagValueNode $templateNode): string => $this->print($templateNode), $node->templateTypes)) . '>'
 				: '';
-			$parameters = implode(', ', array_map(function (CallableTypeParameterNode $parameterNode): string {
-				return $this->print($parameterNode);
-			}, $node->parameters));
+			$parameters = implode(', ', array_map(fn (CallableTypeParameterNode $parameterNode): string => $this->print($parameterNode), $node->parameters));
 			return "{$node->identifier}{$template}({$parameters}): {$returnType}";
 		}
 		if ($node instanceof ConditionalTypeForParameterNode) {
@@ -416,7 +406,7 @@ private function printType(TypeNode $node): string
 				$node->negated ? 'is not' : 'is',
 				$this->printType($node->targetType),
 				$this->printType($node->if),
-				$this->printType($node->else)
+				$this->printType($node->else),
 			);
 		}
 		if ($node instanceof ConditionalTypeNode) {
@@ -426,7 +416,7 @@ private function printType(TypeNode $node): string
 				$node->negated ? 'is not' : 'is',
 				$this->printType($node->targetType),
 				$this->printType($node->if),
-				$this->printType($node->else)
+				$this->printType($node->else),
 			);
 		}
 		if ($node instanceof ConstTypeNode) {
@@ -479,9 +469,7 @@ private function printType(TypeNode $node): string
 			return '?' . $this->printType($node->type);
 		}
 		if ($node instanceof ObjectShapeNode) {
-			$items = array_map(function (ObjectShapeItemNode $item): string {
-				return $this->printType($item);
-			}, $node->items);
+			$items = array_map(fn (ObjectShapeItemNode $item): string => $this->printType($item), $node->items);
 
 			return 'object{' . implode(', ', $items) . '}';
 		}
@@ -491,7 +479,7 @@ private function printType(TypeNode $node): string
 					'%s%s: %s',
 					$this->print($node->keyName),
 					$node->optional ? '?' : '',
-					$this->printType($node->valueType)
+					$this->printType($node->valueType),
 				);
 			}
 
@@ -799,7 +787,7 @@ private function printNodeFormatPreserving(Node $node, TokenIterator $originalTo
 						$originalTokens,
 						$pos,
 						$class,
-						$subNodeName
+						$subNodeName,
 					);
 
 					if ($listResult === null) {
diff --git a/tests/PHPStan/Ast/Attributes/AttributesTest.php b/tests/PHPStan/Ast/Attributes/AttributesTest.php
index a20747ec..8be0d2f2 100644
--- a/tests/PHPStan/Ast/Attributes/AttributesTest.php
+++ b/tests/PHPStan/Ast/Attributes/AttributesTest.php
@@ -13,8 +13,7 @@
 final class AttributesTest extends TestCase
 {
 
-	/** @var PhpDocNode */
-	private $phpDocNode;
+	private PhpDocNode $phpDocNode;
 
 	protected function setUp(): void
 	{
diff --git a/tests/PHPStan/Ast/NodeTraverserTest.php b/tests/PHPStan/Ast/NodeTraverserTest.php
index 10ae5655..49f3646b 100644
--- a/tests/PHPStan/Ast/NodeTraverserTest.php
+++ b/tests/PHPStan/Ast/NodeTraverserTest.php
@@ -163,7 +163,7 @@ public function testMerge(): void
 
 		$this->assertEquals(
 			[$strStart, $strR1, $strR2, $strEnd],
-			$traverser->traverse([$strStart, $strMiddle, $strEnd])
+			$traverser->traverse([$strStart, $strMiddle, $strEnd]),
 		);
 	}
 
diff --git a/tests/PHPStan/Ast/NodeVisitorForTesting.php b/tests/PHPStan/Ast/NodeVisitorForTesting.php
index f3ca0ac0..fa5dd26d 100644
--- a/tests/PHPStan/Ast/NodeVisitorForTesting.php
+++ b/tests/PHPStan/Ast/NodeVisitorForTesting.php
@@ -15,13 +15,12 @@ class NodeVisitorForTesting implements NodeVisitor
 {
 
 	/** @var list<array{string, Node|Node[]}> */
-	public $trace = [];
+	public array $trace = [];
 
 	/** @var list<list<mixed>> */
-	private $returns;
+	private array $returns;
 
-	/** @var int */
-	private $returnsPos;
+	private int $returnsPos;
 
 	/**
 	 * @param list<list<mixed>> $returns
diff --git a/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php b/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
index f3b029bd..1d26181c 100644
--- a/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
@@ -137,7 +137,7 @@ public static function provideOtherCases(): Generator
 			'#desc',
 			new InvalidTagValueNode(
 				'#desc',
-				new ParserException('#desc', Lexer::TOKEN_OTHER, 11, Lexer::TOKEN_IDENTIFIER)
+				new ParserException('#desc', Lexer::TOKEN_OTHER, 11, Lexer::TOKEN_IDENTIFIER),
 			),
 		];
 
@@ -379,7 +379,7 @@ public static function provideDoctrineCases(): iterable
 			'@ORM\Entity()',
 			new PhpDocTagNode('@ORM\Entity', new DoctrineTagValueNode(
 				new DoctrineAnnotation('@ORM\Entity', []),
-				''
+				'',
 			)),
 		];
 
@@ -387,7 +387,7 @@ public static function provideDoctrineCases(): iterable
 			'@ORM\Entity() test',
 			new PhpDocTagNode('@ORM\Entity', new DoctrineTagValueNode(
 				new DoctrineAnnotation('@ORM\Entity', []),
-				'test'
+				'test',
 			)),
 		];
 
@@ -398,7 +398,7 @@ public static function provideDoctrineCases(): iterable
 					new DoctrineArgument(null, new ConstExprIntegerNode('1')),
 					new DoctrineArgument(new IdentifierTypeNode('b'), new ConstExprIntegerNode('2')),
 				]),
-				''
+				'',
 			),
 		];
 
diff --git a/tests/PHPStan/Ast/ToString/TypeToStringTest.php b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
index 1eba0e7d..4b6f7b2a 100644
--- a/tests/PHPStan/Ast/ToString/TypeToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
@@ -136,7 +136,7 @@ public static function provideGenericCases(): Generator
 				new GenericTypeNode(
 					new IdentifierTypeNode('array'),
 					[new IdentifierTypeNode('string'), new IdentifierTypeNode('int')],
-					[GenericTypeNode::VARIANCE_INVARIANT, GenericTypeNode::VARIANCE_BIVARIANT]
+					[GenericTypeNode::VARIANCE_INVARIANT, GenericTypeNode::VARIANCE_BIVARIANT],
 				),
 			],
 			[
@@ -144,7 +144,7 @@ public static function provideGenericCases(): Generator
 				new GenericTypeNode(
 					new IdentifierTypeNode('Foo\\Bar'),
 					[new IdentifierTypeNode('string'), new IdentifierTypeNode('int')],
-					[GenericTypeNode::VARIANCE_COVARIANT, GenericTypeNode::VARIANCE_CONTRAVARIANT]
+					[GenericTypeNode::VARIANCE_COVARIANT, GenericTypeNode::VARIANCE_CONTRAVARIANT],
 				),
 			],
 		];
@@ -160,7 +160,7 @@ public static function provideConditionalCases(): Generator
 					new IdentifierTypeNode('int'),
 					new GenericTypeNode(new IdentifierTypeNode('list'), [new IdentifierTypeNode('int')]),
 					new GenericTypeNode(new IdentifierTypeNode('list'), [new IdentifierTypeNode('string')]),
-					false
+					false,
 				),
 			],
 			[
@@ -170,7 +170,7 @@ public static function provideConditionalCases(): Generator
 					new IdentifierTypeNode('array'),
 					new IdentifierTypeNode('int'),
 					new ArrayTypeNode(new IdentifierTypeNode('int')),
-					true
+					true,
 				),
 			],
 			[
@@ -180,7 +180,7 @@ public static function provideConditionalCases(): Generator
 					new IdentifierTypeNode('Exception'),
 					new IdentifierTypeNode('never'),
 					new IdentifierTypeNode('string'),
-					false
+					false,
 				),
 			],
 			[
@@ -190,7 +190,7 @@ public static function provideConditionalCases(): Generator
 					new IdentifierTypeNode('Exception'),
 					new IdentifierTypeNode('string'),
 					new IdentifierTypeNode('never'),
-					true
+					true,
 				),
 			],
 		];
diff --git a/tests/PHPStan/Parser/ConstExprParserTest.php b/tests/PHPStan/Parser/ConstExprParserTest.php
index bb25af8f..5f7fc43f 100644
--- a/tests/PHPStan/Parser/ConstExprParserTest.php
+++ b/tests/PHPStan/Parser/ConstExprParserTest.php
@@ -21,11 +21,9 @@
 class ConstExprParserTest extends TestCase
 {
 
-	/** @var Lexer */
-	private $lexer;
+	private Lexer $lexer;
 
-	/** @var ConstExprParser */
-	private $constExprParser;
+	private ConstExprParser $constExprParser;
 
 	protected function setUp(): void
 	{
@@ -352,7 +350,7 @@ public function provideArrayNodeParseData(): Iterator
 			new ConstExprArrayNode([
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('123')
+					new ConstExprIntegerNode('123'),
 				),
 			]),
 		];
@@ -362,15 +360,15 @@ public function provideArrayNodeParseData(): Iterator
 			new ConstExprArrayNode([
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('1')
+					new ConstExprIntegerNode('1'),
 				),
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('2')
+					new ConstExprIntegerNode('2'),
 				),
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('3')
+					new ConstExprIntegerNode('3'),
 				),
 			]),
 		];
@@ -380,15 +378,15 @@ public function provideArrayNodeParseData(): Iterator
 			new ConstExprArrayNode([
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('1')
+					new ConstExprIntegerNode('1'),
 				),
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('2')
+					new ConstExprIntegerNode('2'),
 				),
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('3')
+					new ConstExprIntegerNode('3'),
 				),
 			]),
 		];
@@ -398,7 +396,7 @@ public function provideArrayNodeParseData(): Iterator
 			new ConstExprArrayNode([
 				new ConstExprArrayItemNode(
 					new ConstExprIntegerNode('1'),
-					new ConstExprIntegerNode('2')
+					new ConstExprIntegerNode('2'),
 				),
 			]),
 		];
@@ -408,11 +406,11 @@ public function provideArrayNodeParseData(): Iterator
 			new ConstExprArrayNode([
 				new ConstExprArrayItemNode(
 					new ConstExprIntegerNode('1'),
-					new ConstExprIntegerNode('2')
+					new ConstExprIntegerNode('2'),
 				),
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('3')
+					new ConstExprIntegerNode('3'),
 				),
 			]),
 		];
@@ -422,20 +420,20 @@ public function provideArrayNodeParseData(): Iterator
 			new ConstExprArrayNode([
 				new ConstExprArrayItemNode(
 					null,
-					new ConstExprIntegerNode('1')
+					new ConstExprIntegerNode('1'),
 				),
 				new ConstExprArrayItemNode(
 					null,
 					new ConstExprArrayNode([
 						new ConstExprArrayItemNode(
 							null,
-							new ConstExprIntegerNode('2')
+							new ConstExprIntegerNode('2'),
 						),
 						new ConstExprArrayItemNode(
 							null,
-							new ConstExprIntegerNode('3')
+							new ConstExprIntegerNode('3'),
 						),
-					])
+					]),
 				),
 			]),
 		];
diff --git a/tests/PHPStan/Parser/Doctrine/ApiResource.php b/tests/PHPStan/Parser/Doctrine/ApiResource.php
index 21a958c5..9134705f 100644
--- a/tests/PHPStan/Parser/Doctrine/ApiResource.php
+++ b/tests/PHPStan/Parser/Doctrine/ApiResource.php
@@ -13,22 +13,16 @@
 final class ApiResource
 {
 
-	/** @var string */
-	public $shortName;
+	public string $shortName;
 
-	/** @var string */
-	public $description;
+	public string $description;
 
-	/** @var string */
-	public $iri;
+	public string $iri;
 
-	/** @var array */
-	public $itemOperations;
+	public array $itemOperations;
 
-	/** @var array */
-	public $collectionOperations;
+	public array $collectionOperations;
 
-	/** @var array */
-	public $attributes = [];
+	public array $attributes = [];
 
 }
diff --git a/tests/PHPStan/Parser/FuzzyTest.php b/tests/PHPStan/Parser/FuzzyTest.php
index 336083b2..d3c3ed7e 100644
--- a/tests/PHPStan/Parser/FuzzyTest.php
+++ b/tests/PHPStan/Parser/FuzzyTest.php
@@ -19,14 +19,11 @@
 class FuzzyTest extends TestCase
 {
 
-	/** @var Lexer */
-	private $lexer;
+	private Lexer $lexer;
 
-	/** @var TypeParser */
-	private $typeParser;
+	private TypeParser $typeParser;
 
-	/** @var ConstExprParser */
-	private $constExprParser;
+	private ConstExprParser $constExprParser;
 
 	protected function setUp(): void
 	{
@@ -47,7 +44,7 @@ public function testTypeParser(string $input): void
 		$this->assertSame(
 			Lexer::TOKEN_END,
 			$tokens->currentTokenType(),
-			sprintf('Failed to parse input %s', $input)
+			sprintf('Failed to parse input %s', $input),
 		);
 	}
 
@@ -67,7 +64,7 @@ public function testConstExprParser(string $input): void
 		$this->assertSame(
 			Lexer::TOKEN_END,
 			$tokens->currentTokenType(),
-			sprintf('Failed to parse input %s', $input)
+			sprintf('Failed to parse input %s', $input),
 		);
 	}
 
diff --git a/tests/PHPStan/Parser/NodeCollectingVisitor.php b/tests/PHPStan/Parser/NodeCollectingVisitor.php
index aa01e559..dbce9dfc 100644
--- a/tests/PHPStan/Parser/NodeCollectingVisitor.php
+++ b/tests/PHPStan/Parser/NodeCollectingVisitor.php
@@ -9,7 +9,7 @@ class NodeCollectingVisitor extends AbstractNodeVisitor
 {
 
 	/** @var list<Node> */
-	public $nodes = [];
+	public array $nodes = [];
 
 	public function enterNode(Node $node)
 	{
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 588d8a8a..c221ecc6 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -72,11 +72,9 @@
 class PhpDocParserTest extends TestCase
 {
 
-	/** @var Lexer */
-	private $lexer;
+	private Lexer $lexer;
 
-	/** @var PhpDocParser */
-	private $phpDocParser;
+	private PhpDocParser $phpDocParser;
 
 	protected function setUp(): void
 	{
@@ -131,7 +129,7 @@ public function testParse(
 			$this->phpDocParser,
 			$label,
 			$input,
-			$expectedPhpDocNode
+			$expectedPhpDocNode,
 		);
 	}
 
@@ -159,8 +157,8 @@ public function provideParamTagsData(): Iterator
 						new IdentifierTypeNode('Foo'),
 						false,
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -175,8 +173,8 @@ public function provideParamTagsData(): Iterator
 						new IdentifierTypeNode('Foo'),
 						false,
 						'$foo',
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -191,8 +189,8 @@ public function provideParamTagsData(): Iterator
 						new IdentifierTypeNode('Foo'),
 						true,
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -207,8 +205,8 @@ public function provideParamTagsData(): Iterator
 						new IdentifierTypeNode('Foo'),
 						true,
 						'$foo',
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -224,8 +222,8 @@ public function provideParamTagsData(): Iterator
 						false,
 						'$foo',
 						'',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -241,8 +239,8 @@ public function provideParamTagsData(): Iterator
 						false,
 						'$foo',
 						'optional description',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -258,8 +256,8 @@ public function provideParamTagsData(): Iterator
 						true,
 						'$foo',
 						'',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -275,8 +273,8 @@ public function provideParamTagsData(): Iterator
 						true,
 						'$foo',
 						'optional description',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -291,8 +289,8 @@ public function provideParamTagsData(): Iterator
 						new ConstTypeNode(new ConstFetchNode('self', '*')),
 						false,
 						'$foo',
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -311,9 +309,9 @@ public function provideParamTagsData(): Iterator
 							11,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -332,9 +330,9 @@ public function provideParamTagsData(): Iterator
 							11,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -353,9 +351,9 @@ public function provideParamTagsData(): Iterator
 							16,
 							Lexer::TOKEN_CLOSE_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -374,9 +372,9 @@ public function provideParamTagsData(): Iterator
 							16,
 							Lexer::TOKEN_CLOSE_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -395,9 +393,9 @@ public function provideParamTagsData(): Iterator
 							19,
 							Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -416,9 +414,9 @@ public function provideParamTagsData(): Iterator
 							16,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -437,9 +435,9 @@ public function provideParamTagsData(): Iterator
 							15,
 							Lexer::TOKEN_VARIABLE,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -460,9 +458,9 @@ public function provideParamTagsData(): Iterator
 							21,
 							Lexer::TOKEN_VARIABLE,
 							null,
-							2
-						)
-					)
+							2,
+						),
+					),
 				),
 			]),
 		];
@@ -481,9 +479,9 @@ public function provideParamTagsData(): Iterator
 							15,
 							Lexer::TOKEN_VARIABLE,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -508,8 +506,8 @@ public function provideParamTagsData(): Iterator
 						false,
 						'$parameters',
 						'{' . PHP_EOL .
-						'    Optional. Parameters for filtering the list of user assignments. Default empty array.'
-					)
+						'    Optional. Parameters for filtering the list of user assignments. Default empty array.',
+					),
 				),
 				new PhpDocTextNode(''),
 				new PhpDocTagNode('@type', new GenericTagValueNode('bool $is_active                Pass `true` to only return active user assignments and `false` to' . PHP_EOL .
@@ -532,8 +530,8 @@ public function provideTypelessParamTagsData(): Iterator
 					new TypelessParamTagValueNode(
 						false,
 						'$foo',
-						'description'
-					)
+						'description',
+					),
 				),
 			]),
 		];
@@ -548,8 +546,8 @@ public function provideTypelessParamTagsData(): Iterator
 						false,
 						'$foo',
 						'description',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -563,8 +561,8 @@ public function provideTypelessParamTagsData(): Iterator
 					new TypelessParamTagValueNode(
 						true,
 						'$foo',
-						'description'
-					)
+						'description',
+					),
 				),
 			]),
 		];
@@ -579,8 +577,8 @@ public function provideTypelessParamTagsData(): Iterator
 						true,
 						'$foo',
 						'description',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -595,8 +593,8 @@ public function provideTypelessParamTagsData(): Iterator
 						false,
 						'$foo',
 						'',
-						false
-					)
+						false,
+					),
 				),
 			]),
 		];
@@ -612,8 +610,8 @@ public function provideParamImmediatelyInvokedCallableTagsData(): Iterator
 					'@param-immediately-invoked-callable',
 					new ParamImmediatelyInvokedCallableTagValueNode(
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -626,8 +624,8 @@ public function provideParamImmediatelyInvokedCallableTagsData(): Iterator
 					'@param-immediately-invoked-callable',
 					new ParamImmediatelyInvokedCallableTagValueNode(
 						'$foo',
-						'test two three'
-					)
+						'test two three',
+					),
 				),
 			]),
 		];
@@ -643,8 +641,8 @@ public function provideParamLaterInvokedCallableTagsData(): Iterator
 					'@param-later-invoked-callable',
 					new ParamLaterInvokedCallableTagValueNode(
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -657,8 +655,8 @@ public function provideParamLaterInvokedCallableTagsData(): Iterator
 					'@param-later-invoked-callable',
 					new ParamLaterInvokedCallableTagValueNode(
 						'$foo',
-						'test two three'
-					)
+						'test two three',
+					),
 				),
 			]),
 		];
@@ -675,8 +673,8 @@ public function provideParamClosureThisTagsData(): Iterator
 					new ParamClosureThisTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$a',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -690,8 +688,8 @@ public function provideParamClosureThisTagsData(): Iterator
 					new ParamClosureThisTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$a',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -705,8 +703,8 @@ public function provideParamClosureThisTagsData(): Iterator
 					new ParamClosureThisTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$a',
-						'test'
-					)
+						'test',
+					),
 				),
 			]),
 		];
@@ -723,8 +721,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -738,8 +736,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -753,8 +751,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -768,8 +766,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'',
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -782,11 +780,11 @@ public function provideVarTagsData(): Iterator
 					'@var',
 					new VarTagValueNode(
 						new ArrayTypeNode(
-							new IdentifierTypeNode('callable')
+							new IdentifierTypeNode('callable'),
 						),
 						'',
-						'function (Configurator $sender, DI\Compiler $compiler); Occurs after the compiler is created'
-					)
+						'function (Configurator $sender, DI\Compiler $compiler); Occurs after the compiler is created',
+					),
 				),
 			]),
 		];
@@ -800,8 +798,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'',
-						'@inject'
-					)
+						'@inject',
+					),
 				),
 			]),
 		];
@@ -815,8 +813,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'',
-						'(Bar)'
-					)
+						'(Bar)',
+					),
 				),
 			]),
 		];
@@ -830,8 +828,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$foo',
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -845,8 +843,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$this',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -860,8 +858,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$this',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -875,8 +873,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$this',
-						'Testing'
-					)
+						'Testing',
+					),
 				),
 			]),
 		];
@@ -890,8 +888,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$this',
-						'Testing'
-					)
+						'Testing',
+					),
 				),
 			]),
 		];
@@ -905,8 +903,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$foo',
-						'#desc'
-					)
+						'#desc',
+					),
 				),
 			]),
 		];
@@ -920,8 +918,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new ConstTypeNode(new ConstFetchNode('self', '*')),
 						'$foo',
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -935,8 +933,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$var',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -950,8 +948,8 @@ public function provideVarTagsData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -970,9 +968,9 @@ public function provideVarTagsData(): Iterator
 							9,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -991,9 +989,9 @@ public function provideVarTagsData(): Iterator
 							9,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1012,9 +1010,9 @@ public function provideVarTagsData(): Iterator
 							14,
 							Lexer::TOKEN_CLOSE_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1033,9 +1031,9 @@ public function provideVarTagsData(): Iterator
 							14,
 							Lexer::TOKEN_CLOSE_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1054,9 +1052,9 @@ public function provideVarTagsData(): Iterator
 							17,
 							Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1075,9 +1073,9 @@ public function provideVarTagsData(): Iterator
 							14,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1097,10 +1095,10 @@ public function provideVarTagsData(): Iterator
 								46,
 								Lexer::TOKEN_PHPDOC_EOL,
 								null,
-								1
-							)
-						)
-					)
+								1,
+							),
+						),
+					),
 				),
 			]),
 		];
@@ -1118,8 +1116,8 @@ public function providePropertyTagsData(): Iterator
 					new PropertyTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1133,8 +1131,8 @@ public function providePropertyTagsData(): Iterator
 					new PropertyTagValueNode(
 						new IdentifierTypeNode('Foo'),
 						'$foo',
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -1153,9 +1151,9 @@ public function providePropertyTagsData(): Iterator
 							14,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1174,9 +1172,9 @@ public function providePropertyTagsData(): Iterator
 							14,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1195,9 +1193,9 @@ public function providePropertyTagsData(): Iterator
 							19,
 							Lexer::TOKEN_CLOSE_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1216,9 +1214,9 @@ public function providePropertyTagsData(): Iterator
 							19,
 							Lexer::TOKEN_CLOSE_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1237,9 +1235,9 @@ public function providePropertyTagsData(): Iterator
 							22,
 							Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1258,9 +1256,9 @@ public function providePropertyTagsData(): Iterator
 							19,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1279,9 +1277,9 @@ public function providePropertyTagsData(): Iterator
 							18,
 							Lexer::TOKEN_VARIABLE,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1300,9 +1298,9 @@ public function providePropertyTagsData(): Iterator
 							18,
 							Lexer::TOKEN_VARIABLE,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1319,8 +1317,8 @@ public function provideReturnTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1333,8 +1331,8 @@ public function provideReturnTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -1347,8 +1345,8 @@ public function provideReturnTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'[Bar]'
-					)
+						'[Bar]',
+					),
 				),
 			]),
 		];
@@ -1362,10 +1360,10 @@ public function provideReturnTagsData(): Iterator
 					new ReturnTagValueNode(
 						new OffsetAccessTypeNode(
 							new IdentifierTypeNode('Foo'),
-							new IdentifierTypeNode('Bar')
+							new IdentifierTypeNode('Bar'),
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1378,8 +1376,8 @@ public function provideReturnTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('MongoCollection'),
-						'<p>Returns a collection object representing the new collection.</p>'
-					)
+						'<p>Returns a collection object representing the new collection.</p>',
+					),
 				),
 			]),
 		];
@@ -1398,9 +1396,9 @@ public function provideReturnTagsData(): Iterator
 							12,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1419,9 +1417,9 @@ public function provideReturnTagsData(): Iterator
 							12,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1440,9 +1438,9 @@ public function provideReturnTagsData(): Iterator
 							18,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1461,9 +1459,9 @@ public function provideReturnTagsData(): Iterator
 							18,
 							Lexer::TOKEN_OTHER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1482,9 +1480,9 @@ public function provideReturnTagsData(): Iterator
 							18,
 							Lexer::TOKEN_OTHER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1503,9 +1501,9 @@ public function provideReturnTagsData(): Iterator
 							24,
 							Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1526,11 +1524,11 @@ public function provideReturnTagsData(): Iterator
 								],
 								[
 									GenericTypeNode::VARIANCE_INVARIANT,
-								]
+								],
 							),
 						]),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1543,8 +1541,8 @@ public function provideReturnTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new ConstTypeNode(new ConstFetchNode('self', '*')),
-						'example description'
-					)
+						'example description',
+					),
 				),
 			]),
 		];
@@ -1561,10 +1559,10 @@ public function provideReturnTagsData(): Iterator
 							new IdentifierTypeNode('Bar'),
 							new IdentifierTypeNode('never'),
 							new IdentifierTypeNode('int'),
-							false
+							false,
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1581,10 +1579,10 @@ public function provideReturnTagsData(): Iterator
 							new IdentifierTypeNode('Bar'),
 							new IdentifierTypeNode('never'),
 							new IdentifierTypeNode('int'),
-							true
+							true,
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1607,12 +1605,12 @@ public function provideReturnTagsData(): Iterator
 								new ConstTypeNode(new ConstFetchNode('self', 'TYPE_INT')),
 								new IdentifierTypeNode('int'),
 								new IdentifierTypeNode('bool'),
-								false
+								false,
 							),
-							false
+							false,
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1625,8 +1623,8 @@ public function provideReturnTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'is not Bar ? never : int'
-					)
+						'is not Bar ? never : int',
+					),
 				),
 			]),
 		];
@@ -1643,10 +1641,10 @@ public function provideReturnTagsData(): Iterator
 							new IdentifierTypeNode('Bar'),
 							new IdentifierTypeNode('never'),
 							new IdentifierTypeNode('int'),
-							false
+							false,
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1663,10 +1661,10 @@ public function provideReturnTagsData(): Iterator
 							new IdentifierTypeNode('Bar'),
 							new IdentifierTypeNode('never'),
 							new IdentifierTypeNode('int'),
-							true
+							true,
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1689,12 +1687,12 @@ public function provideReturnTagsData(): Iterator
 								new ConstTypeNode(new ConstFetchNode('self', 'TYPE_INT')),
 								new IdentifierTypeNode('int'),
 								new IdentifierTypeNode('bool'),
-								false
+								false,
 							),
-							false
+							false,
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1713,9 +1711,9 @@ public function provideReturnTagsData(): Iterator
 							12,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1735,20 +1733,20 @@ public function provideReturnTagsData(): Iterator
 									false,
 									true,
 									'$u',
-									false
+									false,
 								),
 								new CallableTypeParameterNode(
 									new IdentifierTypeNode('string'),
 									false,
 									false,
 									'',
-									false
+									false,
 								),
 							],
-							new IdentifierTypeNode('string')
+							new IdentifierTypeNode('string'),
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1767,9 +1765,9 @@ public function provideReturnTagsData(): Iterator
 							20,
 							Lexer::TOKEN_HORIZONTAL_WS,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1786,8 +1784,8 @@ public function provideThrowsTagsData(): Iterator
 					'@throws',
 					new ThrowsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1800,8 +1798,8 @@ public function provideThrowsTagsData(): Iterator
 					'@throws',
 					new ThrowsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -1814,8 +1812,8 @@ public function provideThrowsTagsData(): Iterator
 					'@throws',
 					new ThrowsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'[Bar]'
-					)
+						'[Bar]',
+					),
 				),
 			]),
 		];
@@ -1834,9 +1832,9 @@ public function provideThrowsTagsData(): Iterator
 							12,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1855,9 +1853,9 @@ public function provideThrowsTagsData(): Iterator
 							18,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1873,8 +1871,8 @@ public function provideMixinTagsData(): Iterator
 					'@mixin',
 					new MixinTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1887,8 +1885,8 @@ public function provideMixinTagsData(): Iterator
 					'@mixin',
 					new MixinTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -1901,8 +1899,8 @@ public function provideMixinTagsData(): Iterator
 					'@mixin',
 					new MixinTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'[Bar]'
-					)
+						'[Bar]',
+					),
 				),
 			]),
 		];
@@ -1921,9 +1919,9 @@ public function provideMixinTagsData(): Iterator
 							11,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1942,9 +1940,9 @@ public function provideMixinTagsData(): Iterator
 							17,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -1961,8 +1959,8 @@ public function provideMixinTagsData(): Iterator
 						], [
 							GenericTypeNode::VARIANCE_INVARIANT,
 						]),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1978,8 +1976,8 @@ public function provideRequireExtendsTagsData(): Iterator
 					'@phpstan-require-extends',
 					new RequireExtendsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -1992,8 +1990,8 @@ public function provideRequireExtendsTagsData(): Iterator
 					'@phpstan-require-extends',
 					new RequireExtendsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -2006,8 +2004,8 @@ public function provideRequireExtendsTagsData(): Iterator
 					'@psalm-require-extends',
 					new RequireExtendsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -2026,9 +2024,9 @@ public function provideRequireExtendsTagsData(): Iterator
 							29,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -2044,8 +2042,8 @@ public function provideRequireImplementsTagsData(): Iterator
 					'@phpstan-require-implements',
 					new RequireImplementsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2058,8 +2056,8 @@ public function provideRequireImplementsTagsData(): Iterator
 					'@phpstan-require-implements',
 					new RequireImplementsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -2072,8 +2070,8 @@ public function provideRequireImplementsTagsData(): Iterator
 					'@psalm-require-implements',
 					new RequireImplementsTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -2092,9 +2090,9 @@ public function provideRequireImplementsTagsData(): Iterator
 							32,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -2108,7 +2106,7 @@ public function provideDeprecatedTagsData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@deprecated',
-					new DeprecatedTagValueNode('')
+					new DeprecatedTagValueNode(''),
 				),
 			]),
 		];
@@ -2119,7 +2117,7 @@ public function provideDeprecatedTagsData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@deprecated',
-					new DeprecatedTagValueNode('text string')
+					new DeprecatedTagValueNode('text string'),
 				),
 			]),
 		];
@@ -2132,12 +2130,12 @@ public function provideDeprecatedTagsData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@deprecated',
-					new DeprecatedTagValueNode('text first')
+					new DeprecatedTagValueNode('text first'),
 				),
 				new PhpDocTextNode(''),
 				new PhpDocTagNode(
 					'@deprecated',
-					new DeprecatedTagValueNode('text second')
+					new DeprecatedTagValueNode('text second'),
 				),
 			]),
 		];
@@ -2150,11 +2148,11 @@ public function provideDeprecatedTagsData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@deprecated',
-					new DeprecatedTagValueNode('text first')
+					new DeprecatedTagValueNode('text first'),
 				),
 				new PhpDocTagNode(
 					'@deprecated',
-					new DeprecatedTagValueNode('text second')
+					new DeprecatedTagValueNode('text second'),
 				),
 			]),
 		];
@@ -2171,7 +2169,7 @@ public function provideDeprecatedTagsData(): Iterator
 					new DeprecatedTagValueNode('in Drupal 8.6.0 and will be removed before Drupal 9.0.0. In
   Drupal 9 there will be no way to set the status and in Drupal 8 this
   ability has been removed because mb_*() functions are supplied using
-  Symfony\'s polyfill.')
+  Symfony\'s polyfill.'),
 				),
 			]),
 		];
@@ -2192,7 +2190,7 @@ public function provideDeprecatedTagsData(): Iterator
 				new PhpDocTextNode(''),
 				new PhpDocTagNode(
 					'@author',
-					new GenericTagValueNode('Foo Baz <foo@baz.com>')
+					new GenericTagValueNode('Foo Baz <foo@baz.com>'),
 				),
 				new PhpDocTextNode(''),
 				new PhpDocTagNode(
@@ -2200,7 +2198,7 @@ public function provideDeprecatedTagsData(): Iterator
 					new DeprecatedTagValueNode('in Drupal 8.6.0 and will be removed before Drupal 9.0.0. In
   Drupal 9 there will be no way to set the status and in Drupal 8 this
   ability has been removed because mb_*() functions are supplied using
-  Symfony\'s polyfill.')
+  Symfony\'s polyfill.'),
 				),
 			]),
 		];
@@ -2219,8 +2217,8 @@ public function provideMethodTagsData(): Iterator
 						null,
 						'foo',
 						[],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2236,8 +2234,8 @@ public function provideMethodTagsData(): Iterator
 						new IdentifierTypeNode('Foo'),
 						'foo',
 						[],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2253,8 +2251,8 @@ public function provideMethodTagsData(): Iterator
 						new IdentifierTypeNode('static'),
 						'foo',
 						[],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2270,8 +2268,8 @@ public function provideMethodTagsData(): Iterator
 						new IdentifierTypeNode('Foo'),
 						'foo',
 						[],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2287,8 +2285,8 @@ public function provideMethodTagsData(): Iterator
 						new IdentifierTypeNode('static'),
 						'foo',
 						[],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2304,8 +2302,8 @@ public function provideMethodTagsData(): Iterator
 						new IdentifierTypeNode('Foo'),
 						'foo',
 						[],
-						'optional description'
-					)
+						'optional description',
+					),
 				),
 			]),
 		];
@@ -2326,11 +2324,11 @@ public function provideMethodTagsData(): Iterator
 								false,
 								false,
 								'$a',
-								null
+								null,
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2351,11 +2349,11 @@ public function provideMethodTagsData(): Iterator
 								false,
 								false,
 								'$a',
-								null
+								null,
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2376,11 +2374,11 @@ public function provideMethodTagsData(): Iterator
 								true,
 								false,
 								'$a',
-								null
+								null,
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2401,11 +2399,11 @@ public function provideMethodTagsData(): Iterator
 								false,
 								true,
 								'$a',
-								null
+								null,
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2426,11 +2424,11 @@ public function provideMethodTagsData(): Iterator
 								true,
 								true,
 								'$a',
-								null
+								null,
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2451,11 +2449,11 @@ public function provideMethodTagsData(): Iterator
 								false,
 								false,
 								'$a',
-								new ConstExprIntegerNode('123')
+								new ConstExprIntegerNode('123'),
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2476,11 +2474,11 @@ public function provideMethodTagsData(): Iterator
 								true,
 								true,
 								'$a',
-								new ConstExprIntegerNode('123')
+								new ConstExprIntegerNode('123'),
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2501,25 +2499,25 @@ public function provideMethodTagsData(): Iterator
 								false,
 								false,
 								'$a',
-								null
+								null,
 							),
 							new MethodTagValueParameterNode(
 								null,
 								false,
 								false,
 								'$b',
-								null
+								null,
 							),
 							new MethodTagValueParameterNode(
 								null,
 								false,
 								false,
 								'$c',
-								null
+								null,
 							),
 						],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2538,9 +2536,9 @@ public function provideMethodTagsData(): Iterator
 							16,
 							Lexer::TOKEN_OPEN_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -2559,9 +2557,9 @@ public function provideMethodTagsData(): Iterator
 							23,
 							Lexer::TOKEN_OPEN_PARENTHESES,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -2580,9 +2578,9 @@ public function provideMethodTagsData(): Iterator
 							17,
 							Lexer::TOKEN_VARIABLE,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -2608,7 +2606,7 @@ public function provideMethodTagsData(): Iterator
 									[
 										GenericTypeNode::VARIANCE_INVARIANT,
 										GenericTypeNode::VARIANCE_INVARIANT,
-									]
+									],
 								),
 								false,
 								false,
@@ -2616,13 +2614,13 @@ public function provideMethodTagsData(): Iterator
 								new ConstExprArrayNode([
 									new ConstExprArrayItemNode(
 										null,
-										new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED)
+										new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED),
 									),
 									new ConstExprArrayItemNode(
 										null,
-										new ConstExprStringNode('b', ConstExprStringNode::SINGLE_QUOTED)
+										new ConstExprStringNode('b', ConstExprStringNode::SINGLE_QUOTED),
 									),
-								])
+								]),
 							),
 						],
 						'',
@@ -2631,10 +2629,10 @@ public function provideMethodTagsData(): Iterator
 								'T',
 								null,
 								'',
-								new IdentifierTypeNode('string')
+								new IdentifierTypeNode('string'),
 							),
-						]
-					)
+						],
+					),
 				),
 			]),
 		];
@@ -2655,21 +2653,21 @@ public function provideMethodTagsData(): Iterator
 								false,
 								false,
 								'$t1',
-								null
+								null,
 							),
 							new MethodTagValueParameterNode(
 								new IdentifierTypeNode('T2'),
 								false,
 								false,
 								'$t2',
-								null
+								null,
 							),
 							new MethodTagValueParameterNode(
 								new IdentifierTypeNode('T3'),
 								false,
 								false,
 								'$t3',
-								null
+								null,
 							),
 						],
 						'',
@@ -2677,8 +2675,8 @@ public function provideMethodTagsData(): Iterator
 							new TemplateTagValueNode('T1', null, ''),
 							new TemplateTagValueNode('T2', new IdentifierTypeNode('Bar'), ''),
 							new TemplateTagValueNode('T3', new IdentifierTypeNode('Baz'), ''),
-						]
-					)
+						],
+					),
 				),
 			]),
 		];
@@ -2697,8 +2695,8 @@ public function provideMethodTagsData(): Iterator
 						]),
 						'foo',
 						[],
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2718,7 +2716,7 @@ public function provideSingleLinePhpDocData(): Iterator
 			'/** /**/',
 			new PhpDocNode([
 				new PhpDocTextNode(
-					'/*'
+					'/*',
 				),
 			]),
 		];
@@ -2728,7 +2726,7 @@ public function provideSingleLinePhpDocData(): Iterator
 			'/** text */',
 			new PhpDocNode([
 				new PhpDocTextNode(
-					'text'
+					'text',
 				),
 			]),
 		];
@@ -2738,7 +2736,7 @@ public function provideSingleLinePhpDocData(): Iterator
 			'/** text @foo bar */',
 			new PhpDocNode([
 				new PhpDocTextNode(
-					'text @foo bar'
+					'text @foo bar',
 				),
 			]),
 		];
@@ -2749,7 +2747,7 @@ public function provideSingleLinePhpDocData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@foo',
-					new GenericTagValueNode('')
+					new GenericTagValueNode(''),
 				),
 			]),
 		];
@@ -2760,7 +2758,7 @@ public function provideSingleLinePhpDocData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@foo',
-					new GenericTagValueNode('lorem ipsum')
+					new GenericTagValueNode('lorem ipsum'),
 				),
 			]),
 		];
@@ -2771,11 +2769,11 @@ public function provideSingleLinePhpDocData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@foo',
-					new GenericTagValueNode('lorem')
+					new GenericTagValueNode('lorem'),
 				),
 				new PhpDocTagNode(
 					'@bar',
-					new GenericTagValueNode('ipsum')
+					new GenericTagValueNode('ipsum'),
 				),
 			]),
 		];
@@ -2787,8 +2785,8 @@ public function provideSingleLinePhpDocData(): Iterator
 				new PhpDocTagNode(
 					'@varFoo',
 					new GenericTagValueNode(
-						'$foo'
-					)
+						'$foo',
+					),
 				),
 			]),
 		];
@@ -2803,11 +2801,11 @@ public function provideSingleLinePhpDocData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@example',
-					new GenericTagValueNode('')
+					new GenericTagValueNode(''),
 				),
 				new PhpDocTextNode(
 					'entity_managers:' . PHP_EOL .
-					'    default:'
+					'    default:',
 				),
 			]),
 		];
@@ -2824,11 +2822,11 @@ public function provideSingleLinePhpDocData(): Iterator
 							[
 								new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '', false),
 							],
-							new IdentifierTypeNode('void')
+							new IdentifierTypeNode('void'),
 						),
 						'',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -2842,8 +2840,8 @@ public function provideSingleLinePhpDocData(): Iterator
 					new VarTagValueNode(
 						new IdentifierTypeNode('callable'),
 						'',
-						'(int)'
-					)
+						'(int)',
+					),
 				),
 			]),
 		];
@@ -2862,9 +2860,9 @@ public function provideSingleLinePhpDocData(): Iterator
 							17,
 							Lexer::TOKEN_HORIZONTAL_WS,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -2889,8 +2887,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Foo'),
 							false,
 							'$foo',
-							'1st multi world description'
-						)
+							'1st multi world description',
+						),
 					),
 					new PhpDocTagNode(
 						'@param',
@@ -2898,8 +2896,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Bar'),
 							false,
 							'$bar',
-							'2nd multi world description'
-						)
+							'2nd multi world description',
+						),
 					),
 				]),
 			],
@@ -2918,8 +2916,8 @@ public function provideMultiLinePhpDocData(): iterable
 							false,
 							'$foo',
 							'1st multi world description
-some text in the middle'
-						)
+some text in the middle',
+						),
 					),
 					new PhpDocTagNode(
 						'@param',
@@ -2927,8 +2925,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Bar'),
 							false,
 							'$bar',
-							'2nd multi world description'
-						)
+							'2nd multi world description',
+						),
 					),
 				]),
 			],
@@ -2957,8 +2955,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Foo'),
 							false,
 							'$foo',
-							'1st multi world description with empty lines'
-						)
+							'1st multi world description with empty lines',
+						),
 					),
 					new PhpDocTextNode(''),
 					new PhpDocTextNode(''),
@@ -2971,8 +2969,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Bar'),
 							false,
 							'$bar',
-							'2nd multi world description with empty lines'
-						)
+							'2nd multi world description with empty lines',
+						),
 					),
 					new PhpDocTextNode(''),
 					new PhpDocTextNode(''),
@@ -3004,8 +3002,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('int'),
 							false,
 							'$foo',
-							'@param string $bar'
-						)
+							'@param string $bar',
+						),
 					),
 				]),
 			],
@@ -3063,18 +3061,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									new IdentifierTypeNode('int'),
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3088,18 +3086,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									null,
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3111,8 +3109,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBar',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3121,8 +3119,8 @@ public function provideMultiLinePhpDocData(): iterable
 							null,
 							'methodWithNoReturnType',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3136,18 +3134,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									new IdentifierTypeNode('int'),
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3161,18 +3159,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									null,
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3184,8 +3182,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBarStatically',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3194,8 +3192,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('static'),
 							'methodWithNoReturnTypeStatically',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3209,18 +3207,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									new IdentifierTypeNode('int'),
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							'Get an integer with a description.'
-						)
+							'Get an integer with a description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3234,18 +3232,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									null,
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							'Do something with a description.'
-						)
+							'Do something with a description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3257,8 +3255,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBarWithDescription',
 							[],
-							'Get a Foo or a Bar with a description.'
-						)
+							'Get a Foo or a Bar with a description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3267,8 +3265,8 @@ public function provideMultiLinePhpDocData(): iterable
 							null,
 							'methodWithNoReturnTypeWithDescription',
 							[],
-							'Do something with a description but what, who knows!'
-						)
+							'Do something with a description but what, who knows!',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3282,18 +3280,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									new IdentifierTypeNode('int'),
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							'Get an integer with a description statically.'
-						)
+							'Get an integer with a description statically.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3307,18 +3305,18 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$a',
-									null
+									null,
 								),
 								new MethodTagValueParameterNode(
 									null,
 									false,
 									false,
 									'$b',
-									null
+									null,
 								),
 							],
-							'Do something with a description statically.'
-						)
+							'Do something with a description statically.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3330,8 +3328,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBarStaticallyWithDescription',
 							[],
-							'Get a Foo or a Bar with a description statically.'
-						)
+							'Get a Foo or a Bar with a description statically.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3340,8 +3338,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('static'),
 							'methodWithNoReturnTypeStaticallyWithDescription',
 							[],
-							'Do something with a description statically, but what, who knows!'
-						)
+							'Do something with a description statically, but what, who knows!',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3350,8 +3348,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('bool'),
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClass',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3360,8 +3358,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('string'),
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescription',
 							[],
-							'A Description.'
-						)
+							'A Description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3370,8 +3368,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('int'),
 							'getIntegerNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3380,8 +3378,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('void'),
 							'doSomethingNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3393,8 +3391,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBarNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3403,8 +3401,8 @@ public function provideMultiLinePhpDocData(): iterable
 							null,
 							'methodWithNoReturnTypeNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3413,8 +3411,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('int'),
 							'getIntegerStaticallyNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3423,8 +3421,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('void'),
 							'doSomethingStaticallyNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3436,8 +3434,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBarStaticallyNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3446,8 +3444,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('static'),
 							'methodWithNoReturnTypeStaticallyNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3456,8 +3454,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('int'),
 							'getIntegerWithDescriptionNoParams',
 							[],
-							'Get an integer with a description.'
-						)
+							'Get an integer with a description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3466,8 +3464,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('void'),
 							'doSomethingWithDescriptionNoParams',
 							[],
-							'Do something with a description.'
-						)
+							'Do something with a description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3479,8 +3477,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBarWithDescriptionNoParams',
 							[],
-							'Get a Foo or a Bar with a description.'
-						)
+							'Get a Foo or a Bar with a description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3489,8 +3487,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('int'),
 							'getIntegerStaticallyWithDescriptionNoParams',
 							[],
-							'Get an integer with a description statically.'
-						)
+							'Get an integer with a description statically.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3499,8 +3497,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('void'),
 							'doSomethingStaticallyWithDescriptionNoParams',
 							[],
-							'Do something with a description statically.'
-						)
+							'Do something with a description statically.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3512,8 +3510,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'getFooOrBarStaticallyWithDescriptionNoParams',
 							[],
-							'Get a Foo or a Bar with a description statically.'
-						)
+							'Get a Foo or a Bar with a description statically.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3525,8 +3523,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClassNoParams',
 							[],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3538,8 +3536,8 @@ public function provideMultiLinePhpDocData(): iterable
 							]),
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescriptionNoParams',
 							[],
-							'A Description.'
-						)
+							'A Description.',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3553,11 +3551,11 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$args',
-									null
+									null,
 								),
 							],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3571,18 +3569,18 @@ public function provideMultiLinePhpDocData(): iterable
 									true,
 									true,
 									'$angle',
-									new ConstExprArrayNode([])
+									new ConstExprArrayNode([]),
 								),
 								new MethodTagValueParameterNode(
 									null,
 									false,
 									false,
 									'$backgroundColor',
-									null
+									null,
 								),
 							],
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3591,8 +3589,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Foo'),
 							'overridenMethod',
 							[],
-							''
-						)
+							'',
+						),
 					),
 				]),
 			],
@@ -3606,11 +3604,11 @@ public function provideMultiLinePhpDocData(): iterable
 				new PhpDocNode([
 					new PhpDocTagNode(
 						'@template',
-						new TemplateTagValueNode('TKey', new IdentifierTypeNode('array-key'), '')
+						new TemplateTagValueNode('TKey', new IdentifierTypeNode('array-key'), ''),
 					),
 					new PhpDocTagNode(
 						'@template',
-						new TemplateTagValueNode('TValue', null, '')
+						new TemplateTagValueNode('TValue', null, ''),
 					),
 					new PhpDocTagNode(
 						'@method',
@@ -3627,11 +3625,11 @@ public function provideMultiLinePhpDocData(): iterable
 									false,
 									false,
 									'$v',
-									null
+									null,
 								),
 							],
-							'find index of $v'
-						)
+							'find index of $v',
+						),
 					),
 				]),
 			],
@@ -3653,11 +3651,11 @@ public function provideMultiLinePhpDocData(): iterable
 				new PhpDocNode([
 					new PhpDocTagNode(
 						'@template',
-						new TemplateTagValueNode('TRandKey', new IdentifierTypeNode('array-key'), '')
+						new TemplateTagValueNode('TRandKey', new IdentifierTypeNode('array-key'), ''),
 					),
 					new PhpDocTagNode(
 						'@template',
-						new TemplateTagValueNode('TRandVal', null, '')
+						new TemplateTagValueNode('TRandVal', null, ''),
 					),
 					new PhpDocTagNode(
 						'@template',
@@ -3673,7 +3671,7 @@ public function provideMultiLinePhpDocData(): iterable
 									[
 										GenericTypeNode::VARIANCE_INVARIANT,
 										GenericTypeNode::VARIANCE_INVARIANT,
-									]
+									],
 								),
 								new GenericTypeNode(
 									new IdentifierTypeNode('XIterator'),
@@ -3684,7 +3682,7 @@ public function provideMultiLinePhpDocData(): iterable
 									[
 										GenericTypeNode::VARIANCE_INVARIANT,
 										GenericTypeNode::VARIANCE_INVARIANT,
-									]
+									],
 								),
 								new GenericTypeNode(
 									new IdentifierTypeNode('Traversable'),
@@ -3695,11 +3693,11 @@ public function provideMultiLinePhpDocData(): iterable
 									[
 										GenericTypeNode::VARIANCE_INVARIANT,
 										GenericTypeNode::VARIANCE_INVARIANT,
-									]
+									],
 								),
 							]),
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTextNode(''),
 					new PhpDocTagNode(
@@ -3708,8 +3706,8 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('TRandList'),
 							false,
 							'$list',
-							''
-						)
+							'',
+						),
 					),
 					new PhpDocTextNode(''),
 					new PhpDocTagNode(
@@ -3727,7 +3725,7 @@ public function provideMultiLinePhpDocData(): iterable
 									[
 										GenericTypeNode::VARIANCE_INVARIANT,
 										GenericTypeNode::VARIANCE_INVARIANT,
-									]
+									],
 								),
 								new ConditionalTypeNode(
 									new IdentifierTypeNode('TRandList'),
@@ -3741,7 +3739,7 @@ public function provideMultiLinePhpDocData(): iterable
 										[
 											GenericTypeNode::VARIANCE_INVARIANT,
 											GenericTypeNode::VARIANCE_INVARIANT,
-										]
+										],
 									),
 									new UnionTypeNode([
 										new GenericTypeNode(
@@ -3753,7 +3751,7 @@ public function provideMultiLinePhpDocData(): iterable
 											[
 												GenericTypeNode::VARIANCE_INVARIANT,
 												GenericTypeNode::VARIANCE_INVARIANT,
-											]
+											],
 										),
 										new GenericTypeNode(
 											new IdentifierTypeNode('LimitIterator'),
@@ -3764,15 +3762,15 @@ public function provideMultiLinePhpDocData(): iterable
 											[
 												GenericTypeNode::VARIANCE_INVARIANT,
 												GenericTypeNode::VARIANCE_INVARIANT,
-											]
+											],
 										),
 									]),
-									false
+									false,
 								),
-								false
+								false,
 							),
-							''
-						)
+							'',
+						),
 					),
 				]),
 			],
@@ -3824,8 +3822,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						null,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -3839,8 +3837,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						null,
-						'the value type'
-					)
+						'the value type',
+					),
 				),
 			]),
 		];
@@ -3854,8 +3852,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						new IdentifierTypeNode('DateTime'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -3869,8 +3867,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						new IdentifierTypeNode('DateTime'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -3884,8 +3882,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						new IdentifierTypeNode('DateTime'),
-						'the value type'
-					)
+						'the value type',
+					),
 				),
 			]),
 		];
@@ -3899,8 +3897,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						new IdentifierTypeNode('DateTime'),
-						'the value type'
-					)
+						'the value type',
+					),
 				),
 			]),
 		];
@@ -3919,9 +3917,9 @@ public function provideTemplateTagsData(): Iterator
 							14,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -3940,9 +3938,9 @@ public function provideTemplateTagsData(): Iterator
 							14,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -3956,8 +3954,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						null,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -3971,8 +3969,8 @@ public function provideTemplateTagsData(): Iterator
 					new TemplateTagValueNode(
 						'T',
 						null,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -3987,8 +3985,8 @@ public function provideTemplateTagsData(): Iterator
 						'T',
 						null,
 						'',
-						new IdentifierTypeNode('string')
-					)
+						new IdentifierTypeNode('string'),
+					),
 				),
 			]),
 		];
@@ -4003,8 +4001,8 @@ public function provideTemplateTagsData(): Iterator
 						'T',
 						null,
 						'the value type',
-						new IdentifierTypeNode('string')
-					)
+						new IdentifierTypeNode('string'),
+					),
 				),
 			]),
 		];
@@ -4019,8 +4017,8 @@ public function provideTemplateTagsData(): Iterator
 						'T',
 						new IdentifierTypeNode('string'),
 						'the value type',
-						new ConstTypeNode(new ConstExprStringNode('', ConstExprStringNode::SINGLE_QUOTED))
-					)
+						new ConstTypeNode(new ConstExprStringNode('', ConstExprStringNode::SINGLE_QUOTED)),
+					),
 				),
 			]),
 		];
@@ -4042,10 +4040,10 @@ public function provideExtendsTagsData(): Iterator
 							],
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4066,10 +4064,10 @@ public function provideExtendsTagsData(): Iterator
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4090,10 +4088,10 @@ public function provideExtendsTagsData(): Iterator
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4114,10 +4112,10 @@ public function provideExtendsTagsData(): Iterator
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4132,10 +4130,10 @@ public function provideExtendsTagsData(): Iterator
 						new GenericTypeNode(
 							new IdentifierTypeNode('Foo'),
 							[new IdentifierTypeNode('A')],
-							[GenericTypeNode::VARIANCE_INVARIANT]
+							[GenericTypeNode::VARIANCE_INVARIANT],
 						),
-						'extends foo'
-					)
+						'extends foo',
+					),
 				),
 			]),
 		];
@@ -4154,9 +4152,9 @@ public function provideExtendsTagsData(): Iterator
 							13,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4175,9 +4173,9 @@ public function provideExtendsTagsData(): Iterator
 							17,
 							Lexer::TOKEN_OPEN_ANGLE_BRACKET,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4190,8 +4188,8 @@ public function provideExtendsTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('class-string'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4204,8 +4202,8 @@ public function provideExtendsTagsData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('class-string'),
-						'some description'
-					)
+						'some description',
+					),
 				),
 			]),
 		];
@@ -4220,8 +4218,8 @@ public function provideExtendsTagsData(): Iterator
 						new IdentifierTypeNode('class-string'),
 						false,
 						'$test',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4236,8 +4234,8 @@ public function provideExtendsTagsData(): Iterator
 						new IdentifierTypeNode('class-string'),
 						false,
 						'$test',
-						'some description'
-					)
+						'some description',
+					),
 				),
 			]),
 		];
@@ -4256,8 +4254,8 @@ public function provideTypeAliasTagsData(): Iterator
 						new UnionTypeNode([
 							new IdentifierTypeNode('string'),
 							new IdentifierTypeNode('int'),
-						])
-					)
+						]),
+					),
 				),
 			]),
 		];
@@ -4273,8 +4271,8 @@ public function provideTypeAliasTagsData(): Iterator
 						new UnionTypeNode([
 							new IdentifierTypeNode('string'),
 							new IdentifierTypeNode('int'),
-						])
-					)
+						]),
+					),
 				),
 			]),
 		];
@@ -4293,9 +4291,9 @@ public function provideTypeAliasTagsData(): Iterator
 							28,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						))
-					)
+							1,
+						)),
+					),
 				),
 			]),
 		];
@@ -4316,9 +4314,9 @@ public function provideTypeAliasTagsData(): Iterator
 							34,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							2
-						))
-					)
+							2,
+						)),
+					),
 				),
 			]),
 		];
@@ -4340,16 +4338,16 @@ public function provideTypeAliasTagsData(): Iterator
 							34,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							2
-						))
-					)
+							2,
+						)),
+					),
 				),
 				new PhpDocTagNode(
 					'@mixin',
 					new MixinTagValueNode(
 						new IdentifierTypeNode('T'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4365,8 +4363,8 @@ public function provideTypeAliasTagsData(): Iterator
 					'@phpstan-type',
 					new TypeAliasTagValueNode(
 						'Foo',
-						new ArrayShapeNode([])
-					)
+						new ArrayShapeNode([]),
+					),
 				),
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4378,9 +4376,9 @@ public function provideTypeAliasTagsData(): Iterator
 							65,
 							Lexer::TOKEN_PHPDOC_EOL,
 							null,
-							3
-						))
-					)
+							3,
+						)),
+					),
 				),
 			]),
 		];
@@ -4397,8 +4395,8 @@ public function provideTypeAliasTagsData(): Iterator
 					'@phpstan-type',
 					new TypeAliasTagValueNode(
 						'Foo',
-						new ArrayShapeNode([])
-					)
+						new ArrayShapeNode([]),
+					),
 				),
 				new PhpDocTagNode(
 					'@phpstan-type',
@@ -4410,16 +4408,16 @@ public function provideTypeAliasTagsData(): Iterator
 							65,
 							Lexer::TOKEN_PHPDOC_EOL,
 							null,
-							3
-						))
-					)
+							3,
+						)),
+					),
 				),
 				new PhpDocTagNode(
 					'@phpstan-type',
 					new TypeAliasTagValueNode(
 						'Bar',
-						new ArrayShapeNode([])
-					)
+						new ArrayShapeNode([]),
+					),
 				),
 			]),
 		];
@@ -4438,9 +4436,9 @@ public function provideTypeAliasTagsData(): Iterator
 							18,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4457,8 +4455,8 @@ public function provideTypeAliasImportTagsData(): Iterator
 					new TypeAliasImportTagValueNode(
 						'TypeAlias',
 						new IdentifierTypeNode('AnotherClass'),
-						null
-					)
+						null,
+					),
 				),
 			]),
 		];
@@ -4472,8 +4470,8 @@ public function provideTypeAliasImportTagsData(): Iterator
 					new TypeAliasImportTagValueNode(
 						'TypeAlias',
 						new IdentifierTypeNode('AnotherClass'),
-						'DifferentAlias'
-					)
+						'DifferentAlias',
+					),
 				),
 			]),
 		];
@@ -4492,9 +4490,9 @@ public function provideTypeAliasImportTagsData(): Iterator
 							40,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4513,9 +4511,9 @@ public function provideTypeAliasImportTagsData(): Iterator
 							52,
 							Lexer::TOKEN_CLOSE_PHPDOC,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4534,9 +4532,9 @@ public function provideTypeAliasImportTagsData(): Iterator
 							35,
 							Lexer::TOKEN_IDENTIFIER,
 							'from',
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4555,9 +4553,9 @@ public function provideTypeAliasImportTagsData(): Iterator
 							35,
 							Lexer::TOKEN_IDENTIFIER,
 							'from',
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4576,9 +4574,9 @@ public function provideTypeAliasImportTagsData(): Iterator
 							25,
 							Lexer::TOKEN_IDENTIFIER,
 							null,
-							1
-						)
-					)
+							1,
+						),
+					),
 				),
 			]),
 		];
@@ -4596,8 +4594,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$var',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4612,8 +4610,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$var',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4628,8 +4626,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$var',
 						false,
-						'assert Type to $var'
-					)
+						'assert Type to $var',
+					),
 				),
 			]),
 		];
@@ -4647,8 +4645,8 @@ public function provideAssertTagsData(): Iterator
 						]),
 						'$var',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4664,8 +4662,8 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						'method',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4681,8 +4679,8 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						'property',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4697,8 +4695,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$this',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4713,8 +4711,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$this',
 						false,
-						'assert Type to $this'
-					)
+						'assert Type to $this',
+					),
 				),
 			]),
 		];
@@ -4733,12 +4731,12 @@ public function provideAssertTagsData(): Iterator
 							],
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
 						'$this',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4754,8 +4752,8 @@ public function provideAssertTagsData(): Iterator
 						'$this',
 						'method',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4771,8 +4769,8 @@ public function provideAssertTagsData(): Iterator
 						'$this',
 						'property',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4787,8 +4785,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$var',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4803,8 +4801,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$var',
 						false,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4819,8 +4817,8 @@ public function provideAssertTagsData(): Iterator
 						new IdentifierTypeNode('Type'),
 						'$var',
 						true,
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -4836,8 +4834,8 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						false,
 						'',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -4853,8 +4851,8 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						true,
 						'',
-						true
-					)
+						true,
+					),
 				),
 			]),
 		];
@@ -4943,8 +4941,8 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('\Drupal\Core\Field\FieldStorageDefinitionInterface'),
 						false,
 						'$field_definition',
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTextNode('The field definition.'),
 				new PhpDocTextNode(''),
@@ -4952,8 +4950,8 @@ public function provideRealWorldExampleData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('array'),
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTextNode("An empty array if there is no schema, or an associative array with the
   following key/value pairs:
@@ -5020,16 +5018,16 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('Request'),
 						false,
 						'$request',
-						'- The request object'
-					)
+						'- The request object',
+					),
 				),
 				new PhpDocTextNode(''),
 				new PhpDocTagNode(
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('array'),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5069,8 +5067,8 @@ public function provideRealWorldExampleData(): Iterator
 							new ConstTypeNode(new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED)),
 							new ConstTypeNode(new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED)),
 						]),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5103,10 +5101,10 @@ public function provideRealWorldExampleData(): Iterator
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5132,10 +5130,10 @@ public function provideRealWorldExampleData(): Iterator
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5161,10 +5159,10 @@ public function provideRealWorldExampleData(): Iterator
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5186,12 +5184,12 @@ public function provideRealWorldExampleData(): Iterator
 								new CallableTypeParameterNode(new IdentifierTypeNode('A'), false, false, '', false),
 								new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false),
 							],
-							new IdentifierTypeNode('void')
+							new IdentifierTypeNode('void'),
 						),
 						false,
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5214,12 +5212,12 @@ public function provideRealWorldExampleData(): Iterator
 								new CallableTypeParameterNode(new IdentifierTypeNode('A'), false, false, '', false),
 								new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false),
 							],
-							new IdentifierTypeNode('void')
+							new IdentifierTypeNode('void'),
 						),
 						false,
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5242,12 +5240,12 @@ public function provideRealWorldExampleData(): Iterator
 								new CallableTypeParameterNode(new IdentifierTypeNode('A'), false, false, '', false),
 								new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false),
 							],
-							new IdentifierTypeNode('void')
+							new IdentifierTypeNode('void'),
 						),
 						false,
 						'$foo',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5287,10 +5285,10 @@ public function provideRealWorldExampleData(): Iterator
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTextNode(''),
 				new PhpDocTagNode(
@@ -5299,8 +5297,8 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('string'),
 						false,
 						'$pattern',
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTagNode(
 					'@param',
@@ -5308,8 +5306,8 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('string'),
 						false,
 						'$subject',
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTagNode(
 					'@param',
@@ -5317,8 +5315,8 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('mixed'),
 						false,
 						'$matches',
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTagNode(
 					'@param',
@@ -5326,8 +5324,8 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('TFlags'),
 						false,
 						'$flags',
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTagNode(
 					'@param-out',
@@ -5348,7 +5346,7 @@ public function provideRealWorldExampleData(): Iterator
 												new UnionTypeNode([
 													new ConstTypeNode(new ConstExprIntegerNode('0')),
 													new IdentifierTypeNode('positive-int'),
-												])
+												]),
 											),
 										]),
 										new ArrayShapeNode([
@@ -5360,7 +5358,7 @@ public function provideRealWorldExampleData(): Iterator
 								[
 									GenericTypeNode::VARIANCE_INVARIANT,
 									GenericTypeNode::VARIANCE_INVARIANT,
-								]
+								],
 							),
 							new ConditionalTypeNode(
 								new IdentifierTypeNode('TFlags'),
@@ -5377,7 +5375,7 @@ public function provideRealWorldExampleData(): Iterator
 									[
 										GenericTypeNode::VARIANCE_INVARIANT,
 										GenericTypeNode::VARIANCE_INVARIANT,
-									]
+									],
 								),
 								new ConditionalTypeNode(
 									new IdentifierTypeNode('TFlags'),
@@ -5395,7 +5393,7 @@ public function provideRealWorldExampleData(): Iterator
 														new UnionTypeNode([
 															new ConstTypeNode(new ConstExprIntegerNode('0')),
 															new IdentifierTypeNode('positive-int'),
-														])
+														]),
 													),
 												]),
 												new ArrayShapeNode([
@@ -5407,7 +5405,7 @@ public function provideRealWorldExampleData(): Iterator
 										[
 											GenericTypeNode::VARIANCE_INVARIANT,
 											GenericTypeNode::VARIANCE_INVARIANT,
-										]
+										],
 									),
 									new GenericTypeNode(
 										new IdentifierTypeNode('array'),
@@ -5418,17 +5416,17 @@ public function provideRealWorldExampleData(): Iterator
 										[
 											GenericTypeNode::VARIANCE_INVARIANT,
 											GenericTypeNode::VARIANCE_INVARIANT,
-										]
+										],
 									),
-									false
+									false,
 								),
-								false
+								false,
 							),
-							false
+							false,
 						),
 						'$matches',
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTagNode(
 					'@return',
@@ -5438,8 +5436,8 @@ public function provideRealWorldExampleData(): Iterator
 							new ConstTypeNode(new ConstExprIntegerNode('0')),
 							new IdentifierTypeNode('false'),
 						]),
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTagNode('@psalm-ignore-falsable-return', new GenericTagValueNode('')),
 			]),
@@ -5458,8 +5456,8 @@ public function provideDescriptionWithOrWithoutHtml(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('Foo'),
-						'<strong>Important <i>description</i></strong>'
-					)
+						'<strong>Important <i>description</i></strong>',
+					),
 				),
 			]),
 		];
@@ -5474,8 +5472,8 @@ public function provideDescriptionWithOrWithoutHtml(): Iterator
 					'@throws',
 					new ThrowsTagValueNode(
 						new IdentifierTypeNode('FooException'),
-						'<strong>Important <em>description</em> etc</strong>'
-					)
+						'<strong>Important <em>description</em> etc</strong>',
+					),
 				),
 			]),
 		];
@@ -5490,8 +5488,8 @@ public function provideDescriptionWithOrWithoutHtml(): Iterator
 					'@mixin',
 					new MixinTagValueNode(
 						new IdentifierTypeNode('Mixin'),
-						'<strong>Important description</strong>'
-					)
+						'<strong>Important description</strong>',
+					),
 				),
 			]),
 		];
@@ -5512,9 +5510,9 @@ public function provideDescriptionWithOrWithoutHtml(): Iterator
 							PHP_EOL === "\n" ? 27 : 28,
 							Lexer::TOKEN_HORIZONTAL_WS,
 							null,
-							2
-						)
-					)
+							2,
+						),
+					),
 				),
 			]),
 		];
@@ -5533,7 +5531,7 @@ public function dataParseTagValue(): array
 					new ConstTypeNode(new ConstFetchNode('DateTimeImmutable', 'ATOM')),
 					false,
 					'$a',
-					''
+					'',
 				),
 			],
 			[
@@ -5547,8 +5545,8 @@ public function dataParseTagValue(): array
 						0,
 						Lexer::TOKEN_IDENTIFIER,
 						null,
-						1
-					)
+						1,
+					),
 				),
 			],
 		];
@@ -5562,7 +5560,7 @@ public function provideTagsWithNumbers(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@special3',
-					new GenericTagValueNode('Foo')
+					new GenericTagValueNode('Foo'),
 				),
 			]),
 		];
@@ -5576,7 +5574,7 @@ public function provideTagsWithBackslash(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@ORM\Mapping\Entity',
-					new GenericTagValueNode('User')
+					new GenericTagValueNode('User'),
 				),
 			]),
 		];
@@ -5590,7 +5588,7 @@ public function provideTagsWithBackslash(): Iterator
 					new DoctrineTagValueNode(new DoctrineAnnotation('@ORM\Mapping\JoinColumn', [
 						new DoctrineArgument(new IdentifierTypeNode('name'), new DoctrineConstExprStringNode('column_id')),
 						new DoctrineArgument(new IdentifierTypeNode('referencedColumnName'), new DoctrineConstExprStringNode('id')),
-					]), '')
+					]), ''),
 				),
 			]),
 		];
@@ -5606,8 +5604,8 @@ public function provideSelfOutTagsData(): Iterator
 					'@phpstan-self-out',
 					new SelfOutTagValueNode(
 						new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')], [GenericTypeNode::VARIANCE_INVARIANT]),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5620,8 +5618,8 @@ public function provideSelfOutTagsData(): Iterator
 					'@phpstan-this-out',
 					new SelfOutTagValueNode(
 						new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')], [GenericTypeNode::VARIANCE_INVARIANT]),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5634,8 +5632,8 @@ public function provideSelfOutTagsData(): Iterator
 					'@psalm-self-out',
 					new SelfOutTagValueNode(
 						new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')], [GenericTypeNode::VARIANCE_INVARIANT]),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5648,8 +5646,8 @@ public function provideSelfOutTagsData(): Iterator
 					'@psalm-this-out',
 					new SelfOutTagValueNode(
 						new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')], [GenericTypeNode::VARIANCE_INVARIANT]),
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5662,8 +5660,8 @@ public function provideSelfOutTagsData(): Iterator
 					'@phpstan-self-out',
 					new SelfOutTagValueNode(
 						new GenericTypeNode(new IdentifierTypeNode('self'), [new IdentifierTypeNode('T')], [GenericTypeNode::VARIANCE_INVARIANT]),
-						'description'
-					)
+						'description',
+					),
 				),
 			]),
 		];
@@ -5679,7 +5677,7 @@ public function provideCommentLikeDescriptions(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$a',
-					'// this is a description'
+					'// this is a description',
 				)),
 			]),
 		];
@@ -5695,7 +5693,7 @@ public function provideCommentLikeDescriptions(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$a',
-					''
+					'',
 				)),
 				new PhpDocTextNode('// this is a comment'),
 			]),
@@ -5712,7 +5710,7 @@ public function provideCommentLikeDescriptions(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$a',
-					''
+					'',
 				)),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode('// this is a comment'),
@@ -5731,7 +5729,7 @@ public function provideCommentLikeDescriptions(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@\ORM\Doctrine', new DoctrineTagValueNode(
 					new DoctrineAnnotation('@\ORM\Doctrine', []),
-					'// this is a description'
+					'// this is a description',
 				)),
 			]),
 		];
@@ -5741,7 +5739,7 @@ public function provideCommentLikeDescriptions(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@\ORM\Doctrine', new DoctrineTagValueNode(
 					new DoctrineAnnotation('@\ORM\Doctrine', []),
-					'// this is a description'
+					'// this is a description',
 				)),
 			]),
 		];
@@ -5751,11 +5749,11 @@ public function provideCommentLikeDescriptions(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@\ORM\Doctrine', new DoctrineTagValueNode(
 					new DoctrineAnnotation('@\ORM\Doctrine', []),
-					''
+					'',
 				)),
 				new PhpDocTagNode('@\ORM\Entity', new DoctrineTagValueNode(
 					new DoctrineAnnotation('@\ORM\Entity', []),
-					'// this is a description'
+					'// this is a description',
 				)),
 			]),
 		];
@@ -5772,8 +5770,8 @@ public function provideParamOutTagsData(): Iterator
 					new ParamOutTagValueNode(
 						new IdentifierTypeNode('string'),
 						'$s',
-						''
-					)
+						'',
+					),
 				),
 			]),
 		];
@@ -5787,8 +5785,8 @@ public function provideParamOutTagsData(): Iterator
 					new ParamOutTagValueNode(
 						new IdentifierTypeNode('string'),
 						'$s',
-						'description'
-					)
+						'description',
+					),
 				),
 			]),
 		];
@@ -5806,8 +5804,8 @@ public function provideDoctrineData(): Iterator
 					'@X',
 					new DoctrineTagValueNode(
 						new DoctrineAnnotation('@X', []),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[new Doctrine\X()],
@@ -5827,8 +5825,8 @@ public function provideDoctrineData(): Iterator
 						new DoctrineAnnotation('@X', [
 							new DoctrineArgument(null, new DoctrineAnnotation('@Z', [])),
 						]),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[$xWithZ],
@@ -5846,8 +5844,8 @@ public function provideDoctrineData(): Iterator
 						new DoctrineAnnotation('@X', [
 							new DoctrineArgument(new IdentifierTypeNode('a'), new DoctrineAnnotation('@Z', [])),
 						]),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[$xWithZ],
@@ -5865,8 +5863,8 @@ public function provideDoctrineData(): Iterator
 						new DoctrineAnnotation('@X', [
 							new DoctrineArgument(null, new DoctrineAnnotation('@\PHPStan\PhpDocParser\Parser\Doctrine\Z', [])),
 						]),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[$xWithZ],
@@ -5884,8 +5882,8 @@ public function provideDoctrineData(): Iterator
 						new DoctrineAnnotation('@X', [
 							new DoctrineArgument(new IdentifierTypeNode('a'), new DoctrineAnnotation('@\PHPStan\PhpDocParser\Parser\Doctrine\Z', [])),
 						]),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[$xWithZ],
@@ -5902,8 +5900,8 @@ public function provideDoctrineData(): Iterator
 					'@X',
 					new DoctrineTagValueNode(
 						new DoctrineAnnotation('@X', []),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[new Doctrine\X()],
@@ -5920,8 +5918,8 @@ public function provideDoctrineData(): Iterator
 					'@X',
 					new DoctrineTagValueNode(
 						new DoctrineAnnotation('@X', []),
-						''
-					)
+						'',
+					),
 				),
 				new PhpDocTextNode('Content'),
 			]),
@@ -5938,8 +5936,8 @@ public function provideDoctrineData(): Iterator
 					'@\PHPStan\PhpDocParser\Parser\Doctrine\X',
 					new DoctrineTagValueNode(
 						new DoctrineAnnotation('@\PHPStan\PhpDocParser\Parser\Doctrine\X', []),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[new Doctrine\X()],
@@ -5955,8 +5953,8 @@ public function provideDoctrineData(): Iterator
 					'@\PHPStan\PhpDocParser\Parser\Doctrine\X',
 					new DoctrineTagValueNode(
 						new DoctrineAnnotation('@\PHPStan\PhpDocParser\Parser\Doctrine\X', []),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[new Doctrine\X()],
@@ -5993,10 +5991,10 @@ public function provideDoctrineData(): Iterator
 										new DoctrineArgument(new IdentifierTypeNode('code'), new ConstExprIntegerNode('123')),
 									])),
 								])),
-							]
+							],
 						),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[$x],
@@ -6029,10 +6027,10 @@ public function provideDoctrineData(): Iterator
 										new DoctrineArgument(new IdentifierTypeNode('code'), new ConstExprIntegerNode('123')),
 									])),
 								])),
-							]
+							],
 						),
-						'Content'
-					)
+						'Content',
+					),
 				),
 			]),
 			[$x],
@@ -6046,7 +6044,7 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@X',
-					[]
+					[],
 				), 'test')),
 			]),
 			[new Doctrine\X()],
@@ -6061,7 +6059,7 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@X',
-					[]
+					[],
 				), 'test' . PHP_EOL . 'test2')),
 			]),
 			[new Doctrine\X()],
@@ -6076,7 +6074,7 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@X',
-					[]
+					[],
 				), 'test')),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode('test2'),
@@ -6094,13 +6092,13 @@ public function provideDoctrineData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@X',
-					[]
+					[],
 				), 'test')),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode('test2'),
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@Z',
-					[]
+					[],
 				), '')),
 			]),
 			[new Doctrine\X(), new Doctrine\Z()],
@@ -6263,7 +6261,7 @@ public function provideDoctrineData(): Iterator
 										new DoctrineConstExprStringNode('is_granted(' . PHP_EOL .
 											"constant('REDACTED')," . PHP_EOL .
 											'object' . PHP_EOL .
-											')')
+											')'),
 									),
 									new DoctrineArrayItem(
 										new DoctrineConstExprStringNode('normalization_context'),
@@ -6272,15 +6270,15 @@ public function provideDoctrineData(): Iterator
 												new DoctrineConstExprStringNode('groups'),
 												new DoctrineArray([
 													new DoctrineArrayItem(null, new DoctrineConstExprStringNode('Redacted:read')),
-												])
+												]),
 											),
-										])
+										]),
 									),
-								])
+								]),
 							),
 						])),
 					]),
-					''
+					'',
 				)),
 			]),
 			[$apiResource],
@@ -6296,7 +6294,7 @@ public function provideDoctrineData(): Iterator
 					new DoctrineAnnotation('@X', [
 						new DoctrineArgument(new IdentifierTypeNode('a'), new DoctrineConstExprStringNode($xWithString->a)),
 					]),
-					''
+					'',
 				)),
 			]),
 			[$xWithString],
@@ -6312,7 +6310,7 @@ public function provideDoctrineData(): Iterator
 					new DoctrineAnnotation('@X', [
 						new DoctrineArgument(new IdentifierTypeNode('a'), new DoctrineConstExprStringNode($xWithString2->a)),
 					]),
-					''
+					'',
 				)),
 			]),
 			[$xWithString2],
@@ -6328,7 +6326,7 @@ public function provideDoctrineData(): Iterator
 					new DoctrineAnnotation('@X', [
 						new DoctrineArgument(new IdentifierTypeNode('a'), new DoctrineConstExprStringNode($xWithString3->a)),
 					]),
-					''
+					'',
 				)),
 			]),
 			[$xWithString3],
@@ -6344,7 +6342,7 @@ public function provideDoctrineData(): Iterator
 					new DoctrineAnnotation('@X', [
 						new DoctrineArgument(new IdentifierTypeNode('a'), new DoctrineConstExprStringNode($xWithString4->a)),
 					]),
-					''
+					'',
 				)),
 			]),
 			[$xWithString4],
@@ -6361,7 +6359,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new DoctrineAnnotation('@DummyAnnotation', [
 						new DoctrineArgument(new IdentifierTypeNode('dummyValue'), new DoctrineConstExprStringNode('hello')),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6391,7 +6389,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 							])),
 						])),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6404,7 +6402,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new DoctrineAnnotation('@AnnotationTargetAll', [
 						new DoctrineArgument(null, new DoctrineAnnotation('@AnnotationTargetAnnotation', [])),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6417,7 +6415,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new DoctrineAnnotation('@DummyAnnotation', [
 						new DoctrineArgument(new IdentifierTypeNode('dummyValue'), new DoctrineConstExprStringNode('bar')),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6434,7 +6432,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new DoctrineAnnotation('@DummyColumn', [
 						new DoctrineArgument(new IdentifierTypeNode('type'), new DoctrineConstExprStringNode('integer')),
 					]),
-					''
+					'',
 				)),
 				new PhpDocTagNode('@DummyGeneratedValue', new GenericTagValueNode('')),
 				new PhpDocTagNode('@var', new VarTagValueNode(new IdentifierTypeNode('int'), '', '')),
@@ -6457,7 +6455,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new DoctrineAnnotation('@AnnotationWithConstants', [
 						new DoctrineArgument(null, new IdentifierTypeNode('PHP_EOL')),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6474,12 +6472,12 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 							new DoctrineArrayItem(null, new DoctrineArray([
 								new DoctrineArrayItem(new DoctrineConstExprStringNode('key'), new DoctrineAnnotation(
 									'@Name',
-									[]
+									[],
 								)),
 							])),
 						])),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6492,7 +6490,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new DoctrineAnnotation('@AnnotationWithConstants', [
 						new DoctrineArgument(null, new ConstFetchNode('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants', 'FLOAT')),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6505,7 +6503,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new DoctrineAnnotation('@AnnotationWithConstants', [
 						new DoctrineArgument(null, new ConstFetchNode('\Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants', 'FLOAT')),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6521,11 +6519,11 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 						new DoctrineArgument(null, new DoctrineArray([
 							new DoctrineArrayItem(
 								new ConstFetchNode('Doctrine\Tests\Common\Annotations\Fixtures\InterfaceWithConstants', 'SOME_KEY'),
-								new ConstFetchNode('AnnotationWithConstants', 'INTEGER')
+								new ConstFetchNode('AnnotationWithConstants', 'INTEGER'),
 							),
 						])),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6540,7 +6538,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 							new DoctrineArrayItem(new DoctrineConstExprStringNode('foo'), new DoctrineConstExprStringNode('bar')),
 						])),
 					]),
-					''
+					'',
 				)),
 			]),
 		];
@@ -6556,7 +6554,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					19,
 					5,
 					null,
-					1
+					1,
 				))),
 			]),
 			[new Doctrine\X()],
@@ -6573,7 +6571,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					21,
 					5,
 					null,
-					1
+					1,
 				))),
 			]),
 			[new Doctrine\X()],
@@ -6587,7 +6585,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$z',
-					'@X()'
+					'@X()',
 				)),
 			]),
 		];
@@ -6600,7 +6598,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$z',
-					'@\X\Y()'
+					'@\X\Y()',
 				)),
 			]),
 		];
@@ -6613,7 +6611,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$z',
-					'@X'
+					'@X',
 				)),
 			]),
 		];
@@ -6646,8 +6644,8 @@ public function provideSpecializedTags(): Iterator
 				new PhpDocTagNode(
 					'@special:param',
 					new GenericTagValueNode(
-						'this is special'
-					)
+						'this is special',
+					),
 				),
 			]),
 		];
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index c226df84..cd50db40 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -40,11 +40,9 @@
 class TypeParserTest extends TestCase
 {
 
-	/** @var Lexer */
-	private $lexer;
+	private Lexer $lexer;
 
-	/** @var TypeParser */
-	private $typeParser;
+	private TypeParser $typeParser;
 
 	protected function setUp(): void
 	{
@@ -280,13 +278,13 @@ public function provideParseData(): array
 			[
 				'string[]',
 				new ArrayTypeNode(
-					new IdentifierTypeNode('string')
+					new IdentifierTypeNode('string'),
 				),
 			],
 			[
 				'string [  ] ',
 				new ArrayTypeNode(
-					new IdentifierTypeNode('string')
+					new IdentifierTypeNode('string'),
 				),
 			],
 			[
@@ -296,7 +294,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('string'),
 						new IdentifierTypeNode('int'),
 						new IdentifierTypeNode('float'),
-					])
+					]),
 				),
 			],
 			[
@@ -304,9 +302,9 @@ public function provideParseData(): array
 				new ArrayTypeNode(
 					new ArrayTypeNode(
 						new ArrayTypeNode(
-							new IdentifierTypeNode('string')
-						)
-					)
+							new IdentifierTypeNode('string'),
+						),
+					),
 				),
 			],
 			[
@@ -314,9 +312,9 @@ public function provideParseData(): array
 				new ArrayTypeNode(
 					new ArrayTypeNode(
 						new ArrayTypeNode(
-							new IdentifierTypeNode('string')
-						)
-					)
+							new IdentifierTypeNode('string'),
+						),
+					),
 				),
 			],
 			[
@@ -328,9 +326,9 @@ public function provideParseData(): array
 								new IdentifierTypeNode('string'),
 								new IdentifierTypeNode('int'),
 								new IdentifierTypeNode('float'),
-							])
-						)
-					)
+							]),
+						),
+					),
 				),
 			],
 			[
@@ -340,7 +338,7 @@ public function provideParseData(): array
 			[
 				'?int',
 				new NullableTypeNode(
-					new IdentifierTypeNode('int')
+					new IdentifierTypeNode('int'),
 				),
 			],
 			[
@@ -353,8 +351,8 @@ public function provideParseData(): array
 						],
 						[
 							GenericTypeNode::VARIANCE_INVARIANT,
-						]
-					)
+						],
+					),
 				),
 			],
 			[
@@ -368,7 +366,7 @@ public function provideParseData(): array
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
 						GenericTypeNode::VARIANCE_INVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -383,7 +381,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -394,8 +392,8 @@ public function provideParseData(): array
 						new IdentifierTypeNode('a'),
 						false,
 						new NullableTypeNode(
-							new IdentifierTypeNode('int')
-						)
+							new IdentifierTypeNode('int'),
+						),
 					),
 				]),
 			],
@@ -406,8 +404,8 @@ public function provideParseData(): array
 						new IdentifierTypeNode('a'),
 						true,
 						new NullableTypeNode(
-							new IdentifierTypeNode('int')
-						)
+							new IdentifierTypeNode('int'),
+						),
 					),
 				]),
 			],
@@ -417,7 +415,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new ConstExprIntegerNode('0'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -427,7 +425,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new ConstExprIntegerNode('0'),
 						true,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -437,12 +435,12 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						null,
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						null,
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -452,12 +450,12 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -467,27 +465,27 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						true,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 					new ArrayShapeItemNode(
 						new ConstExprIntegerNode('0'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new ConstExprIntegerNode('1'),
 						true,
-						new IdentifierTypeNode('DateTime')
+						new IdentifierTypeNode('DateTime'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('hello'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -497,7 +495,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
@@ -509,10 +507,10 @@ public function provideParseData(): array
 								new CallableTypeNode(
 									new IdentifierTypeNode('callable'),
 									[],
-									new IdentifierTypeNode('int')
-								)
+									new IdentifierTypeNode('int'),
+								),
 							),
-						])
+						]),
 					),
 				]),
 			],
@@ -523,9 +521,9 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
-					])
+					]),
 				),
 			],
 			[
@@ -534,7 +532,7 @@ public function provideParseData(): array
 					'',
 					Lexer::TOKEN_END,
 					6,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -543,7 +541,7 @@ public function provideParseData(): array
 					'=>',
 					Lexer::TOKEN_OTHER,
 					8,
-					Lexer::TOKEN_CLOSE_CURLY_BRACKET
+					Lexer::TOKEN_CLOSE_CURLY_BRACKET,
 				),
 			],
 			[
@@ -552,7 +550,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('a', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -562,7 +560,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -572,7 +570,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('$ref', ConstExprStringNode::SINGLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -582,7 +580,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('$ref', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -594,7 +592,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -606,7 +604,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -619,12 +617,12 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -638,17 +636,17 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('c'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -661,12 +659,12 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -676,12 +674,12 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				], false),
 			],
@@ -691,12 +689,12 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						null,
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ArrayShapeItemNode(
 						null,
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				], false),
 			],
@@ -713,7 +711,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				], false),
 			],
@@ -726,7 +724,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				], false),
 			],
@@ -736,7 +734,7 @@ public function provideParseData(): array
 					'string',
 					Lexer::TOKEN_IDENTIFIER,
 					16,
-					Lexer::TOKEN_CLOSE_CURLY_BRACKET
+					Lexer::TOKEN_CLOSE_CURLY_BRACKET,
 				),
 			],
 			[
@@ -749,16 +747,16 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('string')
+							new IdentifierTypeNode('string'),
 						),
 					],
 					true,
-					ArrayShapeNode::KIND_LIST
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
@@ -769,8 +767,8 @@ public function provideParseData(): array
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -780,20 +778,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('b'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -803,20 +801,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('b'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -831,20 +829,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('b'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -855,8 +853,8 @@ public function provideParseData(): array
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						new IdentifierTypeNode('int')
-					)
+						new IdentifierTypeNode('int'),
+					),
 				),
 			],
 			[
@@ -866,20 +864,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('b'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						new IdentifierTypeNode('int')
-					)
+						new IdentifierTypeNode('int'),
+					),
 				),
 			],
 			[
@@ -889,20 +887,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('b'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						new IdentifierTypeNode('int')
-					)
+						new IdentifierTypeNode('int'),
+					),
 				),
 			],
 			[
@@ -919,20 +917,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('b'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						new IdentifierTypeNode('int')
-					)
+						new IdentifierTypeNode('int'),
+					),
 				),
 			],
 			[
@@ -943,8 +941,8 @@ public function provideParseData(): array
 					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -954,20 +952,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -977,20 +975,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -1005,20 +1003,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -1028,20 +1026,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('0'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('1'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -1051,20 +1049,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('0'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('1'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -1079,20 +1077,20 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('0'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('1'),
 							true,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
 					],
 					false,
 					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
-						null
-					)
+						null,
+					),
 				),
 			],
 			[
@@ -1101,7 +1099,7 @@ public function provideParseData(): array
 					'>',
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					10,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -1110,7 +1108,7 @@ public function provideParseData(): array
 					'>',
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					14,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -1119,7 +1117,7 @@ public function provideParseData(): array
 					',',
 					Lexer::TOKEN_COMMA,
 					21,
-					Lexer::TOKEN_CLOSE_ANGLE_BRACKET
+					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 				),
 			],
 			[
@@ -1128,7 +1126,7 @@ public function provideParseData(): array
 					',',
 					Lexer::TOKEN_COMMA,
 					21,
-					Lexer::TOKEN_CLOSE_ANGLE_BRACKET
+					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 				),
 			],
 			[
@@ -1137,7 +1135,7 @@ public function provideParseData(): array
 					'>',
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					9,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -1146,7 +1144,7 @@ public function provideParseData(): array
 					',',
 					Lexer::TOKEN_COMMA,
 					12,
-					Lexer::TOKEN_CLOSE_ANGLE_BRACKET
+					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 				),
 			],
 			[
@@ -1155,7 +1153,7 @@ public function provideParseData(): array
 					',',
 					Lexer::TOKEN_COMMA,
 					12,
-					Lexer::TOKEN_CLOSE_ANGLE_BRACKET
+					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 				),
 			],
 			[
@@ -1163,7 +1161,7 @@ public function provideParseData(): array
 				new CallableTypeNode(
 					new IdentifierTypeNode('callable'),
 					[],
-					new IdentifierTypeNode('Foo')
+					new IdentifierTypeNode('Foo'),
 				),
 			],
 			[
@@ -1171,7 +1169,7 @@ public function provideParseData(): array
 				new CallableTypeNode(
 					new IdentifierTypeNode('pure-callable'),
 					[],
-					new IdentifierTypeNode('Foo')
+					new IdentifierTypeNode('Foo'),
 				),
 			],
 			[
@@ -1179,7 +1177,7 @@ public function provideParseData(): array
 				new CallableTypeNode(
 					new IdentifierTypeNode('pure-Closure'),
 					[],
-					new IdentifierTypeNode('Foo')
+					new IdentifierTypeNode('Foo'),
 				),
 			],
 			[
@@ -1188,8 +1186,8 @@ public function provideParseData(): array
 					new IdentifierTypeNode('callable'),
 					[],
 					new NullableTypeNode(
-						new IdentifierTypeNode('Foo')
-					)
+						new IdentifierTypeNode('Foo'),
+					),
 				),
 			],
 			[
@@ -1204,8 +1202,8 @@ public function provideParseData(): array
 						],
 						[
 							GenericTypeNode::VARIANCE_INVARIANT,
-						]
-					)
+						],
+					),
 				),
 			],
 			[
@@ -1220,8 +1218,8 @@ public function provideParseData(): array
 						],
 						[
 							GenericTypeNode::VARIANCE_INVARIANT,
-						]
-					))
+						],
+					)),
 				),
 			],
 			[
@@ -1230,7 +1228,7 @@ public function provideParseData(): array
 					new CallableTypeNode(
 						new IdentifierTypeNode('callable'),
 						[],
-						new IdentifierTypeNode('Foo')
+						new IdentifierTypeNode('Foo'),
 					),
 					new IdentifierTypeNode('Bar'),
 				]),
@@ -1241,7 +1239,7 @@ public function provideParseData(): array
 					new CallableTypeNode(
 						new IdentifierTypeNode('callable'),
 						[],
-						new IdentifierTypeNode('Foo')
+						new IdentifierTypeNode('Foo'),
 					),
 					new IdentifierTypeNode('Bar'),
 				]),
@@ -1254,7 +1252,7 @@ public function provideParseData(): array
 					new UnionTypeNode([
 						new IdentifierTypeNode('Foo'),
 						new IdentifierTypeNode('Bar'),
-					])
+					]),
 				),
 			],
 			[
@@ -1265,7 +1263,7 @@ public function provideParseData(): array
 					new IntersectionTypeNode([
 						new IdentifierTypeNode('Foo'),
 						new IdentifierTypeNode('Bar'),
-					])
+					]),
 				),
 			],
 			[
@@ -1277,9 +1275,9 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
-					])
+					]),
 				),
 			],
 			[
@@ -1292,24 +1290,24 @@ public function provideParseData(): array
 							true,
 							true,
 							'$a',
-							true
+							true,
 						),
 						new CallableTypeParameterNode(
 							new IdentifierTypeNode('B'),
 							true,
 							true,
 							'',
-							true
+							true,
 						),
 						new CallableTypeParameterNode(
 							new IdentifierTypeNode('C'),
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 					],
-					new IdentifierTypeNode('Foo')
+					new IdentifierTypeNode('Foo'),
 				),
 			],
 			[
@@ -1322,13 +1320,13 @@ public function provideParseData(): array
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 					],
 					new IdentifierTypeNode('C'),
 					[
 						new TemplateTagValueNode('A', null, ''),
-					]
+					],
 				),
 			],
 			[
@@ -1337,7 +1335,7 @@ public function provideParseData(): array
 					'>',
 					Lexer::TOKEN_END,
 					9,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -1350,14 +1348,14 @@ public function provideParseData(): array
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 						new CallableTypeParameterNode(
 							new IdentifierTypeNode('int'),
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 					],
 					new UnionTypeNode([
@@ -1366,7 +1364,7 @@ public function provideParseData(): array
 					]),
 					[
 						new TemplateTagValueNode('T', new IdentifierTypeNode('Model'), ''),
-					]
+					],
 				),
 			],
 			[
@@ -1379,26 +1377,26 @@ public function provideParseData(): array
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 						new CallableTypeParameterNode(
 							new IdentifierTypeNode('Ty'),
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 					],
 					new ArrayShapeNode([
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('Ty')
+							new IdentifierTypeNode('Ty'),
 						),
 						new ArrayShapeItemNode(
 							null,
 							false,
-							new IdentifierTypeNode('Tx')
+							new IdentifierTypeNode('Tx'),
 						),
 					]),
 					[
@@ -1407,7 +1405,7 @@ public function provideParseData(): array
 							new IdentifierTypeNode('Z'),
 						]), ''),
 						new TemplateTagValueNode('Ty', new IdentifierTypeNode('Y'), ''),
-					]
+					],
 				),
 			],
 			[
@@ -1425,7 +1423,7 @@ public function provideParseData(): array
 								[
 									GenericTypeNode::VARIANCE_INVARIANT,
 									GenericTypeNode::VARIANCE_INVARIANT,
-								]
+								],
 							),
 							new UnionTypeNode([
 								new IdentifierTypeNode('int'),
@@ -1438,17 +1436,17 @@ public function provideParseData(): array
 											],
 											[
 												GenericTypeNode::VARIANCE_INVARIANT,
-											]
+											],
 										),
 										new IdentifierTypeNode('bar'),
-									])
+									]),
 								),
 							]),
 						],
 						[
 							GenericTypeNode::VARIANCE_INVARIANT,
 							GenericTypeNode::VARIANCE_INVARIANT,
-						]
+						],
 					),
 					new IdentifierTypeNode('Lorem'),
 				]),
@@ -1462,20 +1460,20 @@ public function provideParseData(): array
 				'array[ int ]',
 				new OffsetAccessTypeNode(
 					new IdentifierTypeNode('array'),
-					new IdentifierTypeNode('int')
+					new IdentifierTypeNode('int'),
 				),
 			],
 			[
 				'self::TYPES[ int ]',
 				new OffsetAccessTypeNode(
 					new ConstTypeNode(new ConstFetchNode('self', 'TYPES')),
-					new IdentifierTypeNode('int')
+					new IdentifierTypeNode('int'),
 				),
 			],
 			[
 				"?\t\xA009", // edge-case with \h
 				new NullableTypeNode(
-					new IdentifierTypeNode("\xA009")
+					new IdentifierTypeNode("\xA009"),
 				),
 			],
 			[
@@ -1490,8 +1488,8 @@ public function provideParseData(): array
 						[
 							GenericTypeNode::VARIANCE_INVARIANT,
 							GenericTypeNode::VARIANCE_INVARIANT,
-						]
-					)
+						],
+					),
 				),
 			],
 			[
@@ -1508,8 +1506,8 @@ public function provideParseData(): array
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
-						)
+							],
+						),
 					),
 				]),
 			],
@@ -1520,9 +1518,9 @@ public function provideParseData(): array
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('foo'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
-					])
+					]),
 				),
 			],
 			[
@@ -1534,16 +1532,16 @@ public function provideParseData(): array
 							new ArrayShapeItemNode(
 								new IdentifierTypeNode('foo'),
 								false,
-								new IdentifierTypeNode('int')
+								new IdentifierTypeNode('int'),
 							),
-						])
+						]),
 					),
 				]),
 			],
 			[
 				'$this[]',
 				new ArrayTypeNode(
-					new ThisTypeNode()
+					new ThisTypeNode(),
 				),
 			],
 			[
@@ -1551,7 +1549,7 @@ public function provideParseData(): array
 				new UnionTypeNode([
 					new IdentifierTypeNode('int'),
 					new ArrayTypeNode(
-						new ThisTypeNode()
+						new ThisTypeNode(),
 					),
 				]),
 			],
@@ -1561,16 +1559,16 @@ public function provideParseData(): array
 					new IdentifierTypeNode('callable'),
 					[],
 					new ArrayTypeNode(
-						new IdentifierTypeNode('int')
-					)
+						new IdentifierTypeNode('int'),
+					),
 				),
 			],
 			[
 				'?int[]',
 				new NullableTypeNode(
 					new ArrayTypeNode(
-						new IdentifierTypeNode('int')
-					)
+						new IdentifierTypeNode('int'),
+					),
 				),
 			],
 			[
@@ -1707,7 +1705,7 @@ public function provideParseData(): array
 					],
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -1724,7 +1722,7 @@ public function provideParseData(): array
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
 						GenericTypeNode::VARIANCE_INVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -1740,7 +1738,7 @@ public function provideParseData(): array
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
 						GenericTypeNode::VARIANCE_INVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -1761,13 +1759,13 @@ public function provideParseData(): array
 							],
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
 					],
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
 						GenericTypeNode::VARIANCE_INVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -1788,13 +1786,13 @@ public function provideParseData(): array
 							],
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
 					],
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
 						GenericTypeNode::VARIANCE_INVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -1818,7 +1816,7 @@ public function provideParseData(): array
 					[
 						new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false),
 					],
-					new IdentifierTypeNode('void')
+					new IdentifierTypeNode('void'),
 				),
 			],
 			[
@@ -1832,7 +1830,7 @@ public function provideParseData(): array
 						new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false),
 						new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 					],
-					new IdentifierTypeNode('void')
+					new IdentifierTypeNode('void'),
 				),
 			],
 			[
@@ -1845,7 +1843,7 @@ public function provideParseData(): array
 						new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false),
 						new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 					],
-					new IdentifierTypeNode('void')
+					new IdentifierTypeNode('void'),
 				),
 			],
 			[
@@ -1865,15 +1863,15 @@ public function provideParseData(): array
 								[
 									new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 								],
-								new IdentifierTypeNode('void')
+								new IdentifierTypeNode('void'),
 							),
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 					],
-					new IdentifierTypeNode('void')
+					new IdentifierTypeNode('void'),
 				),
 			],
 			[
@@ -1893,15 +1891,15 @@ public function provideParseData(): array
 								[
 									new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 								],
-								new IdentifierTypeNode('void')
+								new IdentifierTypeNode('void'),
 							),
 							false,
 							false,
 							'',
-							false
+							false,
 						),
 					],
-					new IdentifierTypeNode('void')
+					new IdentifierTypeNode('void'),
 				),
 			],
 			[
@@ -1911,7 +1909,7 @@ public function provideParseData(): array
 					new IdentifierTypeNode('Bar'),
 					new IdentifierTypeNode('never'),
 					new IdentifierTypeNode('int'),
-					false
+					false,
 				),
 			],
 			[
@@ -1921,7 +1919,7 @@ public function provideParseData(): array
 					new IdentifierTypeNode('Bar'),
 					new IdentifierTypeNode('never'),
 					new IdentifierTypeNode('int'),
-					true
+					true,
 				),
 			],
 			[
@@ -1935,9 +1933,9 @@ public function provideParseData(): array
 						new ConstTypeNode(new ConstFetchNode('self', 'TYPE_INT')),
 						new IdentifierTypeNode('int'),
 						new IdentifierTypeNode('bool'),
-						false
+						false,
 					),
-					false
+					false,
 				),
 			],
 			[
@@ -1953,7 +1951,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('int'),
 						new IdentifierTypeNode('string'),
 					]),
-					false
+					false,
 				),
 			],
 			[
@@ -1974,7 +1972,7 @@ public function provideParseData(): array
 						[
 							GenericTypeNode::VARIANCE_INVARIANT,
 							GenericTypeNode::VARIANCE_INVARIANT,
-						]
+						],
 					),
 					new ConditionalTypeNode(
 						new IdentifierTypeNode('TRandList'),
@@ -1988,7 +1986,7 @@ public function provideParseData(): array
 							[
 								GenericTypeNode::VARIANCE_INVARIANT,
 								GenericTypeNode::VARIANCE_INVARIANT,
-							]
+							],
 						),
 						new UnionTypeNode([
 							new GenericTypeNode(
@@ -2000,7 +1998,7 @@ public function provideParseData(): array
 								[
 									GenericTypeNode::VARIANCE_INVARIANT,
 									GenericTypeNode::VARIANCE_INVARIANT,
-								]
+								],
 							),
 							new GenericTypeNode(
 								new IdentifierTypeNode('LimitIterator'),
@@ -2011,12 +2009,12 @@ public function provideParseData(): array
 								[
 									GenericTypeNode::VARIANCE_INVARIANT,
 									GenericTypeNode::VARIANCE_INVARIANT,
-								]
+								],
 							),
 						]),
-						false
+						false,
 					),
-					false
+					false,
 				),
 			],
 			[
@@ -2032,7 +2030,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('int'),
 						new IdentifierTypeNode('string'),
 					]),
-					false
+					false,
 				),
 			],
 			[
@@ -2052,7 +2050,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('int'),
 						new IdentifierTypeNode('string'),
 					]),
-					false
+					false,
 				),
 			],
 			[
@@ -2061,9 +2059,9 @@ public function provideParseData(): array
 					new ConstTypeNode(
 						new ConstFetchNode(
 							'Currency',
-							'CURRENCY_*'
-						)
-					)
+							'CURRENCY_*',
+						),
+					),
 				),
 			],
 			[
@@ -2077,9 +2075,9 @@ public function provideParseData(): array
 						new IdentifierTypeNode('Bar'),
 						new IdentifierTypeNode('false'),
 						new IdentifierTypeNode('null'),
-						false
+						false,
 					),
-					false
+					false,
 				),
 			],
 			[
@@ -2088,7 +2086,7 @@ public function provideParseData(): array
 					'is',
 					Lexer::TOKEN_IDENTIFIER,
 					14,
-					Lexer::TOKEN_COLON
+					Lexer::TOKEN_COLON,
 				),
 			],
 			[
@@ -2102,9 +2100,9 @@ public function provideParseData(): array
 						new IdentifierTypeNode('Bar'),
 						new IdentifierTypeNode('false'),
 						new IdentifierTypeNode('null'),
-						false
+						false,
 					),
-					false
+					false,
 				),
 			],
 			[
@@ -2113,7 +2111,7 @@ public function provideParseData(): array
 					'$foo',
 					Lexer::TOKEN_VARIABLE,
 					15,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -2127,7 +2125,7 @@ public function provideParseData(): array
 					[
 						GenericTypeNode::VARIANCE_COVARIANT,
 						GenericTypeNode::VARIANCE_INVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -2141,7 +2139,7 @@ public function provideParseData(): array
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
 						GenericTypeNode::VARIANCE_CONTRAVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -2150,7 +2148,7 @@ public function provideParseData(): array
 					'>',
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					13,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -2159,7 +2157,7 @@ public function provideParseData(): array
 					'Bar',
 					Lexer::TOKEN_IDENTIFIER,
 					16,
-					Lexer::TOKEN_CLOSE_ANGLE_BRACKET
+					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 				),
 			],
 			[
@@ -2173,7 +2171,7 @@ public function provideParseData(): array
 					[
 						GenericTypeNode::VARIANCE_INVARIANT,
 						GenericTypeNode::VARIANCE_BIVARIANT,
-					]
+					],
 				),
 			],
 			[
@@ -2182,7 +2180,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -2193,8 +2191,8 @@ public function provideParseData(): array
 						new IdentifierTypeNode('a'),
 						false,
 						new NullableTypeNode(
-							new IdentifierTypeNode('int')
-						)
+							new IdentifierTypeNode('int'),
+						),
 					),
 				]),
 			],
@@ -2205,8 +2203,8 @@ public function provideParseData(): array
 						new IdentifierTypeNode('a'),
 						true,
 						new NullableTypeNode(
-							new IdentifierTypeNode('int')
-						)
+							new IdentifierTypeNode('int'),
+						),
 					),
 				]),
 			],
@@ -2216,12 +2214,12 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -2231,7 +2229,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('b'),
@@ -2243,10 +2241,10 @@ public function provideParseData(): array
 								new CallableTypeNode(
 									new IdentifierTypeNode('callable'),
 									[],
-									new IdentifierTypeNode('int')
-								)
+									new IdentifierTypeNode('int'),
+								),
 							),
-						])
+						]),
 					),
 				]),
 			],
@@ -2256,7 +2254,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('b'),
@@ -2268,10 +2266,10 @@ public function provideParseData(): array
 								new CallableTypeNode(
 									new IdentifierTypeNode('callable'),
 									[],
-									new IdentifierTypeNode('int')
-								)
+									new IdentifierTypeNode('int'),
+								),
 							),
-						])
+						]),
 					),
 				]),
 			],
@@ -2282,9 +2280,9 @@ public function provideParseData(): array
 						new ObjectShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
-					])
+					]),
 				),
 			],
 			[
@@ -2293,7 +2291,7 @@ public function provideParseData(): array
 					'',
 					Lexer::TOKEN_END,
 					7,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -2302,7 +2300,7 @@ public function provideParseData(): array
 					'=>',
 					Lexer::TOKEN_OTHER,
 					9,
-					Lexer::TOKEN_COLON
+					Lexer::TOKEN_COLON,
 				),
 			],
 			[
@@ -2311,7 +2309,7 @@ public function provideParseData(): array
 					'}',
 					Lexer::TOKEN_CLOSE_CURLY_BRACKET,
 					10,
-					Lexer::TOKEN_COLON
+					Lexer::TOKEN_COLON,
 				),
 			],
 			[
@@ -2320,7 +2318,7 @@ public function provideParseData(): array
 					'0',
 					Lexer::TOKEN_END,
 					7,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -2329,7 +2327,7 @@ public function provideParseData(): array
 					'0',
 					Lexer::TOKEN_END,
 					7,
-					Lexer::TOKEN_IDENTIFIER
+					Lexer::TOKEN_IDENTIFIER,
 				),
 			],
 			[
@@ -2338,7 +2336,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new ConstExprStringNode('a', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -2348,7 +2346,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -2358,7 +2356,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new ConstExprStringNode('$ref', ConstExprStringNode::SINGLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -2368,7 +2366,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new ConstExprStringNode('$ref', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -2380,7 +2378,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -2392,7 +2390,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 				]),
 			],
@@ -2405,12 +2403,12 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -2424,17 +2422,17 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('c'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -2447,12 +2445,12 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
-						new IdentifierTypeNode('int')
+						new IdentifierTypeNode('int'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -2463,9 +2461,9 @@ public function provideParseData(): array
 						new ObjectShapeItemNode(
 							new IdentifierTypeNode('foo'),
 							false,
-							new IdentifierTypeNode('int')
+							new IdentifierTypeNode('int'),
 						),
-					])
+					]),
 				),
 			],
 			[
@@ -2477,9 +2475,9 @@ public function provideParseData(): array
 							new ObjectShapeItemNode(
 								new IdentifierTypeNode('foo'),
 								false,
-								new IdentifierTypeNode('int')
+								new IdentifierTypeNode('int'),
 							),
-						])
+						]),
 					),
 				]),
 			],
@@ -2501,12 +2499,12 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('attribute'),
 						false,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('value'),
 						true,
-						new IdentifierTypeNode('string')
+						new IdentifierTypeNode('string'),
 					),
 				]),
 			],
@@ -2522,8 +2520,8 @@ public function provideParseData(): array
 						[
 							new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false),
 						],
-						new IdentifierTypeNode('Bar')
-					)
+						new IdentifierTypeNode('Bar'),
+					),
 				),
 			],
 			[
@@ -2544,8 +2542,8 @@ public function provideParseData(): array
 					new ArrayTypeNode(
 						new ObjectShapeNode([
 							new ObjectShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
-						])
-					)
+						]),
+					),
 				),
 			],
 			[
@@ -2560,8 +2558,8 @@ public function provideParseData(): array
 				'(callable(): int[])[][]',
 				new ArrayTypeNode(
 					new ArrayTypeNode(
-						new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int')))
-					)
+						new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int'))),
+					),
 				),
 			],
 			[
@@ -2586,7 +2584,7 @@ public function provideParseData(): array
 			[
 				'callable(): 3.5[]',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(
-					new ConstTypeNode(new ConstExprFloatNode('3.5'))
+					new ConstTypeNode(new ConstExprFloatNode('3.5')),
 				)),
 			],
 			[
@@ -2683,7 +2681,7 @@ public function provideParseData(): array
 					new GenericTypeNode(new IdentifierTypeNode('class-string'), [new IdentifierTypeNode('TService')], ['invariant']),
 					new IdentifierTypeNode('TService'),
 					new IdentifierTypeNode('mixed'),
-					false
+					false,
 				)),
 			],
 			[
@@ -2703,9 +2701,7 @@ public function dataLinesAndIndexes(): iterable
 			'int | object{foo: int}[]',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'int | object{foo: int}[]',
 					1,
 					1,
@@ -2713,9 +2709,7 @@ static function (TypeNode $typeNode): TypeNode {
 					12,
 				],
 				[
-					static function (UnionTypeNode $typeNode): TypeNode {
-						return $typeNode->types[0];
-					},
+					static fn (UnionTypeNode $typeNode): TypeNode => $typeNode->types[0],
 					'int',
 					1,
 					1,
@@ -2723,9 +2717,7 @@ static function (UnionTypeNode $typeNode): TypeNode {
 					0,
 				],
 				[
-					static function (UnionTypeNode $typeNode): TypeNode {
-						return $typeNode->types[1];
-					},
+					static fn (UnionTypeNode $typeNode): TypeNode => $typeNode->types[1],
 					'object{foo: int}[]',
 					1,
 					1,
@@ -2739,9 +2731,7 @@ static function (UnionTypeNode $typeNode): TypeNode {
 			'int | object{foo: int}[]    ',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'int | object{foo: int}[]',
 					1,
 					1,
@@ -2749,9 +2739,7 @@ static function (TypeNode $typeNode): TypeNode {
 					12,
 				],
 				[
-					static function (UnionTypeNode $typeNode): TypeNode {
-						return $typeNode->types[0];
-					},
+					static fn (UnionTypeNode $typeNode): TypeNode => $typeNode->types[0],
 					'int',
 					1,
 					1,
@@ -2759,9 +2747,7 @@ static function (UnionTypeNode $typeNode): TypeNode {
 					0,
 				],
 				[
-					static function (UnionTypeNode $typeNode): TypeNode {
-						return $typeNode->types[1];
-					},
+					static fn (UnionTypeNode $typeNode): TypeNode => $typeNode->types[1],
 					'object{foo: int}[]',
 					1,
 					1,
@@ -2778,9 +2764,7 @@ static function (UnionTypeNode $typeNode): TypeNode {
 			 }',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'array{
 				a: int,
 				b: string
@@ -2797,9 +2781,7 @@ static function (TypeNode $typeNode): TypeNode {
 			'callable(Foo, Bar): void',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'callable(Foo, Bar): void',
 					1,
 					1,
@@ -2807,9 +2789,7 @@ static function (TypeNode $typeNode): TypeNode {
 					9,
 				],
 				[
-					static function (CallableTypeNode $typeNode): TypeNode {
-						return $typeNode->identifier;
-					},
+					static fn (CallableTypeNode $typeNode): TypeNode => $typeNode->identifier,
 					'callable',
 					1,
 					1,
@@ -2817,9 +2797,7 @@ static function (CallableTypeNode $typeNode): TypeNode {
 					0,
 				],
 				[
-					static function (CallableTypeNode $typeNode): Node {
-						return $typeNode->parameters[0];
-					},
+					static fn (CallableTypeNode $typeNode): Node => $typeNode->parameters[0],
 					'Foo',
 					1,
 					1,
@@ -2827,9 +2805,7 @@ static function (CallableTypeNode $typeNode): Node {
 					2,
 				],
 				[
-					static function (CallableTypeNode $typeNode): TypeNode {
-						return $typeNode->returnType;
-					},
+					static fn (CallableTypeNode $typeNode): TypeNode => $typeNode->returnType,
 					'void',
 					1,
 					1,
@@ -2843,9 +2819,7 @@ static function (CallableTypeNode $typeNode): TypeNode {
 			'$this',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'$this',
 					1,
 					1,
@@ -2859,9 +2833,7 @@ static function (TypeNode $typeNode): TypeNode {
 			'array{foo: int}',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'array{foo: int}',
 					1,
 					1,
@@ -2869,9 +2841,7 @@ static function (TypeNode $typeNode): TypeNode {
 					6,
 				],
 				[
-					static function (ArrayShapeNode $typeNode): TypeNode {
-						return $typeNode->items[0];
-					},
+					static fn (ArrayShapeNode $typeNode): TypeNode => $typeNode->items[0],
 					'foo: int',
 					1,
 					1,
@@ -2885,9 +2855,7 @@ static function (ArrayShapeNode $typeNode): TypeNode {
 			'array{}',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'array{}',
 					1,
 					1,
@@ -2901,9 +2869,7 @@ static function (TypeNode $typeNode): TypeNode {
 			'object{foo: int}',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'object{foo: int}',
 					1,
 					1,
@@ -2911,9 +2877,7 @@ static function (TypeNode $typeNode): TypeNode {
 					6,
 				],
 				[
-					static function (ObjectShapeNode $typeNode): TypeNode {
-						return $typeNode->items[0];
-					},
+					static fn (ObjectShapeNode $typeNode): TypeNode => $typeNode->items[0],
 					'foo: int',
 					1,
 					1,
@@ -2927,9 +2891,7 @@ static function (ObjectShapeNode $typeNode): TypeNode {
 			'object{}',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'object{}',
 					1,
 					1,
@@ -2943,9 +2905,7 @@ static function (TypeNode $typeNode): TypeNode {
 			'object{}[]',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'object{}[]',
 					1,
 					1,
@@ -2959,9 +2919,7 @@ static function (TypeNode $typeNode): TypeNode {
 			'int[][][]',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'int[][][]',
 					1,
 					1,
@@ -2969,9 +2927,7 @@ static function (TypeNode $typeNode): TypeNode {
 					6,
 				],
 				[
-					static function (ArrayTypeNode $typeNode): TypeNode {
-						return $typeNode->type;
-					},
+					static fn (ArrayTypeNode $typeNode): TypeNode => $typeNode->type,
 					'int[][]',
 					1,
 					1,
@@ -3016,9 +2972,7 @@ static function (ArrayTypeNode $typeNode): TypeNode {
 			'int[foo][bar][baz]',
 			[
 				[
-					static function (TypeNode $typeNode): TypeNode {
-						return $typeNode;
-					},
+					static fn (TypeNode $typeNode): TypeNode => $typeNode,
 					'int[foo][bar][baz]',
 					1,
 					1,
@@ -3026,9 +2980,7 @@ static function (TypeNode $typeNode): TypeNode {
 					9,
 				],
 				[
-					static function (OffsetAccessTypeNode $typeNode): TypeNode {
-						return $typeNode->type;
-					},
+					static fn (OffsetAccessTypeNode $typeNode): TypeNode => $typeNode->type,
 					'int[foo][bar]',
 					1,
 					1,
@@ -3036,9 +2988,7 @@ static function (OffsetAccessTypeNode $typeNode): TypeNode {
 					6,
 				],
 				[
-					static function (OffsetAccessTypeNode $typeNode): TypeNode {
-						return $typeNode->offset;
-					},
+					static fn (OffsetAccessTypeNode $typeNode): TypeNode => $typeNode->offset,
 					'baz',
 					1,
 					1,
diff --git a/tests/PHPStan/Printer/DifferTest.php b/tests/PHPStan/Printer/DifferTest.php
index b06f1692..cca1f61c 100644
--- a/tests/PHPStan/Printer/DifferTest.php
+++ b/tests/PHPStan/Printer/DifferTest.php
@@ -44,9 +44,7 @@ private function formatDiffString(array $diff): string
 	 */
 	public function testDiff(string $oldStr, string $newStr, string $expectedDiffStr): void
 	{
-		$differ = new Differ(static function ($a, $b) {
-			return $a === $b;
-		});
+		$differ = new Differ(static fn ($a, $b) => $a === $b);
 		$diff = $differ->diff(str_split($oldStr), str_split($newStr));
 		$this->assertSame($expectedDiffStr, $this->formatDiffString($diff));
 	}
@@ -72,9 +70,7 @@ public function provideTestDiff(): array
 	 */
 	public function testDiffWithReplacements(string $oldStr, string $newStr, string $expectedDiffStr): void
 	{
-		$differ = new Differ(static function ($a, $b) {
-			return $a === $b;
-		});
+		$differ = new Differ(static fn ($a, $b) => $a === $b);
 		$diff = $differ->diffWithReplacements(str_split($oldStr), str_split($newStr));
 		$this->assertSame($expectedDiffStr, $this->formatDiffString($diff));
 	}
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index 52fab1d1..36ad360b 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -41,7 +41,7 @@ public function enterNode(Node $node)
 						new IdentifierTypeNode('Bar'),
 						false,
 						'$b',
-						''
+						'',
 					));
 				}
 				return $node;
@@ -83,8 +83,7 @@ public function testPrint(string $file, string $expectedFile, NodeVisitor $visit
 		$phpTraverser2 = new PhpParserNodeTraverser();
 		$phpTraverser2->addVisitor(new class ($visitor) extends NodeVisitorAbstract {
 
-			/** @var NodeVisitor */
-			private $visitor;
+			private NodeVisitor $visitor;
 
 			public function __construct(NodeVisitor $visitor)
 			{
@@ -104,7 +103,7 @@ public function enterNode(PhpNode $phpNode)
 				$phpDocParser = new PhpDocParser(
 					new TypeParser($constExprParser, $usedAttributes),
 					$constExprParser,
-					$usedAttributes
+					$usedAttributes,
 				);
 				$lexer = new Lexer();
 				$tokens = new TokenIterator($lexer->tokenize($phpDoc));
diff --git a/tests/PHPStan/Printer/PhpPrinter.php b/tests/PHPStan/Printer/PhpPrinter.php
index d691a981..e904313d 100644
--- a/tests/PHPStan/Printer/PhpPrinter.php
+++ b/tests/PHPStan/Printer/PhpPrinter.php
@@ -9,11 +9,9 @@
 class PhpPrinter extends Standard
 {
 
-	/** @var string */
-	private $indentCharacter = ' ';
+	private string $indentCharacter = ' ';
 
-	/** @var int */
-	private $indentSize = 4;
+	private int $indentSize = 4;
 
 	protected function resetState(): void
 	{
diff --git a/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php b/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php
index 58a807eb..d0da8f46 100644
--- a/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php
+++ b/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php
@@ -16,14 +16,11 @@
 class PhpPrinterIndentationDetectorVisitor extends NodeVisitorAbstract
 {
 
-	/** @var string */
-	public $indentCharacter = ' ';
+	public string $indentCharacter = ' ';
 
-	/** @var int */
-	public $indentSize = 4;
+	public int $indentSize = 4;
 
-	/** @var TokenStream */
-	private $origTokens;
+	private TokenStream $origTokens;
 
 	public function __construct(TokenStream $origTokens)
 	{
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 1c4f4cd1..7ed808c7 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -59,11 +59,9 @@
 class PrinterTest extends TestCase
 {
 
-	/** @var TypeParser */
-	private $typeParser;
+	private TypeParser $typeParser;
 
-	/** @var PhpDocParser */
-	private $phpDocParser;
+	private PhpDocParser $phpDocParser;
 
 	protected function setUp(): void
 	{
@@ -73,7 +71,7 @@ protected function setUp(): void
 		$this->phpDocParser = new PhpDocParser(
 			$this->typeParser,
 			$constExprParser,
-			$usedAttributes
+			$usedAttributes,
 		);
 	}
 
@@ -600,7 +598,7 @@ public function enterNode(Node $node)
 					$node->templateTypes[] = new TemplateTagValueNode(
 						'T',
 						new IdentifierTypeNode('int'),
-						''
+						'',
 					);
 				}
 
@@ -1191,7 +1189,7 @@ public function enterNode(Node $node)
 					$node->templateTypes[] = new TemplateTagValueNode(
 						'T',
 						new IdentifierTypeNode('int'),
-						''
+						'',
 					);
 				}
 
@@ -1212,27 +1210,24 @@ public function enterNode(Node $node)
 			$addMethodTemplateType,
 		];
 
-		$changeCallableReturnTypeFactory = function (TypeNode $type): NodeVisitor {
-			return new class ($type) extends AbstractNodeVisitor {
+		$changeCallableReturnTypeFactory = static fn (TypeNode $type): NodeVisitor => new class ($type) extends AbstractNodeVisitor {
 
-				/** @var TypeNode */
-				private $type;
+			private TypeNode $type;
 
-				public function __construct(TypeNode $type)
-				{
-					$this->type = $type;
-				}
-
-				public function enterNode(Node $node)
-				{
-					if ($node instanceof CallableTypeNode) {
-						$node->returnType = $this->type;
-					}
+			public function __construct(TypeNode $type)
+			{
+				$this->type = $type;
+			}
 
-					return $node;
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof CallableTypeNode) {
+					$node->returnType = $this->type;
 				}
 
-			};
+				return $node;
+			}
+
 		};
 
 		yield [
@@ -1265,44 +1260,38 @@ public function enterNode(Node $node)
 			])),
 		];
 
-		$changeCallableReturnTypeCallbackFactory = function (callable $callback): NodeVisitor {
-			return new class ($callback) extends AbstractNodeVisitor {
+		$changeCallableReturnTypeCallbackFactory = fn (callable $callback): NodeVisitor => new class ($callback) extends AbstractNodeVisitor {
 
-				/** @var callable(TypeNode): TypeNode */
-				private $callback;
-
-				public function __construct(callable $callback)
-				{
-					$this->callback = $callback;
-				}
+			/** @var callable(TypeNode): TypeNode */
+			private $callback;
 
-				public function enterNode(Node $node)
-				{
-					if ($node instanceof CallableTypeNode) {
-						$cb = $this->callback;
-						$node->returnType = $cb($node->returnType);
-					}
+			public function __construct(callable $callback)
+			{
+				$this->callback = $callback;
+			}
 
-					return $node;
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof CallableTypeNode) {
+					$cb = $this->callback;
+					$node->returnType = $cb($node->returnType);
 				}
 
-			};
+				return $node;
+			}
+
 		};
 
 		yield [
 			'/** @param callable(): int $a */',
 			'/** @param callable(): string $a */',
-			$changeCallableReturnTypeCallbackFactory(static function (TypeNode $typeNode): TypeNode {
-				return new IdentifierTypeNode('string');
-			}),
+			$changeCallableReturnTypeCallbackFactory(static fn (TypeNode $typeNode) => new IdentifierTypeNode('string')),
 		];
 
 		yield [
 			'/** @param callable(): (int) $a */',
 			'/** @param callable(): string $a */',
-			$changeCallableReturnTypeCallbackFactory(static function (TypeNode $typeNode): TypeNode {
-				return new IdentifierTypeNode('string');
-			}),
+			$changeCallableReturnTypeCallbackFactory(static fn (TypeNode $typeNode) => new IdentifierTypeNode('string')),
 		];
 
 		yield [
@@ -1885,7 +1874,7 @@ public function testPrintFormatPreserving(string $phpDoc, string $expectedResult
 
 		$this->assertEquals(
 			$this->unsetAttributes($newNode),
-			$this->unsetAttributes($this->phpDocParser->parse(new TokenIterator($lexer->tokenize($newPhpDoc))))
+			$this->unsetAttributes($this->phpDocParser->parse(new TokenIterator($lexer->tokenize($newPhpDoc)))),
 		);
 	}
 
@@ -1941,7 +1930,7 @@ public function dataPrintType(): iterable
 				[
 					GenericTypeNode::VARIANCE_INVARIANT,
 					GenericTypeNode::VARIANCE_INVARIANT,
-				]
+				],
 			),
 			'array<int, int|string>',
 		];
@@ -1959,8 +1948,8 @@ public function dataPrintType(): iterable
 		yield [
 			new ArrayTypeNode(
 				new ArrayTypeNode(
-					new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int')))
-				)
+					new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int'))),
+				),
 			),
 			'(callable(): int[])[][]',
 		];
@@ -2003,7 +1992,7 @@ public function dataPrintType(): iterable
 		yield [
 			new OffsetAccessTypeNode(
 				new ConstTypeNode(new ConstFetchNode('self', 'TYPES')),
-				new IdentifierTypeNode('int')
+				new IdentifierTypeNode('int'),
 			),
 			'self::TYPES[int]',
 		];
@@ -2021,7 +2010,7 @@ public function testPrintType(TypeNode $node, string $expectedResult): void
 		$lexer = new Lexer();
 		$this->assertEquals(
 			$this->unsetAttributes($node),
-			$this->unsetAttributes($this->typeParser->parse(new TokenIterator($lexer->tokenize($phpDoc))))
+			$this->unsetAttributes($this->typeParser->parse(new TokenIterator($lexer->tokenize($phpDoc)))),
 		);
 	}
 
@@ -2036,7 +2025,7 @@ public function dataPrintPhpDocNode(): iterable
 					new IdentifierTypeNode('int'),
 					false,
 					'$a',
-					''
+					'',
 				)),
 			]),
 			'/**
@@ -2057,7 +2046,7 @@ public function testPrintPhpDocNode(PhpDocNode $node, string $expectedResult): v
 		$lexer = new Lexer();
 		$this->assertEquals(
 			$this->unsetAttributes($node),
-			$this->unsetAttributes($this->phpDocParser->parse(new TokenIterator($lexer->tokenize($phpDoc))))
+			$this->unsetAttributes($this->phpDocParser->parse(new TokenIterator($lexer->tokenize($phpDoc)))),
 		);
 	}
 

From 010d10a40beb624a2e906cb98502c2e88d5970fb Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 7 Sep 2024 22:35:37 +0200
Subject: [PATCH 09/45] Deploy 2.0.x API reference

---
 .github/workflows/apiref.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/apiref.yml b/.github/workflows/apiref.yml
index 24e6cd97..f9384b3f 100644
--- a/.github/workflows/apiref.yml
+++ b/.github/workflows/apiref.yml
@@ -5,7 +5,7 @@ name: "Deploy API Reference"
 on:
   push:
     branches:
-      - "1.23.x"
+      - "2.0.x"
 
 concurrency:
   group: "pages"

From fdb8bc4ba2b87abfb05b00b3144d9c32722b0e26 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 7 Sep 2024 17:31:44 +0200
Subject: [PATCH 10/45] Text between tags always belongs to description

---
 src/Parser/PhpDocParser.php               | 86 +++++++++++++--------
 tests/PHPStan/Parser/PhpDocParserTest.php | 94 +++++++++++------------
 2 files changed, 101 insertions(+), 79 deletions(-)

diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index d3b7dcff..3dd15264 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -39,16 +39,13 @@ class PhpDocParser
 
 	private bool $useIndexAttributes;
 
-	private bool $textBetweenTagsBelongsToDescription;
-
 	/**
 	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
 	 */
 	public function __construct(
 		TypeParser $typeParser,
 		ConstExprParser $constantExprParser,
-		array $usedAttributes = [],
-		bool $textBetweenTagsBelongsToDescription = false
+		array $usedAttributes = []
 	)
 	{
 		$this->typeParser = $typeParser;
@@ -56,7 +53,6 @@ public function __construct(
 		$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
 		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
 		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
-		$this->textBetweenTagsBelongsToDescription = $textBetweenTagsBelongsToDescription;
 	}
 
 
@@ -164,7 +160,7 @@ private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
 
 		$startLine = $tokens->currentTokenLine();
 		$startIndex = $tokens->currentTokenIndex();
-		$text = $this->parseText($tokens);
+		$text = $this->parsePhpDocTextNode($tokens);
 
 		return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex);
 	}
@@ -194,15 +190,12 @@ private function parseText(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode
 	{
 		$text = '';
 
-		$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
-		if ($this->textBetweenTagsBelongsToDescription) {
-			$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
-		}
+		$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
 
 		$savepoint = false;
 
 		// if the next token is EOL, everything below is skipped and empty string is returned
-		while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+		while (true) {
 			$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
 			$text .= $tmpText;
 
@@ -211,14 +204,12 @@ private function parseText(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode
 				break;
 			}
 
-			if ($this->textBetweenTagsBelongsToDescription) {
-				if (!$savepoint) {
-					$tokens->pushSavePoint();
-					$savepoint = true;
-				} elseif ($tmpText !== '') {
-					$tokens->dropSavePoint();
-					$tokens->pushSavePoint();
-				}
+			if (!$savepoint) {
+				$tokens->pushSavePoint();
+				$savepoint = true;
+			} elseif ($tmpText !== '') {
+				$tokens->dropSavePoint();
+				$tokens->pushSavePoint();
 			}
 
 			$tokens->pushSavePoint();
@@ -250,15 +241,12 @@ private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens)
 	{
 		$text = '';
 
-		$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
-		if ($this->textBetweenTagsBelongsToDescription) {
-			$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
-		}
+		$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
 
 		$savepoint = false;
 
 		// if the next token is EOL, everything below is skipped and empty string is returned
-		while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+		while (true) {
 			$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
 			$text .= $tmpText;
 
@@ -297,14 +285,12 @@ private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens)
 				break;
 			}
 
-			if ($this->textBetweenTagsBelongsToDescription) {
-				if (!$savepoint) {
-					$tokens->pushSavePoint();
-					$savepoint = true;
-				} elseif ($tmpText !== '') {
-					$tokens->dropSavePoint();
-					$tokens->pushSavePoint();
-				}
+			if (!$savepoint) {
+				$tokens->pushSavePoint();
+				$savepoint = true;
+			} elseif ($tmpText !== '') {
+				$tokens->dropSavePoint();
+				$tokens->pushSavePoint();
 			}
 
 			$tokens->pushSavePoint();
@@ -332,6 +318,42 @@ private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens)
 	}
 
 
+	private function parsePhpDocTextNode(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode
+	{
+		$text = '';
+
+		$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
+
+		// if the next token is EOL, everything below is skipped and empty string is returned
+		while (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+			$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
+			$text .= $tmpText;
+
+			// stop if we're not at EOL - meaning it's the end of PHPDoc
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC)) {
+				break;
+			}
+
+			$tokens->pushSavePoint();
+			$tokens->next();
+
+			// if we're at EOL, check what's next
+			// if next is a PHPDoc tag, EOL, or end of PHPDoc, stop
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) {
+				$tokens->rollback();
+				break;
+			}
+
+			// otherwise if the next is text, continue building the description string
+
+			$tokens->dropSavePoint();
+			$text .= $tokens->getDetectedNewline() ?? "\n";
+		}
+
+		return new Ast\PhpDoc\PhpDocTextNode(trim($text, " \t"));
+	}
+
+
 	public function parseTag(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagNode
 	{
 		$tag = $tokens->currentTokenValue();
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 426ac073..849ba674 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -2801,11 +2801,9 @@ public function provideSingleLinePhpDocData(): Iterator
 			new PhpDocNode([
 				new PhpDocTagNode(
 					'@example',
-					new GenericTagValueNode(''),
-				),
-				new PhpDocTextNode(
-					'entity_managers:' . PHP_EOL .
-					'    default:',
+					new GenericTagValueNode(PHP_EOL .
+						'  entity_managers:' . PHP_EOL .
+						'    default:'),
 				),
 			]),
 		];
@@ -2955,26 +2953,27 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Foo'),
 							false,
 							'$foo',
-							'1st multi world description with empty lines',
+							'1st multi world description with empty lines' . PHP_EOL .
+							PHP_EOL .
+							PHP_EOL .
+							'some text in the middle',
 						),
 					),
 					new PhpDocTextNode(''),
 					new PhpDocTextNode(''),
-					new PhpDocTextNode('some text in the middle'),
-					new PhpDocTextNode(''),
-					new PhpDocTextNode(''),
 					new PhpDocTagNode(
 						'@param',
 						new ParamTagValueNode(
 							new IdentifierTypeNode('Bar'),
 							false,
 							'$bar',
-							'2nd multi world description with empty lines',
+							'2nd multi world description with empty lines' . PHP_EOL .
+							PHP_EOL .
+							PHP_EOL .
+							'test',
 						),
 					),
-					new PhpDocTextNode(''),
-					new PhpDocTextNode(''),
-					new PhpDocTextNode('test'),
+
 				]),
 			],
 			[
@@ -3803,10 +3802,18 @@ public function provideMultiLinePhpDocData(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', '')),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('test'),
+				new PhpDocTagNode(
+					'@param',
+					new ParamTagValueNode(
+						new IdentifierTypeNode('int'),
+						false,
+						'$a',
+						PHP_EOL
+						. PHP_EOL
+						. PHP_EOL
+						. 'test',
+					),
+				),
 			]),
 		];
 	}
@@ -4941,19 +4948,15 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('\Drupal\Core\Field\FieldStorageDefinitionInterface'),
 						false,
 						'$field_definition',
-						'',
+						PHP_EOL . '  The field definition.',
 					),
 				),
-				new PhpDocTextNode('The field definition.'),
 				new PhpDocTextNode(''),
 				new PhpDocTagNode(
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('array'),
-						'',
-					),
-				),
-				new PhpDocTextNode("An empty array if there is no schema, or an associative array with the
+						PHP_EOL . "  An empty array if there is no schema, or an associative array with the
   following key/value pairs:
   - columns: An array of Schema API column specifications, keyed by column
     name. The columns need to be a subset of the properties defined in
@@ -4975,7 +4978,9 @@ public function provideRealWorldExampleData(): Iterator
     definitions. Note, however, that the field data is not necessarily
     stored in SQL. Also, the possible usage is limited, as you cannot
     specify another field as related, only existing SQL tables,
-    such as {taxonomy_term_data}."),
+    such as {taxonomy_term_data}.",
+					),
+				),
 			]),
 		];
 
@@ -5693,9 +5698,8 @@ public function provideCommentLikeDescriptions(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$a',
-					'',
+					PHP_EOL . '// this is a comment',
 				)),
-				new PhpDocTextNode('// this is a comment'),
 			]),
 		];
 		yield [
@@ -5710,10 +5714,8 @@ public function provideCommentLikeDescriptions(): Iterator
 					new IdentifierTypeNode('int'),
 					false,
 					'$a',
-					'',
+					PHP_EOL . PHP_EOL . '// this is a comment',
 				)),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('// this is a comment'),
 			]),
 		];
 		yield [
@@ -5918,10 +5920,10 @@ public function provideDoctrineData(): Iterator
 					'@X',
 					new DoctrineTagValueNode(
 						new DoctrineAnnotation('@X', []),
-						'',
+						PHP_EOL .
+						'Content',
 					),
 				),
-				new PhpDocTextNode('Content'),
 			]),
 			[new Doctrine\X()],
 		];
@@ -6075,9 +6077,8 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@X',
 					[],
-				), 'test')),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('test2'),
+				), 'test' . PHP_EOL .
+					PHP_EOL . 'test2')),
 			]),
 			[new Doctrine\X()],
 		];
@@ -6093,9 +6094,8 @@ public function provideDoctrineData(): Iterator
 				new PhpDocTagNode('@X', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@X',
 					[],
-				), 'test')),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('test2'),
+				), 'test' . PHP_EOL .
+					PHP_EOL . 'test2')),
 				new PhpDocTagNode('@Z', new DoctrineTagValueNode(new DoctrineAnnotation(
 					'@Z',
 					[],
@@ -6134,9 +6134,9 @@ public function provideDoctrineData(): Iterator
 			' * test2' . PHP_EOL .
 			' */',
 			new PhpDocNode([
-				new PhpDocTagNode('@X', new GenericTagValueNode('test')),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('test2'),
+				new PhpDocTagNode('@X', new GenericTagValueNode('test' . PHP_EOL .
+					PHP_EOL .
+					'test2')),
 			]),
 			[new Doctrine\X()],
 		];
@@ -6149,9 +6149,9 @@ public function provideDoctrineData(): Iterator
 			' * @Z' . PHP_EOL .
 			' */',
 			new PhpDocNode([
-				new PhpDocTagNode('@X', new GenericTagValueNode('test')),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('test2'),
+				new PhpDocTagNode('@X', new GenericTagValueNode('test' . PHP_EOL .
+					PHP_EOL .
+					'test2')),
 				new PhpDocTagNode('@Z', new GenericTagValueNode('')),
 			]),
 			[new Doctrine\X(), new Doctrine\Z()],
@@ -7309,7 +7309,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 					new DeprecatedTagValueNode('in Drupal 8.6.0 and will be removed before Drupal 9.0.0. In
   Drupal 9 there will be no way to set the status and in Drupal 8 this
   ability has been removed because mb_*() functions are supplied using
-  Symfony\'s polyfill.')
+  Symfony\'s polyfill.'),
 				),
 			]),
 		];
@@ -7327,8 +7327,8 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 						'in Drupal 8.6.0 and will be removed before Drupal 9.0.0. In
   Drupal 9 there will be no way to set the status and in Drupal 8 this
   ability has been removed because mb_*() functions are supplied using
-  Symfony\'s polyfill.'
-					)
+  Symfony\'s polyfill.',
+					),
 				),
 			]),
 		];
@@ -7344,7 +7344,7 @@ public function testTextBetweenTagsBelongsToDescription(
 	{
 		$constExprParser = new ConstExprParser();
 		$typeParser = new TypeParser($constExprParser);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, [], true);
+		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, []);
 
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 		$actualPhpDocNode = $phpDocParser->parse($tokens);

From 938b7fa87d872df1d55cf4a3033964b1a1e4ff3d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 7 Sep 2024 23:00:17 +0200
Subject: [PATCH 11/45] Fix Windows tests

---
 tests/PHPStan/Parser/PhpDocParserTest.php | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 849ba674..aaf3d2b6 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -2953,10 +2953,10 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Foo'),
 							false,
 							'$foo',
-							'1st multi world description with empty lines' . PHP_EOL .
-							PHP_EOL .
-							PHP_EOL .
-							'some text in the middle',
+							'1st multi world description with empty lines
+
+
+some text in the middle',
 						),
 					),
 					new PhpDocTextNode(''),
@@ -2967,10 +2967,10 @@ public function provideMultiLinePhpDocData(): iterable
 							new IdentifierTypeNode('Bar'),
 							false,
 							'$bar',
-							'2nd multi world description with empty lines' . PHP_EOL .
-							PHP_EOL .
-							PHP_EOL .
-							'test',
+							'2nd multi world description with empty lines
+
+
+test',
 						),
 					),
 
@@ -4948,7 +4948,8 @@ public function provideRealWorldExampleData(): Iterator
 						new IdentifierTypeNode('\Drupal\Core\Field\FieldStorageDefinitionInterface'),
 						false,
 						'$field_definition',
-						PHP_EOL . '  The field definition.',
+						'
+  The field definition.',
 					),
 				),
 				new PhpDocTextNode(''),
@@ -4956,7 +4957,8 @@ public function provideRealWorldExampleData(): Iterator
 					'@return',
 					new ReturnTagValueNode(
 						new IdentifierTypeNode('array'),
-						PHP_EOL . "  An empty array if there is no schema, or an associative array with the
+						"
+  An empty array if there is no schema, or an associative array with the
   following key/value pairs:
   - columns: An array of Schema API column specifications, keyed by column
     name. The columns need to be a subset of the properties defined in

From 9d57f3db5bba9b0d8d11726389eb8af3126780b4 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 7 Sep 2024 23:12:57 +0200
Subject: [PATCH 12/45] Use one ParserConfig object across all parser-involved
 classes

---
 README.md                                     | 22 +++----
 src/Lexer/Lexer.php                           |  9 +++
 src/Parser/ConstExprParser.php                | 24 +++-----
 src/Parser/PhpDocParser.php                   | 21 +++----
 src/Parser/TypeParser.php                     | 21 +++----
 src/ParserConfig.php                          | 21 +++++++
 .../PHPStan/Ast/Attributes/AttributesTest.php |  9 ++-
 tests/PHPStan/Parser/ConstExprParserTest.php  |  9 ++-
 tests/PHPStan/Parser/FuzzyTest.php            |  8 ++-
 tests/PHPStan/Parser/PhpDocParserTest.php     | 58 ++++++++++---------
 tests/PHPStan/Parser/TokenIteratorTest.php    | 10 ++--
 tests/PHPStan/Parser/TypeParserTest.php       | 16 ++---
 .../IntegrationPrinterWithPhpParserTest.php   | 11 ++--
 tests/PHPStan/Printer/PrinterTest.php         | 18 +++---
 14 files changed, 146 insertions(+), 111 deletions(-)
 create mode 100644 src/ParserConfig.php

diff --git a/README.md b/README.md
index 99d5ba9f..415c1a5e 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
 use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPStan\PhpDocParser\Parser\ConstExprParser;
 use PHPStan\PhpDocParser\Parser\PhpDocParser;
 use PHPStan\PhpDocParser\Parser\TokenIterator;
@@ -41,10 +42,11 @@ use PHPStan\PhpDocParser\Parser\TypeParser;
 
 // basic setup
 
-$lexer = new Lexer();
-$constExprParser = new ConstExprParser();
-$typeParser = new TypeParser($constExprParser);
-$phpDocParser = new PhpDocParser($typeParser, $constExprParser);
+$config = new ParserConfig(usedAttributes: []);
+$lexer = new Lexer($config);
+$constExprParser = new ConstExprParser($config);
+$typeParser = new TypeParser($config, $constExprParser);
+$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 
 // parsing and reading a PHPDoc string
 
@@ -72,6 +74,7 @@ use PHPStan\PhpDocParser\Ast\NodeVisitor\CloningVisitor;
 use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
 use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPStan\PhpDocParser\Parser\ConstExprParser;
 use PHPStan\PhpDocParser\Parser\PhpDocParser;
 use PHPStan\PhpDocParser\Parser\TokenIterator;
@@ -80,12 +83,11 @@ use PHPStan\PhpDocParser\Printer\Printer;
 
 // basic setup with enabled required lexer attributes
 
-$usedAttributes = ['lines' => true, 'indexes' => true];
-
-$lexer = new Lexer();
-$constExprParser = new ConstExprParser(true, true, $usedAttributes);
-$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
-$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
+$config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true]);
+$lexer = new Lexer($config);
+$constExprParser = new ConstExprParser($config);
+$typeParser = new TypeParser($config, $constExprParser);
+$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 
 $tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
 $phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode
diff --git a/src/Lexer/Lexer.php b/src/Lexer/Lexer.php
index c090e62a..be85fa9f 100644
--- a/src/Lexer/Lexer.php
+++ b/src/Lexer/Lexer.php
@@ -2,6 +2,7 @@
 
 namespace PHPStan\PhpDocParser\Lexer;
 
+use PHPStan\PhpDocParser\ParserConfig;
 use function implode;
 use function preg_match_all;
 use const PREG_SET_ORDER;
@@ -94,8 +95,16 @@ class Lexer
 	public const TYPE_OFFSET = 1;
 	public const LINE_OFFSET = 2;
 
+	private ParserConfig $config; // @phpstan-ignore property.onlyWritten
+
 	private ?string $regexp = null;
 
+	public function __construct(ParserConfig $config)
+	{
+		$this->config = $config;
+	}
+
+
 	/**
 	 * @return list<array{string, int, int}>
 	 */
diff --git a/src/Parser/ConstExprParser.php b/src/Parser/ConstExprParser.php
index de476406..396b8d7c 100644
--- a/src/Parser/ConstExprParser.php
+++ b/src/Parser/ConstExprParser.php
@@ -4,27 +4,22 @@
 
 use PHPStan\PhpDocParser\Ast;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use function str_replace;
 use function strtolower;
 
 class ConstExprParser
 {
 
-	private bool $useLinesAttributes;
-
-	private bool $useIndexAttributes;
+	private ParserConfig $config;
 
 	private bool $parseDoctrineStrings;
 
-	/**
-	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
-	 */
 	public function __construct(
-		array $usedAttributes = []
+		ParserConfig $config
 	)
 	{
-		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
-		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
+		$this->config = $config;
 		$this->parseDoctrineStrings = false;
 	}
 
@@ -33,12 +28,7 @@ public function __construct(
 	 */
 	public function toDoctrine(): self
 	{
-		$self = new self(
-			[
-				'lines' => $this->useLinesAttributes,
-				'indexes' => $this->useIndexAttributes,
-			],
-		);
+		$self = new self($this->config);
 		$self->parseDoctrineStrings = true;
 		return $self;
 	}
@@ -286,12 +276,12 @@ private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprA
 	 */
 	private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex): Ast\ConstExpr\ConstExprNode
 	{
-		if ($this->useLinesAttributes) {
+		if ($this->config->useLinesAttributes) {
 			$node->setAttribute(Ast\Attribute::START_LINE, $startLine);
 			$node->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
 		}
 
-		if ($this->useIndexAttributes) {
+		if ($this->config->useIndexAttributes) {
 			$node->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
 			$node->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
 		}
diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index 3dd15264..e4aa77b3 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -10,6 +10,7 @@
 use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
 use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPStan\ShouldNotHappenException;
 use function array_key_exists;
 use function array_values;
@@ -29,30 +30,24 @@ class PhpDocParser
 		Lexer::TOKEN_INTERSECTION,
 	];
 
+	private ParserConfig $config;
+
 	private TypeParser $typeParser;
 
 	private ConstExprParser $constantExprParser;
 
 	private ConstExprParser $doctrineConstantExprParser;
 
-	private bool $useLinesAttributes;
-
-	private bool $useIndexAttributes;
-
-	/**
-	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
-	 */
 	public function __construct(
+		ParserConfig $config,
 		TypeParser $typeParser,
-		ConstExprParser $constantExprParser,
-		array $usedAttributes = []
+		ConstExprParser $constantExprParser
 	)
 	{
+		$this->config = $config;
 		$this->typeParser = $typeParser;
 		$this->constantExprParser = $constantExprParser;
 		$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
-		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
-		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
 	}
 
 
@@ -172,12 +167,12 @@ private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
 	 */
 	private function enrichWithAttributes(TokenIterator $tokens, Ast\Node $tag, int $startLine, int $startIndex): Ast\Node
 	{
-		if ($this->useLinesAttributes) {
+		if ($this->config->useLinesAttributes) {
 			$tag->setAttribute(Ast\Attribute::START_LINE, $startLine);
 			$tag->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
 		}
 
-		if ($this->useIndexAttributes) {
+		if ($this->config->useIndexAttributes) {
 			$tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
 			$tag->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
 		}
diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index a9e58833..a6256473 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -6,6 +6,7 @@
 use PHPStan\PhpDocParser\Ast;
 use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use function in_array;
 use function str_replace;
 use function strlen;
@@ -15,23 +16,17 @@
 class TypeParser
 {
 
-	private ConstExprParser $constExprParser;
-
-	private bool $useLinesAttributes;
+	private ParserConfig $config;
 
-	private bool $useIndexAttributes;
+	private ConstExprParser $constExprParser;
 
-	/**
-	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
-	 */
 	public function __construct(
-		ConstExprParser $constExprParser,
-		array $usedAttributes = []
+		ParserConfig $config,
+		ConstExprParser $constExprParser
 	)
 	{
+		$this->config = $config;
 		$this->constExprParser = $constExprParser;
-		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
-		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
 	}
 
 	/** @phpstan-impure */
@@ -64,12 +59,12 @@ public function parse(TokenIterator $tokens): Ast\Type\TypeNode
 	 */
 	public function enrichWithAttributes(TokenIterator $tokens, Ast\Node $type, int $startLine, int $startIndex): Ast\Node
 	{
-		if ($this->useLinesAttributes) {
+		if ($this->config->useLinesAttributes) {
 			$type->setAttribute(Ast\Attribute::START_LINE, $startLine);
 			$type->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
 		}
 
-		if ($this->useIndexAttributes) {
+		if ($this->config->useIndexAttributes) {
 			$type->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
 			$type->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
 		}
diff --git a/src/ParserConfig.php b/src/ParserConfig.php
new file mode 100644
index 00000000..b3a8889a
--- /dev/null
+++ b/src/ParserConfig.php
@@ -0,0 +1,21 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser;
+
+class ParserConfig
+{
+
+	public bool $useLinesAttributes;
+
+	public bool $useIndexAttributes;
+
+	/**
+	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
+	 */
+	public function __construct(array $usedAttributes)
+	{
+		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
+		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
+	}
+
+}
diff --git a/tests/PHPStan/Ast/Attributes/AttributesTest.php b/tests/PHPStan/Ast/Attributes/AttributesTest.php
index 8be0d2f2..f513b1fa 100644
--- a/tests/PHPStan/Ast/Attributes/AttributesTest.php
+++ b/tests/PHPStan/Ast/Attributes/AttributesTest.php
@@ -8,6 +8,7 @@
 use PHPStan\PhpDocParser\Parser\PhpDocParser;
 use PHPStan\PhpDocParser\Parser\TokenIterator;
 use PHPStan\PhpDocParser\Parser\TypeParser;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 
 final class AttributesTest extends TestCase
@@ -18,9 +19,11 @@ final class AttributesTest extends TestCase
 	protected function setUp(): void
 	{
 		parent::setUp();
-		$lexer = new Lexer();
-		$constExprParser = new ConstExprParser();
-		$phpDocParser = new PhpDocParser(new TypeParser($constExprParser), $constExprParser);
+
+		$config = new ParserConfig([]);
+		$lexer = new Lexer($config);
+		$constExprParser = new ConstExprParser($config);
+		$phpDocParser = new PhpDocParser($config, new TypeParser($config, $constExprParser), $constExprParser);
 
 		$input = '/** @var string */';
 		$tokens = new TokenIterator($lexer->tokenize($input));
diff --git a/tests/PHPStan/Parser/ConstExprParserTest.php b/tests/PHPStan/Parser/ConstExprParserTest.php
index 5f7fc43f..18922e86 100644
--- a/tests/PHPStan/Parser/ConstExprParserTest.php
+++ b/tests/PHPStan/Parser/ConstExprParserTest.php
@@ -16,6 +16,7 @@
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
 use PHPStan\PhpDocParser\Ast\NodeTraverser;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 
 class ConstExprParserTest extends TestCase
@@ -28,8 +29,9 @@ class ConstExprParserTest extends TestCase
 	protected function setUp(): void
 	{
 		parent::setUp();
-		$this->lexer = new Lexer();
-		$this->constExprParser = new ConstExprParser();
+		$config = new ParserConfig([]);
+		$this->lexer = new Lexer($config);
+		$this->constExprParser = new ConstExprParser($config);
 	}
 
 
@@ -67,10 +69,11 @@ public function testParse(string $input, ConstExprNode $expectedExpr, int $nextT
 	public function testVerifyAttributes(string $input): void
 	{
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
-		$constExprParser = new ConstExprParser([
+		$config = new ParserConfig([
 			'lines' => true,
 			'indexes' => true,
 		]);
+		$constExprParser = new ConstExprParser($config);
 		$visitor = new NodeCollectingVisitor();
 		$traverser = new NodeTraverser([$visitor]);
 		$traverser->traverse([$constExprParser->parse($tokens)]);
diff --git a/tests/PHPStan/Parser/FuzzyTest.php b/tests/PHPStan/Parser/FuzzyTest.php
index d3c3ed7e..9491bf69 100644
--- a/tests/PHPStan/Parser/FuzzyTest.php
+++ b/tests/PHPStan/Parser/FuzzyTest.php
@@ -4,6 +4,7 @@
 
 use Iterator;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Process\Process;
 use function file_get_contents;
@@ -28,9 +29,10 @@ class FuzzyTest extends TestCase
 	protected function setUp(): void
 	{
 		parent::setUp();
-		$this->lexer = new Lexer();
-		$this->typeParser = new TypeParser(new ConstExprParser());
-		$this->constExprParser = new ConstExprParser();
+		$config = new ParserConfig([]);
+		$this->lexer = new Lexer($config);
+		$this->typeParser = new TypeParser($config, new ConstExprParser($config));
+		$this->constExprParser = new ConstExprParser($config);
 	}
 
 	/**
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index aaf3d2b6..3bd2c546 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -64,6 +64,7 @@
 use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 use function count;
 use function sprintf;
@@ -79,10 +80,11 @@ class PhpDocParserTest extends TestCase
 	protected function setUp(): void
 	{
 		parent::setUp();
-		$this->lexer = new Lexer();
-		$constExprParser = new ConstExprParser();
-		$typeParser = new TypeParser($constExprParser);
-		$this->phpDocParser = new PhpDocParser($typeParser, $constExprParser, []);
+		$config = new ParserConfig([]);
+		$this->lexer = new Lexer($config);
+		$constExprParser = new ConstExprParser($config);
+		$typeParser = new TypeParser($config, $constExprParser);
+		$this->phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 	}
 
 
@@ -6807,13 +6809,13 @@ public function dataLinesAndIndexes(): iterable
 	public function testLinesAndIndexes(string $phpDoc, array $childrenLines): void
 	{
 		$tokens = new TokenIterator($this->lexer->tokenize($phpDoc));
-		$usedAttributes = [
+		$config = new ParserConfig([
 			'lines' => true,
 			'indexes' => true,
-		];
-		$constExprParser = new ConstExprParser($usedAttributes);
-		$typeParser = new TypeParser($constExprParser, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
+		]);
+		$constExprParser = new ConstExprParser($config);
+		$typeParser = new TypeParser($config, $constExprParser);
+		$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$children = $phpDocNode->children;
 		$this->assertCount(count($childrenLines), $children);
@@ -6891,13 +6893,13 @@ public function dataDeepNodesLinesAndIndexes(): iterable
 	public function testDeepNodesLinesAndIndexes(string $phpDoc, array $nodeAttributes): void
 	{
 		$tokens = new TokenIterator($this->lexer->tokenize($phpDoc));
-		$usedAttributes = [
+		$config = new ParserConfig([
 			'lines' => true,
 			'indexes' => true,
-		];
-		$constExprParser = new ConstExprParser($usedAttributes);
-		$typeParser = new TypeParser($constExprParser, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
+		]);
+		$constExprParser = new ConstExprParser($config);
+		$typeParser = new TypeParser($config, $constExprParser);
+		$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 		$visitor = new NodeCollectingVisitor();
 		$traverser = new NodeTraverser([$visitor]);
 		$traverser->traverse([$phpDocParser->parse($tokens)]);
@@ -6964,13 +6966,13 @@ public function dataReturnTypeLinesAndIndexes(): iterable
 	public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): void
 	{
 		$tokens = new TokenIterator($this->lexer->tokenize($phpDoc));
-		$usedAttributes = [
+		$config = new ParserConfig([
 			'lines' => true,
 			'indexes' => true,
-		];
-		$constExprParser = new ConstExprParser($usedAttributes);
-		$typeParser = new TypeParser($constExprParser, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
+		]);
+		$constExprParser = new ConstExprParser($config);
+		$typeParser = new TypeParser($config, $constExprParser);
+		$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 		$phpDocNode = $phpDocParser->parse($tokens);
 		$returnTag = $phpDocNode->getReturnTagValues()[0];
 		$type = $returnTag->type;
@@ -7014,10 +7016,13 @@ public function testReturnTypeLinesAndIndexes(string $phpDoc, array $lines): voi
 	 */
 	public function testVerifyAttributes(string $label, string $input): void
 	{
-		$usedAttributes = ['lines' => true, 'indexes' => true];
-		$constExprParser = new ConstExprParser($usedAttributes);
-		$typeParser = new TypeParser($constExprParser, $usedAttributes);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, $usedAttributes);
+		$config = new ParserConfig([
+			'lines' => true,
+			'indexes' => true,
+		]);
+		$constExprParser = new ConstExprParser($config);
+		$typeParser = new TypeParser($config, $constExprParser);
+		$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 
 		$visitor = new NodeCollectingVisitor();
@@ -7344,9 +7349,10 @@ public function testTextBetweenTagsBelongsToDescription(
 		PhpDocNode $expectedPhpDocNode
 	): void
 	{
-		$constExprParser = new ConstExprParser();
-		$typeParser = new TypeParser($constExprParser);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser, []);
+		$config = new ParserConfig([]);
+		$constExprParser = new ConstExprParser($config);
+		$typeParser = new TypeParser($config, $constExprParser);
+		$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 		$actualPhpDocNode = $phpDocParser->parse($tokens);
diff --git a/tests/PHPStan/Parser/TokenIteratorTest.php b/tests/PHPStan/Parser/TokenIteratorTest.php
index 8f2b4c9d..3cc51fc1 100644
--- a/tests/PHPStan/Parser/TokenIteratorTest.php
+++ b/tests/PHPStan/Parser/TokenIteratorTest.php
@@ -3,6 +3,7 @@
 namespace PHPStan\PhpDocParser\Parser;
 
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 use const PHP_EOL;
 
@@ -46,11 +47,12 @@ public function dataGetDetectedNewline(): iterable
 	 */
 	public function testGetDetectedNewline(string $phpDoc, ?string $expectedNewline): void
 	{
-		$lexer = new Lexer();
+		$config = new ParserConfig([]);
+		$lexer = new Lexer($config);
 		$tokens = new TokenIterator($lexer->tokenize($phpDoc));
-		$constExprParser = new ConstExprParser();
-		$typeParser = new TypeParser($constExprParser);
-		$phpDocParser = new PhpDocParser($typeParser, $constExprParser);
+		$constExprParser = new ConstExprParser($config);
+		$typeParser = new TypeParser($config, $constExprParser);
+		$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
 		$phpDocParser->parse($tokens);
 		$this->assertSame($expectedNewline, $tokens->getDetectedNewline());
 	}
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index cd50db40..de9d7230 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -31,6 +31,7 @@
 use PHPStan\PhpDocParser\Ast\Type\TypeNode;
 use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
 use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPStan\PhpDocParser\Printer\Printer;
 use PHPUnit\Framework\TestCase;
 use function get_class;
@@ -47,8 +48,9 @@ class TypeParserTest extends TestCase
 	protected function setUp(): void
 	{
 		parent::setUp();
-		$this->lexer = new Lexer();
-		$this->typeParser = new TypeParser(new ConstExprParser());
+		$config = new ParserConfig([]);
+		$this->lexer = new Lexer($config);
+		$this->typeParser = new TypeParser($config, new ConstExprParser($config));
 	}
 
 
@@ -115,8 +117,8 @@ public function testVerifyAttributes(string $input, $expectedResult): void
 			$this->expectExceptionMessage($expectedResult->getMessage());
 		}
 
-		$usedAttributes = ['lines' => true, 'indexes' => true];
-		$typeParser = new TypeParser(new ConstExprParser($usedAttributes), $usedAttributes);
+		$config = new ParserConfig(['lines' => true, 'indexes' => true]);
+		$typeParser = new TypeParser($config, new ConstExprParser($config));
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 
 		$visitor = new NodeCollectingVisitor();
@@ -3069,11 +3071,11 @@ public function testLinesAndIndexes(string $input, array $assertions): void
 	{
 		$tokensArray = $this->lexer->tokenize($input);
 		$tokens = new TokenIterator($tokensArray);
-		$usedAttributes = [
+		$config = new ParserConfig([
 			'lines' => true,
 			'indexes' => true,
-		];
-		$typeParser = new TypeParser(new ConstExprParser(), $usedAttributes);
+		]);
+		$typeParser = new TypeParser($config, new ConstExprParser($config));
 		$typeNode = $typeParser->parse($tokens);
 
 		foreach ($assertions as [$callable, $expectedContent, $startLine, $endLine, $startIndex, $endIndex]) {
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index 36ad360b..d2f2c361 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -21,6 +21,7 @@
 use PHPStan\PhpDocParser\Parser\PhpDocParser;
 use PHPStan\PhpDocParser\Parser\TokenIterator;
 use PHPStan\PhpDocParser\Parser\TypeParser;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 use function file_get_contents;
 
@@ -98,14 +99,14 @@ public function enterNode(PhpNode $phpNode)
 
 				$phpDoc = $phpNode->getDocComment()->getText();
 
-				$usedAttributes = ['lines' => true, 'indexes' => true];
-				$constExprParser = new ConstExprParser($usedAttributes);
+				$config = new ParserConfig(['lines' => true, 'indexes' => true]);
+				$constExprParser = new ConstExprParser($config);
 				$phpDocParser = new PhpDocParser(
-					new TypeParser($constExprParser, $usedAttributes),
+					$config,
+					new TypeParser($config, $constExprParser),
 					$constExprParser,
-					$usedAttributes,
 				);
-				$lexer = new Lexer();
+				$lexer = new Lexer($config);
 				$tokens = new TokenIterator($lexer->tokenize($phpDoc));
 				$phpDocNode = $phpDocParser->parse($tokens);
 				$cloningTraverser = new NodeTraverser([new NodeVisitor\CloningVisitor()]);
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 7ed808c7..b4ca6244 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -47,6 +47,7 @@
 use PHPStan\PhpDocParser\Parser\PhpDocParser;
 use PHPStan\PhpDocParser\Parser\TokenIterator;
 use PHPStan\PhpDocParser\Parser\TypeParser;
+use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 use function array_pop;
 use function array_splice;
@@ -65,13 +66,13 @@ class PrinterTest extends TestCase
 
 	protected function setUp(): void
 	{
-		$usedAttributes = ['lines' => true, 'indexes' => true];
-		$constExprParser = new ConstExprParser($usedAttributes);
-		$this->typeParser = new TypeParser($constExprParser, $usedAttributes);
+		$config = new ParserConfig(['lines' => true, 'indexes' => true]);
+		$constExprParser = new ConstExprParser($config);
+		$this->typeParser = new TypeParser($config, $constExprParser);
 		$this->phpDocParser = new PhpDocParser(
+			$config,
 			$this->typeParser,
 			$constExprParser,
-			$usedAttributes,
 		);
 	}
 
@@ -1857,7 +1858,8 @@ public function enterNode(Node $node)
 	 */
 	public function testPrintFormatPreserving(string $phpDoc, string $expectedResult, NodeVisitor $visitor): void
 	{
-		$lexer = new Lexer();
+		$config = new ParserConfig([]);
+		$lexer = new Lexer($config);
 		$tokens = new TokenIterator($lexer->tokenize($phpDoc));
 		$phpDocNode = $this->phpDocParser->parse($tokens);
 		$cloningTraverser = new NodeTraverser([new NodeVisitor\CloningVisitor()]);
@@ -2007,7 +2009,8 @@ public function testPrintType(TypeNode $node, string $expectedResult): void
 		$phpDoc = $printer->print($node);
 		$this->assertSame($expectedResult, $phpDoc);
 
-		$lexer = new Lexer();
+		$config = new ParserConfig([]);
+		$lexer = new Lexer($config);
 		$this->assertEquals(
 			$this->unsetAttributes($node),
 			$this->unsetAttributes($this->typeParser->parse(new TokenIterator($lexer->tokenize($phpDoc)))),
@@ -2043,7 +2046,8 @@ public function testPrintPhpDocNode(PhpDocNode $node, string $expectedResult): v
 		$phpDoc = $printer->print($node);
 		$this->assertSame($expectedResult, $phpDoc);
 
-		$lexer = new Lexer();
+		$config = new ParserConfig([]);
+		$lexer = new Lexer($config);
 		$this->assertEquals(
 			$this->unsetAttributes($node),
 			$this->unsetAttributes($this->phpDocParser->parse(new TokenIterator($lexer->tokenize($phpDoc)))),

From 0ed667be75329ee8448d41553c048269291217ff Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 8 Sep 2024 13:23:37 +0200
Subject: [PATCH 13/45] Fix

---
 tests/PHPStan/Printer/PrinterTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 01844226..e7d0be67 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -2006,7 +2006,7 @@ public function dataPrintType(): iterable
 					new IdentifierTypeNode('string'),
 				),
 				new ArrayShapeItemNode(
-					new QuoteAwareConstExprStringNode('Full Name', QuoteAwareConstExprStringNode::SINGLE_QUOTED),
+					new ConstExprStringNode('Full Name', ConstExprStringNode::SINGLE_QUOTED),
 					false,
 					new IdentifierTypeNode('string'),
 				),
@@ -2021,7 +2021,7 @@ public function dataPrintType(): iterable
 					new IdentifierTypeNode('string'),
 				),
 				new ObjectShapeItemNode(
-					new QuoteAwareConstExprStringNode('Full Name', QuoteAwareConstExprStringNode::SINGLE_QUOTED),
+					new ConstExprStringNode('Full Name', ConstExprStringNode::SINGLE_QUOTED),
 					false,
 					new IdentifierTypeNode('string'),
 				),

From 4c9c7abdf629e6afaab9e7117fa428d2eee678b4 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 9 Sep 2024 11:20:15 +0200
Subject: [PATCH 14/45] Deploy all branches of API Reference at once

---
 .github/workflows/apiref.yml | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/apiref.yml b/.github/workflows/apiref.yml
index f9384b3f..530d3fb4 100644
--- a/.github/workflows/apiref.yml
+++ b/.github/workflows/apiref.yml
@@ -16,9 +16,17 @@ jobs:
     name: "Build API References Pages"
     runs-on: "ubuntu-latest"
 
+    strategy:
+      matrix:
+        branch:
+          - "1.23.x"
+          - "2.0.x"
+
     steps:
       - name: "Checkout"
         uses: actions/checkout@v4
+        with:
+          ref: ${{ matrix.branch }}
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
@@ -36,18 +44,36 @@ jobs:
         run: "composer install --no-interaction --no-progress"
 
       - name: "Run ApiGen"
-        run: "apigen/vendor/bin/apigen -c apigen/apigen.neon --output docs/${{ github.ref_name }} -- src"
+        run: "apigen/vendor/bin/apigen -c apigen/apigen.neon --output docs/${{ matrix.branch }} -- src"
 
       - name: "Copy favicon"
         run: "cp apigen/favicon.png docs/favicon.png"
 
+      - uses: actions/upload-artifact@v4
+        with:
+          name: docs-${{ matrix.branch }}
+          path: docs/*
+
+  merge:
+    name: "Merge docs"
+    needs: build
+
+    runs-on: "ubuntu-latest"
+
+    steps:
+      - uses: actions/download-artifact@v4
+        with:
+          pattern: docs-*
+          path: docs
+          merge-multiple: true
+
       - name: Upload artifact
         uses: actions/upload-pages-artifact@v3
         with:
           path: 'docs'
 
   deploy:
-    needs: build
+    needs: merge
 
     # from https://github.com/actions/deploy-pages
 

From c3761a0111a40bedc81e2ddd73ff2ddc57ea4d11 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 9 Sep 2024 13:28:39 +0200
Subject: [PATCH 15/45] CallableTypeNode tests

---
 tests/PHPStan/Parser/PhpDocParserTest.php | 63 +++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 3bd2c546..f17e0fc8 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -1773,6 +1773,69 @@ public function provideReturnTagsData(): Iterator
 				),
 			]),
 		];
+
+		yield [
+			'valid CallableTypeNode without space after "callable"',
+			'/** @return callable(int, string): void */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@return',
+					new ReturnTagValueNode(
+						new CallableTypeNode(new IdentifierTypeNode('callable'), [
+							new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '', false),
+							new CallableTypeParameterNode(new IdentifierTypeNode('string'), false, false, '', false),
+						], new IdentifierTypeNode('void'), []),
+						'',
+					),
+				),
+			]),
+		];
+
+		yield [
+			'valid CallableTypeNode with space after "callable"',
+			'/** @return callable (int, string): void */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@return',
+					new ReturnTagValueNode(
+						new CallableTypeNode(new IdentifierTypeNode('callable'), [
+							new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '', false),
+							new CallableTypeParameterNode(new IdentifierTypeNode('string'), false, false, '', false),
+						], new IdentifierTypeNode('void'), []),
+						'',
+					),
+				),
+			]),
+		];
+
+		yield [
+			'valid IdentifierTypeNode with space after "callable" turns the rest to description',
+			'/** @return callable (int, string) */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@return',
+					new ReturnTagValueNode(new IdentifierTypeNode('callable'), '(int, string)'),
+				),
+			]),
+		];
+
+		yield [
+			'invalid CallableTypeNode without space after "callable"',
+			'/** @return callable(int, string) */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@return',
+					new InvalidTagValueNode('callable(int, string)', new ParserException(
+						'(',
+						4,
+						20,
+						27,
+						null,
+						1,
+					)),
+				),
+			]),
+		];
 	}
 
 

From f9635550d59587171fc60dc0840ee015e8a76453 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 9 Sep 2024 15:53:00 +0200
Subject: [PATCH 16/45] UPGRADING.md

---
 README.md    |   2 +-
 UPGRADING.md | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 116 insertions(+), 1 deletion(-)
 create mode 100644 UPGRADING.md

diff --git a/README.md b/README.md
index 415c1a5e..ff6e808e 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ For the complete list of supported PHPDoc features check out PHPStan documentati
 * [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) (list of PHPDoc types)
 * [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/2.0.x/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc.
 
-This parser also supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/2.0.x/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html). The support needs to be turned on by setting `bool $parseDoctrineAnnotations` to `true` in `Lexer` and `PhpDocParser` class constructors.
+This parser also supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/2.0.x/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html).
 
 ## Installation
 
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 00000000..4e47c3d8
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,115 @@
+Upgrading from phpstan/phpdoc-parser 1.x to 2.0
+=================================
+
+### PHP version requirements
+
+phpstan/phpdoc-parser now requires PHP 7.4 or newer to run.
+
+### Changed constructors of parser classes
+
+Instead of different arrays and boolean values passed into class constructors during setup, parser classes now share a common ParserConfig object.
+
+Before:
+
+```php
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\Parser\ConstExprParser;
+use PHPStan\PhpDocParser\Parser\TypeParser;
+use PHPStan\PhpDocParser\Parser\PhpDocParser;
+
+$usedAttributes = ['lines' => true, 'indexes' => true];
+
+$lexer = new Lexer();
+$constExprParser = new ConstExprParser(true, true, $usedAttributes);
+$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
+$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
+```
+
+After:
+
+```php
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\ParserConfig;
+use PHPStan\PhpDocParser\Parser\ConstExprParser;
+use PHPStan\PhpDocParser\Parser\TypeParser;
+use PHPStan\PhpDocParser\Parser\PhpDocParser;
+
+$config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true]);
+$lexer = new Lexer($config);
+$constExprParser = new ConstExprParser($config);
+$typeParser = new TypeParser($config, $constExprParser);
+$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
+```
+
+The point of ParserConfig is that over the course of phpstan/phpdoc-parser 2.x development series it's most likely going to gain new optional parameters akin to PHPStan's [bleeding edge](https://phpstan.org/blog/what-is-bleeding-edge). These parameters will allow opting in to new behaviour which will become the default in 3.0.
+
+With ParserConfig object, it's now going to be impossible to configure parser classes inconsistently. Which [happened to users](https://github.com/phpstan/phpdoc-parser/issues/251#issuecomment-2333927959) when they were separate boolean values.
+
+### Support for parsing Doctrine annotations
+
+This parser now supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes representing Doctrine Annotations live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/2.0.x/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html).
+
+### Whitespace before description is required
+
+phpdoc-parser 1.x sometimes silently consumed invalid part of a PHPDoc type as description:
+
+```php
+/** @return \Closure(...int, string): string */
+```
+
+This became `IdentifierTypeNode` of `\Closure` and with `(...int, string): string` as description. (Valid callable syntax is: `\Closure(int ...$u, string): string`.)
+
+Another example:
+
+```php
+/** @return array{foo: int}} */
+```
+
+The extra `}` also became description.
+
+Both of these examples are now InvalidTagValueNode.
+
+If these parts are supposed to be PHPDoc descriptions, you need to put whitespace between the type and the description text:
+
+```php
+/** @return \Closure (...int, string): string */
+/** @return array{foo: int} } */
+```
+
+## Type aliases with invalid types are preserved
+
+In phpdoc-parser 1.x, invalid type alias syntax was represented as [`InvalidTagValueNode`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.PhpDoc.InvalidTagValueNode.html), losing information about a type alias being present.
+
+```php
+/**
+ * @phpstan-type TypeAlias
+ */
+```
+
+This `@phpstan-type` is missing the actual type to alias. In phpdoc-parser 2.0 this is now represented as [`TypeAliasTagValueNode`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.PhpDoc.TypeAliasTagValueNode.html) (instead of `InvalidTagValueNode`) with [`InvalidTypeNode`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.Type.InvalidTypeNode.html) in place of the type.
+
+## Removal of QuoteAwareConstExprStringNode
+
+The class [QuoteAwareConstExprStringNode](https://phpstan.github.io/phpdoc-parser/1.23.x/PHPStan.PhpDocParser.Ast.ConstExpr.QuoteAwareConstExprStringNode.html) has been removed.
+
+Instead, [ConstExprStringNode](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.ConstExpr.ConstExprStringNode.html) gained information about the kind of quotes being used.
+
+## Removed 2nd parameter of `ConstExprParser::parse()` (`$trimStrings`)
+
+`ConstExprStringNode::$value` now contains unescaped values without surrounding `''` or `""` quotes.
+
+Use `ConstExprStringNode::__toString()` or [`Printer`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Printer.Printer.html) to get the escaped value along with surrounding quotes.
+
+## Text between tags always belongs to description
+
+Multi-line descriptions between tags were previously represented as separate [PhpDocTextNode](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.PhpDoc.PhpDocTextNode.html):
+
+```php
+/**
+ * @param Foo $foo 1st multi world description
+ * some text in the middle
+ * @param Bar $bar 2nd multi world description
+ */
+```
+
+The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the description of the first `@param` tag.

From 7cfc5304ab1953156ace46fa525ca55b99a17510 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 13:33:54 +0200
Subject: [PATCH 17/45] [BCB] Constructor parameter `$isEquality` in
 `AssertTag*ValueNode` made required

---
 UPGRADING.md                                  | 12 ++++++++----
 src/Ast/PhpDoc/AssertTagMethodValueNode.php   |  2 +-
 src/Ast/PhpDoc/AssertTagPropertyValueNode.php |  2 +-
 src/Ast/PhpDoc/AssertTagValueNode.php         |  2 +-
 tests/PHPStan/Parser/PhpDocParserTest.php     | 14 ++++++++++++++
 5 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index 4e47c3d8..2c8a7a84 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -76,7 +76,7 @@ If these parts are supposed to be PHPDoc descriptions, you need to put whitespac
 /** @return array{foo: int} } */
 ```
 
-## Type aliases with invalid types are preserved
+### Type aliases with invalid types are preserved
 
 In phpdoc-parser 1.x, invalid type alias syntax was represented as [`InvalidTagValueNode`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.PhpDoc.InvalidTagValueNode.html), losing information about a type alias being present.
 
@@ -88,19 +88,19 @@ In phpdoc-parser 1.x, invalid type alias syntax was represented as [`InvalidTagV
 
 This `@phpstan-type` is missing the actual type to alias. In phpdoc-parser 2.0 this is now represented as [`TypeAliasTagValueNode`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.PhpDoc.TypeAliasTagValueNode.html) (instead of `InvalidTagValueNode`) with [`InvalidTypeNode`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.Type.InvalidTypeNode.html) in place of the type.
 
-## Removal of QuoteAwareConstExprStringNode
+### Removal of QuoteAwareConstExprStringNode
 
 The class [QuoteAwareConstExprStringNode](https://phpstan.github.io/phpdoc-parser/1.23.x/PHPStan.PhpDocParser.Ast.ConstExpr.QuoteAwareConstExprStringNode.html) has been removed.
 
 Instead, [ConstExprStringNode](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.ConstExpr.ConstExprStringNode.html) gained information about the kind of quotes being used.
 
-## Removed 2nd parameter of `ConstExprParser::parse()` (`$trimStrings`)
+### Removed 2nd parameter of `ConstExprParser::parse()` (`$trimStrings`)
 
 `ConstExprStringNode::$value` now contains unescaped values without surrounding `''` or `""` quotes.
 
 Use `ConstExprStringNode::__toString()` or [`Printer`](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Printer.Printer.html) to get the escaped value along with surrounding quotes.
 
-## Text between tags always belongs to description
+### Text between tags always belongs to description
 
 Multi-line descriptions between tags were previously represented as separate [PhpDocTextNode](https://phpstan.github.io/phpdoc-parser/2.0.x/PHPStan.PhpDocParser.Ast.PhpDoc.PhpDocTextNode.html):
 
@@ -113,3 +113,7 @@ Multi-line descriptions between tags were previously represented as separate [Ph
 ```
 
 The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the description of the first `@param` tag.
+
+### Minor BC breaks
+
+* Constructor parameter `$isEquality` in `AssertTag*ValueNode` made required
diff --git a/src/Ast/PhpDoc/AssertTagMethodValueNode.php b/src/Ast/PhpDoc/AssertTagMethodValueNode.php
index 2bd64631..0dfee314 100644
--- a/src/Ast/PhpDoc/AssertTagMethodValueNode.php
+++ b/src/Ast/PhpDoc/AssertTagMethodValueNode.php
@@ -24,7 +24,7 @@ class AssertTagMethodValueNode implements PhpDocTagValueNode
 	/** @var string (may be empty) */
 	public string $description;
 
-	public function __construct(TypeNode $type, string $parameter, string $method, bool $isNegated, string $description, bool $isEquality = false)
+	public function __construct(TypeNode $type, string $parameter, string $method, bool $isNegated, string $description, bool $isEquality)
 	{
 		$this->type = $type;
 		$this->parameter = $parameter;
diff --git a/src/Ast/PhpDoc/AssertTagPropertyValueNode.php b/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
index 44e7c244..8bfd1d0e 100644
--- a/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
+++ b/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
@@ -24,7 +24,7 @@ class AssertTagPropertyValueNode implements PhpDocTagValueNode
 	/** @var string (may be empty) */
 	public string $description;
 
-	public function __construct(TypeNode $type, string $parameter, string $property, bool $isNegated, string $description, bool $isEquality = false)
+	public function __construct(TypeNode $type, string $parameter, string $property, bool $isNegated, string $description, bool $isEquality)
 	{
 		$this->type = $type;
 		$this->parameter = $parameter;
diff --git a/src/Ast/PhpDoc/AssertTagValueNode.php b/src/Ast/PhpDoc/AssertTagValueNode.php
index 9f2be9f0..5dc9e8c3 100644
--- a/src/Ast/PhpDoc/AssertTagValueNode.php
+++ b/src/Ast/PhpDoc/AssertTagValueNode.php
@@ -22,7 +22,7 @@ class AssertTagValueNode implements PhpDocTagValueNode
 	/** @var string (may be empty) */
 	public string $description;
 
-	public function __construct(TypeNode $type, string $parameter, bool $isNegated, string $description, bool $isEquality = false)
+	public function __construct(TypeNode $type, string $parameter, bool $isNegated, string $description, bool $isEquality)
 	{
 		$this->type = $type;
 		$this->parameter = $parameter;
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index f17e0fc8..2ac880f8 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -4667,6 +4667,7 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4683,6 +4684,7 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4699,6 +4701,7 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						false,
 						'assert Type to $var',
+						false,
 					),
 				),
 			]),
@@ -4718,6 +4721,7 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4735,6 +4739,7 @@ public function provideAssertTagsData(): Iterator
 						'method',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4752,6 +4757,7 @@ public function provideAssertTagsData(): Iterator
 						'property',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4768,6 +4774,7 @@ public function provideAssertTagsData(): Iterator
 						'$this',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4784,6 +4791,7 @@ public function provideAssertTagsData(): Iterator
 						'$this',
 						false,
 						'assert Type to $this',
+						false,
 					),
 				),
 			]),
@@ -4808,6 +4816,7 @@ public function provideAssertTagsData(): Iterator
 						'$this',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4825,6 +4834,7 @@ public function provideAssertTagsData(): Iterator
 						'method',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4842,6 +4852,7 @@ public function provideAssertTagsData(): Iterator
 						'property',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4858,6 +4869,7 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4874,6 +4886,7 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						false,
 						'',
+						false,
 					),
 				),
 			]),
@@ -4890,6 +4903,7 @@ public function provideAssertTagsData(): Iterator
 						'$var',
 						true,
 						'',
+						false,
 					),
 				),
 			]),

From 6e68248a0eb61161e8a0add8b690825e55d6db7f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 13:35:30 +0200
Subject: [PATCH 18/45] [BCB] Constructor parameter `$templateTypes` in
 `MethodTagValueNode` made required

---
 UPGRADING.md                              |  1 +
 src/Ast/PhpDoc/MethodTagValueNode.php     |  2 +-
 tests/PHPStan/Parser/PhpDocParserTest.php | 53 +++++++++++++++++++++++
 3 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index 2c8a7a84..d7eaea2e 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -117,3 +117,4 @@ The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the
 ### Minor BC breaks
 
 * Constructor parameter `$isEquality` in `AssertTag*ValueNode` made required
+* Constructor parameter `$templateTypes` in `MethodTagValueNode` made required
diff --git a/src/Ast/PhpDoc/MethodTagValueNode.php b/src/Ast/PhpDoc/MethodTagValueNode.php
index 467f19bb..223d6e47 100644
--- a/src/Ast/PhpDoc/MethodTagValueNode.php
+++ b/src/Ast/PhpDoc/MethodTagValueNode.php
@@ -31,7 +31,7 @@ class MethodTagValueNode implements PhpDocTagValueNode
 	 * @param MethodTagValueParameterNode[] $parameters
 	 * @param TemplateTagValueNode[] $templateTypes
 	 */
-	public function __construct(bool $isStatic, ?TypeNode $returnType, string $methodName, array $parameters, string $description, array $templateTypes = [])
+	public function __construct(bool $isStatic, ?TypeNode $returnType, string $methodName, array $parameters, string $description, array $templateTypes)
 	{
 		$this->isStatic = $isStatic;
 		$this->returnType = $returnType;
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 2ac880f8..9991ac90 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -2283,6 +2283,7 @@ public function provideMethodTagsData(): Iterator
 						'foo',
 						[],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2300,6 +2301,7 @@ public function provideMethodTagsData(): Iterator
 						'foo',
 						[],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2317,6 +2319,7 @@ public function provideMethodTagsData(): Iterator
 						'foo',
 						[],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2334,6 +2337,7 @@ public function provideMethodTagsData(): Iterator
 						'foo',
 						[],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2351,6 +2355,7 @@ public function provideMethodTagsData(): Iterator
 						'foo',
 						[],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2368,6 +2373,7 @@ public function provideMethodTagsData(): Iterator
 						'foo',
 						[],
 						'optional description',
+						[],
 					),
 				),
 			]),
@@ -2393,6 +2399,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2418,6 +2425,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2443,6 +2451,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2468,6 +2477,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2493,6 +2503,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2518,6 +2529,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2543,6 +2555,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2582,6 +2595,7 @@ public function provideMethodTagsData(): Iterator
 							),
 						],
 						'',
+						[],
 					),
 				),
 			]),
@@ -2761,6 +2775,7 @@ public function provideMethodTagsData(): Iterator
 						'foo',
 						[],
 						'',
+						[],
 					),
 				),
 			]),
@@ -3136,6 +3151,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3161,6 +3177,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3174,6 +3191,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBar',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3184,6 +3202,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'methodWithNoReturnType',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3209,6 +3228,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3234,6 +3254,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3247,6 +3268,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBarStatically',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3257,6 +3279,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'methodWithNoReturnTypeStatically',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3282,6 +3305,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'Get an integer with a description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3307,6 +3331,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'Do something with a description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3320,6 +3345,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBarWithDescription',
 							[],
 							'Get a Foo or a Bar with a description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3330,6 +3356,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'methodWithNoReturnTypeWithDescription',
 							[],
 							'Do something with a description but what, who knows!',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3355,6 +3382,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'Get an integer with a description statically.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3380,6 +3408,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'Do something with a description statically.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3393,6 +3422,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBarStaticallyWithDescription',
 							[],
 							'Get a Foo or a Bar with a description statically.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3403,6 +3433,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'methodWithNoReturnTypeStaticallyWithDescription',
 							[],
 							'Do something with a description statically, but what, who knows!',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3413,6 +3444,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClass',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3423,6 +3455,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescription',
 							[],
 							'A Description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3433,6 +3466,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getIntegerNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3443,6 +3477,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'doSomethingNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3456,6 +3491,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBarNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3466,6 +3502,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'methodWithNoReturnTypeNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3476,6 +3513,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getIntegerStaticallyNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3486,6 +3524,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'doSomethingStaticallyNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3499,6 +3538,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBarStaticallyNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3509,6 +3549,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'methodWithNoReturnTypeStaticallyNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3519,6 +3560,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getIntegerWithDescriptionNoParams',
 							[],
 							'Get an integer with a description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3529,6 +3571,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'doSomethingWithDescriptionNoParams',
 							[],
 							'Do something with a description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3542,6 +3585,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBarWithDescriptionNoParams',
 							[],
 							'Get a Foo or a Bar with a description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3552,6 +3596,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getIntegerStaticallyWithDescriptionNoParams',
 							[],
 							'Get an integer with a description statically.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3562,6 +3607,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'doSomethingStaticallyWithDescriptionNoParams',
 							[],
 							'Do something with a description statically.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3575,6 +3621,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'getFooOrBarStaticallyWithDescriptionNoParams',
 							[],
 							'Get a Foo or a Bar with a description statically.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3588,6 +3635,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClassNoParams',
 							[],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3601,6 +3649,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescriptionNoParams',
 							[],
 							'A Description.',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3619,6 +3668,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3644,6 +3694,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'',
+							[],
 						),
 					),
 					new PhpDocTagNode(
@@ -3654,6 +3705,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'overridenMethod',
 							[],
 							'',
+							[],
 						),
 					),
 				]),
@@ -3693,6 +3745,7 @@ public function provideMultiLinePhpDocData(): iterable
 								),
 							],
 							'find index of $v',
+							[],
 						),
 					),
 				]),

From be3aafa0ba1e01ffca5bfade1057eeeb09129f73 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 13:36:37 +0200
Subject: [PATCH 19/45] [BCB] Constructor parameter `$isReference` in
 `ParamTagValueNode` made required

---
 UPGRADING.md                                  |  1 +
 src/Ast/PhpDoc/ParamTagValueNode.php          |  2 +-
 tests/PHPStan/Parser/PhpDocParserTest.php     | 63 ++++++++++++++-----
 .../IntegrationPrinterWithPhpParserTest.php   |  1 +
 tests/PHPStan/Printer/PrinterTest.php         |  9 +--
 5 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index d7eaea2e..fb9e4e05 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -118,3 +118,4 @@ The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the
 
 * Constructor parameter `$isEquality` in `AssertTag*ValueNode` made required
 * Constructor parameter `$templateTypes` in `MethodTagValueNode` made required
+* Constructor parameter `$isReference` in `ParamTagValueNode` made required
diff --git a/src/Ast/PhpDoc/ParamTagValueNode.php b/src/Ast/PhpDoc/ParamTagValueNode.php
index 6e4089e4..8d1ef27a 100644
--- a/src/Ast/PhpDoc/ParamTagValueNode.php
+++ b/src/Ast/PhpDoc/ParamTagValueNode.php
@@ -22,7 +22,7 @@ class ParamTagValueNode implements PhpDocTagValueNode
 	/** @var string (may be empty) */
 	public string $description;
 
-	public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
+	public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference)
 	{
 		$this->type = $type;
 		$this->isReference = $isReference;
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 9991ac90..0b494d94 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -160,6 +160,7 @@ public function provideParamTagsData(): Iterator
 						false,
 						'$foo',
 						'',
+						false,
 					),
 				),
 			]),
@@ -176,6 +177,7 @@ public function provideParamTagsData(): Iterator
 						false,
 						'$foo',
 						'optional description',
+						false,
 					),
 				),
 			]),
@@ -192,6 +194,7 @@ public function provideParamTagsData(): Iterator
 						true,
 						'$foo',
 						'',
+						false,
 					),
 				),
 			]),
@@ -208,6 +211,7 @@ public function provideParamTagsData(): Iterator
 						true,
 						'$foo',
 						'optional description',
+						false,
 					),
 				),
 			]),
@@ -292,6 +296,7 @@ public function provideParamTagsData(): Iterator
 						false,
 						'$foo',
 						'optional description',
+						false,
 					),
 				),
 			]),
@@ -509,6 +514,7 @@ public function provideParamTagsData(): Iterator
 						'$parameters',
 						'{' . PHP_EOL .
 						'    Optional. Parameters for filtering the list of user assignments. Default empty array.',
+						false,
 					),
 				),
 				new PhpDocTextNode(''),
@@ -2966,6 +2972,7 @@ public function provideMultiLinePhpDocData(): iterable
 							false,
 							'$foo',
 							'1st multi world description',
+							false,
 						),
 					),
 					new PhpDocTagNode(
@@ -2975,6 +2982,7 @@ public function provideMultiLinePhpDocData(): iterable
 							false,
 							'$bar',
 							'2nd multi world description',
+							false,
 						),
 					),
 				]),
@@ -2995,6 +3003,7 @@ public function provideMultiLinePhpDocData(): iterable
 							'$foo',
 							'1st multi world description
 some text in the middle',
+							false,
 						),
 					),
 					new PhpDocTagNode(
@@ -3004,6 +3013,7 @@ public function provideMultiLinePhpDocData(): iterable
 							false,
 							'$bar',
 							'2nd multi world description',
+							false,
 						),
 					),
 				]),
@@ -3037,6 +3047,7 @@ public function provideMultiLinePhpDocData(): iterable
 
 
 some text in the middle',
+							false,
 						),
 					),
 					new PhpDocTextNode(''),
@@ -3051,6 +3062,7 @@ public function provideMultiLinePhpDocData(): iterable
 
 
 test',
+							false,
 						),
 					),
 
@@ -3082,6 +3094,7 @@ public function provideMultiLinePhpDocData(): iterable
 							false,
 							'$foo',
 							'@param string $bar',
+							false,
 						),
 					),
 				]),
@@ -3824,6 +3837,7 @@ public function provideMultiLinePhpDocData(): iterable
 							false,
 							'$list',
 							'',
+							false,
 						),
 					),
 					new PhpDocTextNode(''),
@@ -3903,7 +3917,7 @@ public function provideMultiLinePhpDocData(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', '')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', '', false)),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode(''),
 			]),
@@ -3930,6 +3944,7 @@ public function provideMultiLinePhpDocData(): iterable
 						. PHP_EOL
 						. PHP_EOL
 						. 'test',
+						false,
 					),
 				),
 			]),
@@ -4344,6 +4359,7 @@ public function provideExtendsTagsData(): Iterator
 						false,
 						'$test',
 						'',
+						false,
 					),
 				),
 			]),
@@ -4360,6 +4376,7 @@ public function provideExtendsTagsData(): Iterator
 						false,
 						'$test',
 						'some description',
+						false,
 					),
 				),
 			]),
@@ -5082,6 +5099,7 @@ public function provideRealWorldExampleData(): Iterator
 						'$field_definition',
 						'
   The field definition.',
+						false,
 					),
 				),
 				new PhpDocTextNode(''),
@@ -5158,6 +5176,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$request',
 						'- The request object',
+						false,
 					),
 				),
 				new PhpDocTextNode(''),
@@ -5328,6 +5347,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$foo',
 						'',
+						false,
 					),
 				),
 			]),
@@ -5356,6 +5376,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$foo',
 						'',
+						false,
 					),
 				),
 			]),
@@ -5384,6 +5405,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$foo',
 						'',
+						false,
 					),
 				),
 			]),
@@ -5437,6 +5459,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$pattern',
 						'',
+						false,
 					),
 				),
 				new PhpDocTagNode(
@@ -5446,6 +5469,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$subject',
 						'',
+						false,
 					),
 				),
 				new PhpDocTagNode(
@@ -5455,6 +5479,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$matches',
 						'',
+						false,
 					),
 				),
 				new PhpDocTagNode(
@@ -5464,6 +5489,7 @@ public function provideRealWorldExampleData(): Iterator
 						false,
 						'$flags',
 						'',
+						false,
 					),
 				),
 				new PhpDocTagNode(
@@ -5671,6 +5697,7 @@ public function dataParseTagValue(): array
 					false,
 					'$a',
 					'',
+					false,
 				),
 			],
 			[
@@ -5817,6 +5844,7 @@ public function provideCommentLikeDescriptions(): Iterator
 					false,
 					'$a',
 					'// this is a description',
+					false,
 				)),
 			]),
 		];
@@ -5833,6 +5861,7 @@ public function provideCommentLikeDescriptions(): Iterator
 					false,
 					'$a',
 					PHP_EOL . '// this is a comment',
+					false,
 				)),
 			]),
 		];
@@ -5849,6 +5878,7 @@ public function provideCommentLikeDescriptions(): Iterator
 					false,
 					'$a',
 					PHP_EOL . PHP_EOL . '// this is a comment',
+					false,
 				)),
 			]),
 		];
@@ -6720,6 +6750,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					false,
 					'$z',
 					'@X()',
+					false,
 				)),
 			]),
 		];
@@ -6733,6 +6764,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					false,
 					'$z',
 					'@\X\Y()',
+					false,
 				)),
 			]),
 		];
@@ -6746,6 +6778,7 @@ public function provideDoctrineWithoutDoctrineCheckData(): Iterator
 					false,
 					'$z',
 					'@X',
+					false,
 				)),
 			]),
 		];
@@ -7198,8 +7231,8 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			  ' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . '  paramA description')),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$b', PHP_EOL . '  paramB description')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . '  paramA description', false)),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$b', PHP_EOL . '  paramB description', false)),
 			]),
 		];
 
@@ -7212,9 +7245,9 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', '')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', '', false)),
 				new PhpDocTextNode(''),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$b', '')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$b', '', false)),
 			]),
 		];
 
@@ -7228,7 +7261,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', 'aaaa' . PHP_EOL . '  bbbb' . PHP_EOL . PHP_EOL . 'ccc')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', 'aaaa' . PHP_EOL . '  bbbb' . PHP_EOL . PHP_EOL . 'ccc', false)),
 			]),
 		];
 
@@ -7272,7 +7305,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
 				new PhpDocTagNode('@ORM\Column', new DoctrineTagValueNode(new DoctrineAnnotation('@ORM\Column', []), 'aaaa' . PHP_EOL . '  bbbb' . PHP_EOL . PHP_EOL . 'ccc')),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$b', '')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$b', '', false)),
 			]),
 		];
 
@@ -7285,7 +7318,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', '')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', '', false)),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode(''),
 			]),
@@ -7301,7 +7334,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . PHP_EOL . PHP_EOL . 'test')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . PHP_EOL . PHP_EOL . 'test', false)),
 			]),
 		];
 
@@ -7313,7 +7346,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', 'test')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', 'test', false)),
 				new PhpDocTextNode(''),
 			]),
 		];
@@ -7327,7 +7360,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', 'test')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', 'test', false)),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode(''),
 			]),
@@ -7343,7 +7376,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test', false)),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode(''),
 			]),
@@ -7360,7 +7393,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test', false)),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode(''),
@@ -7378,7 +7411,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test' . PHP_EOL . PHP_EOL . 'test 2')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test' . PHP_EOL . PHP_EOL . 'test 2', false)),
 				new PhpDocTextNode(''),
 			]),
 		];
@@ -7394,7 +7427,7 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' */',
 			new PhpDocNode([
 				new PhpDocTextNode('Real description'),
-				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test' . PHP_EOL . PHP_EOL . 'test 2')),
+				new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('int'), false, '$a', PHP_EOL . ' test' . PHP_EOL . PHP_EOL . 'test 2', false)),
 				new PhpDocTextNode(''),
 				new PhpDocTextNode(''),
 			]),
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index d2f2c361..6d5b02aa 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -43,6 +43,7 @@ public function enterNode(Node $node)
 						false,
 						'$b',
 						'',
+						false,
 					));
 				}
 				return $node;
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index e7d0be67..5d3e3624 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -339,7 +339,7 @@ public function enterNode(Node $node)
 			public function enterNode(Node $node)
 			{
 				if ($node instanceof PhpDocNode) {
-					$node->children[0] = new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', ''));
+					$node->children[0] = new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', '', false));
 					return $node;
 				}
 
@@ -379,7 +379,7 @@ public function enterNode(Node $node)
 			public function enterNode(Node $node)
 			{
 				if ($node instanceof PhpDocNode) {
-					array_unshift($node->children, new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', '')));
+					array_unshift($node->children, new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', '', false)));
 
 					return $node;
 				}
@@ -417,7 +417,7 @@ public function enterNode(Node $node)
 			{
 				if ($node instanceof PhpDocNode) {
 					array_splice($node->children, 1, 0, [
-						new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', '')),
+						new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', '', false)),
 					]);
 
 					return $node;
@@ -483,7 +483,7 @@ public function enterNode(Node $node)
 			public function enterNode(Node $node)
 			{
 				if ($node instanceof PhpDocNode) {
-					$node->children[count($node->children) - 1] = new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', ''));
+					$node->children[count($node->children) - 1] = new PhpDocTagNode('@param', new ParamTagValueNode(new IdentifierTypeNode('Baz'), false, '$a', '', false));
 					return $node;
 				}
 
@@ -2059,6 +2059,7 @@ public function dataPrintPhpDocNode(): iterable
 					false,
 					'$a',
 					'',
+					false,
 				)),
 			]),
 			'/**

From 1a0544cf0b174fc47b658dc4b95e2c99b9e9ea2e Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 13:37:45 +0200
Subject: [PATCH 20/45] [BCB] Constructor parameter `$isReference` in
 `TypelessParamTagValueNode` made required

---
 UPGRADING.md                                 | 1 +
 src/Ast/PhpDoc/TypelessParamTagValueNode.php | 2 +-
 tests/PHPStan/Parser/PhpDocParserTest.php    | 2 ++
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index fb9e4e05..a186db4f 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -119,3 +119,4 @@ The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the
 * Constructor parameter `$isEquality` in `AssertTag*ValueNode` made required
 * Constructor parameter `$templateTypes` in `MethodTagValueNode` made required
 * Constructor parameter `$isReference` in `ParamTagValueNode` made required
+* Constructor parameter `$isReference` in `TypelessParamTagValueNode` made required
diff --git a/src/Ast/PhpDoc/TypelessParamTagValueNode.php b/src/Ast/PhpDoc/TypelessParamTagValueNode.php
index 778972aa..bb99e781 100644
--- a/src/Ast/PhpDoc/TypelessParamTagValueNode.php
+++ b/src/Ast/PhpDoc/TypelessParamTagValueNode.php
@@ -19,7 +19,7 @@ class TypelessParamTagValueNode implements PhpDocTagValueNode
 	/** @var string (may be empty) */
 	public string $description;
 
-	public function __construct(bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
+	public function __construct(bool $isVariadic, string $parameterName, string $description, bool $isReference)
 	{
 		$this->isReference = $isReference;
 		$this->isVariadic = $isVariadic;
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 0b494d94..e3527f96 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -539,6 +539,7 @@ public function provideTypelessParamTagsData(): Iterator
 						false,
 						'$foo',
 						'description',
+						false,
 					),
 				),
 			]),
@@ -570,6 +571,7 @@ public function provideTypelessParamTagsData(): Iterator
 						true,
 						'$foo',
 						'description',
+						false,
 					),
 				),
 			]),

From e99bce958143494ce2da357508d94c1cf8befd2f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 13:41:28 +0200
Subject: [PATCH 21/45] [BCB] `ArrayShapeNode` constructor made private, added
 public static methods `createSealed()` and `createUnsealed()`

---
 UPGRADING.md                                  |   4 +
 src/Ast/Type/ArrayShapeNode.php               |  27 +++-
 src/Parser/TypeParser.php                     |   8 +-
 .../PHPStan/Ast/ToString/TypeToStringTest.php |  18 +--
 tests/PHPStan/Parser/PhpDocParserTest.php     |  14 +-
 tests/PHPStan/Parser/TypeParserTest.php       | 147 ++++++++----------
 tests/PHPStan/Printer/PrinterTest.php         |   2 +-
 7 files changed, 113 insertions(+), 107 deletions(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index a186db4f..29411629 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -114,6 +114,10 @@ Multi-line descriptions between tags were previously represented as separate [Ph
 
 The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the description of the first `@param` tag.
 
+### `ArrayShapeNode` construction changes
+
+`ArrayShapeNode` constructor made private, added public static methods `createSealed()` and `createUnsealed()`.
+
 ### Minor BC breaks
 
 * Constructor parameter `$isEquality` in `AssertTag*ValueNode` made required
diff --git a/src/Ast/Type/ArrayShapeNode.php b/src/Ast/Type/ArrayShapeNode.php
index e99ad321..3034c104 100644
--- a/src/Ast/Type/ArrayShapeNode.php
+++ b/src/Ast/Type/ArrayShapeNode.php
@@ -27,17 +27,36 @@ class ArrayShapeNode implements TypeNode
 	 * @param ArrayShapeItemNode[] $items
 	 * @param self::KIND_* $kind
 	 */
-	public function __construct(
+	private function __construct(
 		array $items,
 		bool $sealed = true,
-		string $kind = self::KIND_ARRAY,
-		?ArrayShapeUnsealedTypeNode $unsealedType = null
+		?ArrayShapeUnsealedTypeNode $unsealedType = null,
+		string $kind = self::KIND_ARRAY
 	)
 	{
 		$this->items = $items;
 		$this->sealed = $sealed;
-		$this->kind = $kind;
 		$this->unsealedType = $unsealedType;
+		$this->kind = $kind;
+	}
+
+
+	/**
+	 * @param ArrayShapeItemNode[] $items
+	 * @param self::KIND_* $kind
+	 */
+	public static function createSealed(array $items, string $kind = self::KIND_ARRAY): self
+	{
+		return new self($items, true, null, $kind);
+	}
+
+	/**
+	 * @param ArrayShapeItemNode[] $items
+	 * @param self::KIND_* $kind
+	 */
+	public static function createUnsealed(array $items, ?ArrayShapeUnsealedTypeNode $unsealedType, string $kind = self::KIND_ARRAY): self
+	{
+		return new self($items, false, $unsealedType, $kind);
 	}
 
 
diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index a6256473..ffcdd882 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -818,7 +818,7 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
 
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
-				return new Ast\Type\ArrayShapeNode($items, true, $kind);
+				return Ast\Type\ArrayShapeNode::createSealed($items, $kind);
 			}
 
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC)) {
@@ -846,7 +846,11 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
 
-		return new Ast\Type\ArrayShapeNode($items, $sealed, $kind, $unsealedType);
+		if ($sealed) {
+			return Ast\Type\ArrayShapeNode::createSealed($items, $kind);
+		}
+
+		return Ast\Type\ArrayShapeNode::createUnsealed($items, $unsealedType, $kind);
 	}
 
 
diff --git a/tests/PHPStan/Ast/ToString/TypeToStringTest.php b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
index 4b6f7b2a..0837af82 100644
--- a/tests/PHPStan/Ast/ToString/TypeToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
@@ -58,31 +58,31 @@ public static function provideArrayCases(): Generator
 		];
 
 		yield from [
-			['array{}', new ArrayShapeNode([])],
-			['array{...}', new ArrayShapeNode([], false)],
+			['array{}', ArrayShapeNode::createSealed([])],
+			['array{...}', ArrayShapeNode::createUnsealed([], null)],
 			[
 				'array{string, int, ...}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createUnsealed([
 					new ArrayShapeItemNode(null, false, new IdentifierTypeNode('string')),
 					new ArrayShapeItemNode(null, false, new IdentifierTypeNode('int')),
-				], false),
+				], null),
 			],
 			[
 				'array{\'foo\': Foo, \'bar\'?: Bar, 1: Baz}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(new ConstExprStringNode('foo', ConstExprStringNode::SINGLE_QUOTED), false, new IdentifierTypeNode('Foo')),
 					new ArrayShapeItemNode(new ConstExprStringNode('bar', ConstExprStringNode::SINGLE_QUOTED), true, new IdentifierTypeNode('Bar')),
 					new ArrayShapeItemNode(new ConstExprIntegerNode('1'), false, new IdentifierTypeNode('Baz')),
 				]),
 			],
-			['list{}', new ArrayShapeNode([], true, 'list')],
-			['list{...}', new ArrayShapeNode([], false, 'list')],
+			['list{}', ArrayShapeNode::createSealed([], 'list')],
+			['list{...}', ArrayShapeNode::createUnsealed([], null, 'list')],
 			[
 				'list{string, int, ...}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createUnsealed([
 					new ArrayShapeItemNode(null, false, new IdentifierTypeNode('string')),
 					new ArrayShapeItemNode(null, false, new IdentifierTypeNode('int')),
-				], false, 'list'),
+				], null, 'list'),
 			],
 		];
 	}
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index e3527f96..1a76b287 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -4507,7 +4507,7 @@ public function provideTypeAliasTagsData(): Iterator
 					'@phpstan-type',
 					new TypeAliasTagValueNode(
 						'Foo',
-						new ArrayShapeNode([]),
+						ArrayShapeNode::createSealed([]),
 					),
 				),
 				new PhpDocTagNode(
@@ -4539,7 +4539,7 @@ public function provideTypeAliasTagsData(): Iterator
 					'@phpstan-type',
 					new TypeAliasTagValueNode(
 						'Foo',
-						new ArrayShapeNode([]),
+						ArrayShapeNode::createSealed([]),
 					),
 				),
 				new PhpDocTagNode(
@@ -4560,7 +4560,7 @@ public function provideTypeAliasTagsData(): Iterator
 					'@phpstan-type',
 					new TypeAliasTagValueNode(
 						'Bar',
-						new ArrayShapeNode([]),
+						ArrayShapeNode::createSealed([]),
 					),
 				),
 			]),
@@ -5505,7 +5505,7 @@ public function provideRealWorldExampleData(): Iterator
 								[
 									new IdentifierTypeNode('array-key'),
 									new UnionTypeNode([
-										new ArrayShapeNode([
+										ArrayShapeNode::createSealed([
 											new ArrayShapeItemNode(null, false, new IdentifierTypeNode('string')),
 											new ArrayShapeItemNode(
 												null,
@@ -5516,7 +5516,7 @@ public function provideRealWorldExampleData(): Iterator
 												]),
 											),
 										]),
-										new ArrayShapeNode([
+										ArrayShapeNode::createSealed([
 											new ArrayShapeItemNode(null, false, new ConstTypeNode(new ConstExprStringNode('', ConstExprStringNode::SINGLE_QUOTED))),
 											new ArrayShapeItemNode(null, false, new ConstTypeNode(new ConstExprIntegerNode('-1'))),
 										]),
@@ -5552,7 +5552,7 @@ public function provideRealWorldExampleData(): Iterator
 										[
 											new IdentifierTypeNode('array-key'),
 											new UnionTypeNode([
-												new ArrayShapeNode([
+												ArrayShapeNode::createSealed([
 													new ArrayShapeItemNode(null, false, new IdentifierTypeNode('string')),
 													new ArrayShapeItemNode(
 														null,
@@ -5563,7 +5563,7 @@ public function provideRealWorldExampleData(): Iterator
 														]),
 													),
 												]),
-												new ArrayShapeNode([
+												ArrayShapeNode::createSealed([
 													new ArrayShapeItemNode(null, false, new IdentifierTypeNode('null')),
 													new ArrayShapeItemNode(null, false, new ConstTypeNode(new ConstExprIntegerNode('-1'))),
 												]),
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index de9d7230..b7361537 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -379,7 +379,7 @@ public function provideParseData(): array
 
 			[
 				'array{a: int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -389,7 +389,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a: ?int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -401,7 +401,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a?: ?int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						true,
@@ -413,7 +413,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{0: int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new ConstExprIntegerNode('0'),
 						false,
@@ -423,7 +423,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{0?: int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new ConstExprIntegerNode('0'),
 						true,
@@ -433,7 +433,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{int, int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						null,
 						false,
@@ -448,7 +448,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a: int, b: string}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -463,7 +463,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a?: int, b: string, 0: int, 1?: DateTime, hello: string}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						true,
@@ -493,7 +493,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a: int, b: array{c: callable(): int}}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -502,7 +502,7 @@ public function provideParseData(): array
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new ArrayShapeNode([
+						ArrayShapeNode::createSealed([
 							new ArrayShapeItemNode(
 								new IdentifierTypeNode('c'),
 								false,
@@ -519,7 +519,7 @@ public function provideParseData(): array
 			[
 				'?array{a: int}',
 				new NullableTypeNode(
-					new ArrayShapeNode([
+					ArrayShapeNode::createSealed([
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
@@ -548,7 +548,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{"a": int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('a', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
@@ -558,7 +558,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{\'a\': int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('a', ConstExprStringNode::SINGLE_QUOTED),
 						false,
@@ -568,7 +568,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{\'$ref\': int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('$ref', ConstExprStringNode::SINGLE_QUOTED),
 						false,
@@ -578,7 +578,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{"$ref": int}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new ConstExprStringNode('$ref', ConstExprStringNode::DOUBLE_QUOTED),
 						false,
@@ -590,7 +590,7 @@ public function provideParseData(): array
 				'array{
 				 *	a: int
 				 *}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -602,7 +602,7 @@ public function provideParseData(): array
 				'array{
 				 	a: int,
 				 }',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -615,7 +615,7 @@ public function provideParseData(): array
 				 	a: int,
 				 	b: string,
 				 }',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -634,7 +634,7 @@ public function provideParseData(): array
 				 	, b: string
 				 	, c: string
 				 }',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -657,7 +657,7 @@ public function provideParseData(): array
 				 	a: int,
 				 	b: string
 				 }',
-				new ArrayShapeNode([
+				ArrayShapeNode::createSealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -672,7 +672,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a: int, b: int, ...}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createUnsealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
@@ -683,11 +683,11 @@ public function provideParseData(): array
 						false,
 						new IdentifierTypeNode('int'),
 					),
-				], false),
+				], null),
 			],
 			[
 				'array{int, string, ...}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createUnsealed([
 					new ArrayShapeItemNode(
 						null,
 						false,
@@ -698,37 +698,37 @@ public function provideParseData(): array
 						false,
 						new IdentifierTypeNode('string'),
 					),
-				], false),
+				], null),
 			],
 			[
 				'array{...}',
-				new ArrayShapeNode([], false),
+				ArrayShapeNode::createUnsealed([], null),
 			],
 			[
 				'array{
 				 *	a: int,
 				 *	...
 				 *}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createUnsealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
 						new IdentifierTypeNode('int'),
 					),
-				], false),
+				], null),
 			],
 			[
 				'array{
 					a: int,
 					...,
 				}',
-				new ArrayShapeNode([
+				ArrayShapeNode::createUnsealed([
 					new ArrayShapeItemNode(
 						new IdentifierTypeNode('a'),
 						false,
 						new IdentifierTypeNode('int'),
 					),
-				], false),
+				], null),
 			],
 			[
 				'array{int, ..., string}',
@@ -744,7 +744,7 @@ public function provideParseData(): array
 				 	int,
 				 	string
 				 }',
-				new ArrayShapeNode(
+				ArrayShapeNode::createSealed(
 					[
 						new ArrayShapeItemNode(
 							null,
@@ -757,25 +757,23 @@ public function provideParseData(): array
 							new IdentifierTypeNode('string'),
 						),
 					],
-					true,
 					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
 				'array{...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_ARRAY,
 				),
 			],
 			[
 				'array{a: int, b?: int, ...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
@@ -788,8 +786,6 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
@@ -798,7 +794,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a:int,b?:int,...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
@@ -811,12 +807,11 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_ARRAY,
 				),
 			],
 			[
@@ -826,7 +821,7 @@ public function provideParseData(): array
 				. '  >  ' . PHP_EOL
 				. '  ,  ' . PHP_EOL
 				. ' }',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
@@ -839,8 +834,6 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
@@ -849,19 +842,18 @@ public function provideParseData(): array
 			],
 			[
 				'array{...<int, string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						new IdentifierTypeNode('int'),
 					),
+					ArrayShapeNode::KIND_ARRAY,
 				),
 			],
 			[
 				'array{a: int, b?: int, ...<int, string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
@@ -874,8 +866,6 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						new IdentifierTypeNode('int'),
@@ -884,7 +874,7 @@ public function provideParseData(): array
 			],
 			[
 				'array{a:int,b?:int,...<int,string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
@@ -897,8 +887,6 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						new IdentifierTypeNode('int'),
@@ -914,7 +902,7 @@ public function provideParseData(): array
 				. '  >  ' . PHP_EOL
 				. '  ,  ' . PHP_EOL
 				. '  }',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
@@ -927,8 +915,6 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_ARRAY,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						new IdentifierTypeNode('int'),
@@ -937,19 +923,18 @@ public function provideParseData(): array
 			],
 			[
 				'list{...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[],
-					false,
-					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
 				'list{int, int, ...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							null,
@@ -962,17 +947,16 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
 				'list{int,int,...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							null,
@@ -985,12 +969,11 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
@@ -1000,7 +983,7 @@ public function provideParseData(): array
 				. '  >  ' . PHP_EOL
 				. '  ,  ' . PHP_EOL
 				. '  }',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							null,
@@ -1013,17 +996,16 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
 				'list{0: int, 1?: int, ...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('0'),
@@ -1036,17 +1018,16 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
 				'list{0:int,1?:int,...<string>}',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('0'),
@@ -1059,12 +1040,11 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
@@ -1074,7 +1054,7 @@ public function provideParseData(): array
 				. '  >  ' . PHP_EOL
 				. '  ,  ' . PHP_EOL
 				. '  }',
-				new ArrayShapeNode(
+				ArrayShapeNode::createUnsealed(
 					[
 						new ArrayShapeItemNode(
 							new ConstExprIntegerNode('0'),
@@ -1087,12 +1067,11 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					],
-					false,
-					ArrayShapeNode::KIND_LIST,
 					new ArrayShapeUnsealedTypeNode(
 						new IdentifierTypeNode('string'),
 						null,
 					),
+					ArrayShapeNode::KIND_LIST,
 				),
 			],
 			[
@@ -1273,7 +1252,7 @@ public function provideParseData(): array
 				new CallableTypeNode(
 					new IdentifierTypeNode('callable'),
 					[],
-					new ArrayShapeNode([
+					ArrayShapeNode::createSealed([
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('a'),
 							false,
@@ -1389,7 +1368,7 @@ public function provideParseData(): array
 							false,
 						),
 					],
-					new ArrayShapeNode([
+					ArrayShapeNode::createSealed([
 						new ArrayShapeItemNode(
 							null,
 							false,
@@ -1516,7 +1495,7 @@ public function provideParseData(): array
 			[
 				'array{foo: int}[]',
 				new ArrayTypeNode(
-					new ArrayShapeNode([
+					ArrayShapeNode::createSealed([
 						new ArrayShapeItemNode(
 							new IdentifierTypeNode('foo'),
 							false,
@@ -1530,7 +1509,7 @@ public function provideParseData(): array
 				new UnionTypeNode([
 					new IdentifierTypeNode('int'),
 					new ArrayTypeNode(
-						new ArrayShapeNode([
+						ArrayShapeNode::createSealed([
 							new ArrayShapeItemNode(
 								new IdentifierTypeNode('foo'),
 								false,
@@ -1799,15 +1778,15 @@ public function provideParseData(): array
 			],
 			[
 				'array{}',
-				new ArrayShapeNode([]),
+				ArrayShapeNode::createSealed([]),
 			],
 			[
 				'array{}|int',
-				new UnionTypeNode([new ArrayShapeNode([]), new IdentifierTypeNode('int')]),
+				new UnionTypeNode([ArrayShapeNode::createSealed([]), new IdentifierTypeNode('int')]),
 			],
 			[
 				'int|array{}',
-				new UnionTypeNode([new IdentifierTypeNode('int'), new ArrayShapeNode([])]),
+				new UnionTypeNode([new IdentifierTypeNode('int'), ArrayShapeNode::createSealed([])]),
 			],
 			[
 				'callable(' . PHP_EOL .
@@ -2236,7 +2215,7 @@ public function provideParseData(): array
 					new ObjectShapeItemNode(
 						new IdentifierTypeNode('b'),
 						false,
-						new ArrayShapeNode([
+						ArrayShapeNode::createSealed([
 							new ArrayShapeItemNode(
 								new IdentifierTypeNode('c'),
 								false,
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 5d3e3624..c4fd0007 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -1999,7 +1999,7 @@ public function dataPrintType(): iterable
 			'self::TYPES[int]',
 		];
 		yield [
-			new ArrayShapeNode([
+			ArrayShapeNode::createSealed([
 				new ArrayShapeItemNode(
 					new IdentifierTypeNode('name'),
 					false,

From 34fa2aedc52d45ae294cad61c3ec7a9a6353fba6 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 14:19:09 +0200
Subject: [PATCH 22/45] [BCB] Constructor parameter `$templateTypes` in
 `CallableTypeNode` made required

---
 UPGRADING.md                                  |  1 +
 src/Ast/Type/CallableTypeNode.php             |  2 +-
 .../PHPStan/Ast/ToString/TypeToStringTest.php | 10 ++--
 tests/PHPStan/Parser/PhpDocParserTest.php     |  5 ++
 tests/PHPStan/Parser/TypeParserTest.php       | 60 +++++++++++++------
 tests/PHPStan/Printer/PrinterTest.php         |  6 +-
 6 files changed, 58 insertions(+), 26 deletions(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index 29411629..1bcfac23 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -124,3 +124,4 @@ The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the
 * Constructor parameter `$templateTypes` in `MethodTagValueNode` made required
 * Constructor parameter `$isReference` in `ParamTagValueNode` made required
 * Constructor parameter `$isReference` in `TypelessParamTagValueNode` made required
+* Constructor parameter `$templateTypes` in `CallableTypeNode` made required
diff --git a/src/Ast/Type/CallableTypeNode.php b/src/Ast/Type/CallableTypeNode.php
index 2f0f6361..0a9e3442 100644
--- a/src/Ast/Type/CallableTypeNode.php
+++ b/src/Ast/Type/CallableTypeNode.php
@@ -25,7 +25,7 @@ class CallableTypeNode implements TypeNode
 	 * @param CallableTypeParameterNode[] $parameters
 	 * @param TemplateTagValueNode[]  $templateTypes
 	 */
-	public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType, array $templateTypes = [])
+	public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType, array $templateTypes)
 	{
 		$this->identifier = $identifier;
 		$this->parameters = $parameters;
diff --git a/tests/PHPStan/Ast/ToString/TypeToStringTest.php b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
index 0837af82..6716e433 100644
--- a/tests/PHPStan/Ast/ToString/TypeToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/TypeToStringTest.php
@@ -92,34 +92,34 @@ public static function provideCallableCases(): Generator
 		yield from [
 			[
 				'\\Closure(): string',
-				new CallableTypeNode(new IdentifierTypeNode('\Closure'), [], new IdentifierTypeNode('string')),
+				new CallableTypeNode(new IdentifierTypeNode('\Closure'), [], new IdentifierTypeNode('string'), []),
 			],
 			[
 				'callable(int, int $foo): void',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [
 					new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '', false),
 					new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '$foo', false),
-				], new IdentifierTypeNode('void')),
+				], new IdentifierTypeNode('void'), []),
 			],
 			[
 				'callable(int=, int $foo=): void',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [
 					new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '', true),
 					new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '$foo', true),
-				], new IdentifierTypeNode('void')),
+				], new IdentifierTypeNode('void'), []),
 			],
 			[
 				'callable(int &, int &$foo): void',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [
 					new CallableTypeParameterNode(new IdentifierTypeNode('int'), true, false, '', false),
 					new CallableTypeParameterNode(new IdentifierTypeNode('int'), true, false, '$foo', false),
-				], new IdentifierTypeNode('void')),
+				], new IdentifierTypeNode('void'), []),
 			],
 			[
 				'callable(string ...$foo): void',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [
 					new CallableTypeParameterNode(new IdentifierTypeNode('string'), false, true, '$foo', false),
-				], new IdentifierTypeNode('void')),
+				], new IdentifierTypeNode('void'), []),
 			],
 		];
 	}
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 1a76b287..b70c6ea6 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -1754,6 +1754,7 @@ public function provideReturnTagsData(): Iterator
 								),
 							],
 							new IdentifierTypeNode('string'),
+							[],
 						),
 						'',
 					),
@@ -2909,6 +2910,7 @@ public function provideSingleLinePhpDocData(): Iterator
 								new CallableTypeParameterNode(new IdentifierTypeNode('int'), false, false, '', false),
 							],
 							new IdentifierTypeNode('void'),
+							[],
 						),
 						'',
 						'',
@@ -5345,6 +5347,7 @@ public function provideRealWorldExampleData(): Iterator
 								new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false),
 							],
 							new IdentifierTypeNode('void'),
+							[],
 						),
 						false,
 						'$foo',
@@ -5374,6 +5377,7 @@ public function provideRealWorldExampleData(): Iterator
 								new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false),
 							],
 							new IdentifierTypeNode('void'),
+							[],
 						),
 						false,
 						'$foo',
@@ -5403,6 +5407,7 @@ public function provideRealWorldExampleData(): Iterator
 								new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false),
 							],
 							new IdentifierTypeNode('void'),
+							[],
 						),
 						false,
 						'$foo',
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index b7361537..b171689d 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -510,6 +510,7 @@ public function provideParseData(): array
 									new IdentifierTypeNode('callable'),
 									[],
 									new IdentifierTypeNode('int'),
+									[],
 								),
 							),
 						]),
@@ -1143,6 +1144,7 @@ public function provideParseData(): array
 					new IdentifierTypeNode('callable'),
 					[],
 					new IdentifierTypeNode('Foo'),
+					[],
 				),
 			],
 			[
@@ -1151,6 +1153,7 @@ public function provideParseData(): array
 					new IdentifierTypeNode('pure-callable'),
 					[],
 					new IdentifierTypeNode('Foo'),
+					[],
 				),
 			],
 			[
@@ -1159,6 +1162,7 @@ public function provideParseData(): array
 					new IdentifierTypeNode('pure-Closure'),
 					[],
 					new IdentifierTypeNode('Foo'),
+					[],
 				),
 			],
 			[
@@ -1169,6 +1173,7 @@ public function provideParseData(): array
 					new NullableTypeNode(
 						new IdentifierTypeNode('Foo'),
 					),
+					[],
 				),
 			],
 			[
@@ -1185,6 +1190,7 @@ public function provideParseData(): array
 							GenericTypeNode::VARIANCE_INVARIANT,
 						],
 					),
+					[],
 				),
 			],
 			[
@@ -1201,6 +1207,7 @@ public function provideParseData(): array
 							GenericTypeNode::VARIANCE_INVARIANT,
 						],
 					)),
+					[],
 				),
 			],
 			[
@@ -1210,6 +1217,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('callable'),
 						[],
 						new IdentifierTypeNode('Foo'),
+						[],
 					),
 					new IdentifierTypeNode('Bar'),
 				]),
@@ -1221,6 +1229,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('callable'),
 						[],
 						new IdentifierTypeNode('Foo'),
+						[],
 					),
 					new IdentifierTypeNode('Bar'),
 				]),
@@ -1234,6 +1243,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('Foo'),
 						new IdentifierTypeNode('Bar'),
 					]),
+					[],
 				),
 			],
 			[
@@ -1245,6 +1255,7 @@ public function provideParseData(): array
 						new IdentifierTypeNode('Foo'),
 						new IdentifierTypeNode('Bar'),
 					]),
+					[],
 				),
 			],
 			[
@@ -1259,6 +1270,7 @@ public function provideParseData(): array
 							new IdentifierTypeNode('int'),
 						),
 					]),
+					[],
 				),
 			],
 			[
@@ -1289,6 +1301,7 @@ public function provideParseData(): array
 						),
 					],
 					new IdentifierTypeNode('Foo'),
+					[],
 				),
 			],
 			[
@@ -1542,6 +1555,7 @@ public function provideParseData(): array
 					new ArrayTypeNode(
 						new IdentifierTypeNode('int'),
 					),
+					[],
 				),
 			],
 			[
@@ -1556,7 +1570,7 @@ public function provideParseData(): array
 				'callable(mixed...): TReturn',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [
 					new CallableTypeParameterNode(new IdentifierTypeNode('mixed'), false, true, '', false),
-				], new IdentifierTypeNode('TReturn')),
+				], new IdentifierTypeNode('TReturn'), []),
 			],
 			[
 				"'foo'|'bar'",
@@ -1798,6 +1812,7 @@ public function provideParseData(): array
 						new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false),
 					],
 					new IdentifierTypeNode('void'),
+					[],
 				),
 			],
 			[
@@ -1812,6 +1827,7 @@ public function provideParseData(): array
 						new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 					],
 					new IdentifierTypeNode('void'),
+					[],
 				),
 			],
 			[
@@ -1825,6 +1841,7 @@ public function provideParseData(): array
 						new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 					],
 					new IdentifierTypeNode('void'),
+					[],
 				),
 			],
 			[
@@ -1845,6 +1862,7 @@ public function provideParseData(): array
 									new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 								],
 								new IdentifierTypeNode('void'),
+								[],
 							),
 							false,
 							false,
@@ -1853,6 +1871,7 @@ public function provideParseData(): array
 						),
 					],
 					new IdentifierTypeNode('void'),
+					[],
 				),
 			],
 			[
@@ -1873,6 +1892,7 @@ public function provideParseData(): array
 									new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false),
 								],
 								new IdentifierTypeNode('void'),
+								[],
 							),
 							false,
 							false,
@@ -1881,6 +1901,7 @@ public function provideParseData(): array
 						),
 					],
 					new IdentifierTypeNode('void'),
+					[],
 				),
 			],
 			[
@@ -2223,6 +2244,7 @@ public function provideParseData(): array
 									new IdentifierTypeNode('callable'),
 									[],
 									new IdentifierTypeNode('int'),
+									[],
 								),
 							),
 						]),
@@ -2248,6 +2270,7 @@ public function provideParseData(): array
 									new IdentifierTypeNode('callable'),
 									[],
 									new IdentifierTypeNode('int'),
+									[],
 								),
 							),
 						]),
@@ -2502,18 +2525,20 @@ public function provideParseData(): array
 							new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false),
 						],
 						new IdentifierTypeNode('Bar'),
+						[],
 					),
+					[],
 				),
 			],
 			[
 				'callable(): ?int',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new IdentifierTypeNode('int'))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new IdentifierTypeNode('int')), []),
 			],
 			[
 				'callable(): object{foo: int}',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ObjectShapeNode([
 					new ObjectShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
-				])),
+				]), []),
 			],
 			[
 				'callable(): object{foo: int}[]',
@@ -2525,31 +2550,32 @@ public function provideParseData(): array
 							new ObjectShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
 						]),
 					),
+					[],
 				),
 			],
 			[
 				'callable(): int[][][]',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ArrayTypeNode(new ArrayTypeNode(new IdentifierTypeNode('int'))))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ArrayTypeNode(new ArrayTypeNode(new IdentifierTypeNode('int')))), []),
 			],
 			[
 				'callable(): (int[][][])',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ArrayTypeNode(new ArrayTypeNode(new IdentifierTypeNode('int'))))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ArrayTypeNode(new ArrayTypeNode(new IdentifierTypeNode('int')))), []),
 			],
 			[
 				'(callable(): int[])[][]',
 				new ArrayTypeNode(
 					new ArrayTypeNode(
-						new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int'))),
+						new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int')), []),
 					),
 				),
 			],
 			[
 				'callable(): $this',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ThisTypeNode()),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ThisTypeNode(), []),
 			],
 			[
 				'callable(): $this[]',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ThisTypeNode())),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ThisTypeNode()), []),
 			],
 			[
 				'2.5|3',
@@ -2560,29 +2586,29 @@ public function provideParseData(): array
 			],
 			[
 				'callable(): 3.5',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstExprFloatNode('3.5'))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstExprFloatNode('3.5')), []),
 			],
 			[
 				'callable(): 3.5[]',
 				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(
 					new ConstTypeNode(new ConstExprFloatNode('3.5')),
-				)),
+				), []),
 			],
 			[
 				'callable(): Foo',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new IdentifierTypeNode('Foo')),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new IdentifierTypeNode('Foo'), []),
 			],
 			[
 				'callable(): (Foo)[]',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('Foo'))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('Foo')), []),
 			],
 			[
 				'callable(): Foo::BAR',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', 'BAR'))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', 'BAR')), []),
 			],
 			[
 				'callable(): Foo::*',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', '*'))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ConstTypeNode(new ConstFetchNode('Foo', '*')), []),
 			],
 			[
 				'?Foo[]',
@@ -2590,11 +2616,11 @@ public function provideParseData(): array
 			],
 			[
 				'callable(): ?Foo',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new IdentifierTypeNode('Foo'))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new IdentifierTypeNode('Foo')), []),
 			],
 			[
 				'callable(): ?Foo[]',
-				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new ArrayTypeNode(new IdentifierTypeNode('Foo')))),
+				new CallableTypeNode(new IdentifierTypeNode('callable'), [], new NullableTypeNode(new ArrayTypeNode(new IdentifierTypeNode('Foo'))), []),
 			],
 			[
 				'?(Foo|Bar)',
@@ -2663,7 +2689,7 @@ public function provideParseData(): array
 					new IdentifierTypeNode('TService'),
 					new IdentifierTypeNode('mixed'),
 					false,
-				)),
+				), []),
 			],
 			[
 				'MongoCollection <p>Returns a collection object representing the new collection.</p>',
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index c4fd0007..5306b5b9 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -1940,17 +1940,17 @@ public function dataPrintType(): iterable
 			new CallableTypeNode(new IdentifierTypeNode('callable'), [], new UnionTypeNode([
 				new IdentifierTypeNode('int'),
 				new IdentifierTypeNode('string'),
-			])),
+			]), []),
 			'callable(): (int|string)',
 		];
 		yield [
-			new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ArrayTypeNode(new ArrayTypeNode(new IdentifierTypeNode('int'))))),
+			new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new ArrayTypeNode(new ArrayTypeNode(new IdentifierTypeNode('int')))), []),
 			'callable(): int[][][]',
 		];
 		yield [
 			new ArrayTypeNode(
 				new ArrayTypeNode(
-					new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int'))),
+					new CallableTypeNode(new IdentifierTypeNode('callable'), [], new ArrayTypeNode(new IdentifierTypeNode('int')), []),
 				),
 			),
 			'(callable(): int[])[][]',

From 5f84c953cf3f18566954ddcbfe50e6e322b389f8 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 14:34:05 +0200
Subject: [PATCH 23/45] [BCB] Constructor parameters `$expectedTokenValue` and
 `$currentTokenLine` in ParserException made required

---
 UPGRADING.md                                  |  1 +
 src/Parser/ParserException.php                |  8 ++--
 .../Ast/ToString/PhpDocToStringTest.php       |  2 +-
 tests/PHPStan/Parser/TypeParserTest.php       | 40 +++++++++++++++++++
 4 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index 1bcfac23..51f947d0 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -125,3 +125,4 @@ The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the
 * Constructor parameter `$isReference` in `ParamTagValueNode` made required
 * Constructor parameter `$isReference` in `TypelessParamTagValueNode` made required
 * Constructor parameter `$templateTypes` in `CallableTypeNode` made required
+* Constructor parameters `$expectedTokenValue` and `$currentTokenLine` in `ParserException` made required
diff --git a/src/Parser/ParserException.php b/src/Parser/ParserException.php
index 67715e74..ae72fd9c 100644
--- a/src/Parser/ParserException.php
+++ b/src/Parser/ParserException.php
@@ -22,17 +22,17 @@ class ParserException extends Exception
 
 	private int $expectedTokenType;
 
-	private ?string $expectedTokenValue = null;
+	private ?string $expectedTokenValue;
 
-	private ?int $currentTokenLine = null;
+	private ?int $currentTokenLine;
 
 	public function __construct(
 		string $currentTokenValue,
 		int $currentTokenType,
 		int $currentOffset,
 		int $expectedTokenType,
-		?string $expectedTokenValue = null,
-		?int $currentTokenLine = null
+		?string $expectedTokenValue,
+		?int $currentTokenLine
 	)
 	{
 		$this->currentTokenValue = $currentTokenValue;
diff --git a/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php b/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
index 1d26181c..7827b04d 100644
--- a/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
+++ b/tests/PHPStan/Ast/ToString/PhpDocToStringTest.php
@@ -137,7 +137,7 @@ public static function provideOtherCases(): Generator
 			'#desc',
 			new InvalidTagValueNode(
 				'#desc',
-				new ParserException('#desc', Lexer::TOKEN_OTHER, 11, Lexer::TOKEN_IDENTIFIER),
+				new ParserException('#desc', Lexer::TOKEN_OTHER, 11, Lexer::TOKEN_IDENTIFIER, null, null),
 			),
 		];
 
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index b171689d..a05bdbfa 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -536,6 +536,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_END,
 					6,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -545,6 +547,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_OTHER,
 					8,
 					Lexer::TOKEN_CLOSE_CURLY_BRACKET,
+					null,
+					null,
 				),
 			],
 			[
@@ -738,6 +742,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_IDENTIFIER,
 					16,
 					Lexer::TOKEN_CLOSE_CURLY_BRACKET,
+					null,
+					null,
 				),
 			],
 			[
@@ -1082,6 +1088,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					10,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -1091,6 +1099,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					14,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -1100,6 +1110,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_COMMA,
 					21,
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
+					null,
+					null,
 				),
 			],
 			[
@@ -1109,6 +1121,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_COMMA,
 					21,
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
+					null,
+					null,
 				),
 			],
 			[
@@ -1118,6 +1132,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					9,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -1127,6 +1143,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_COMMA,
 					12,
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
+					null,
+					null,
 				),
 			],
 			[
@@ -1136,6 +1154,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_COMMA,
 					12,
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
+					null,
+					null,
 				),
 			],
 			[
@@ -1330,6 +1350,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_END,
 					9,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -2089,6 +2111,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_IDENTIFIER,
 					14,
 					Lexer::TOKEN_COLON,
+					null,
+					null,
 				),
 			],
 			[
@@ -2114,6 +2138,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_VARIABLE,
 					15,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -2151,6 +2177,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
 					13,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -2160,6 +2188,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_IDENTIFIER,
 					16,
 					Lexer::TOKEN_CLOSE_ANGLE_BRACKET,
+					null,
+					null,
 				),
 			],
 			[
@@ -2296,6 +2326,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_END,
 					7,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -2305,6 +2337,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_OTHER,
 					9,
 					Lexer::TOKEN_COLON,
+					null,
+					null,
 				),
 			],
 			[
@@ -2314,6 +2348,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_CLOSE_CURLY_BRACKET,
 					10,
 					Lexer::TOKEN_COLON,
+					null,
+					null,
 				),
 			],
 			[
@@ -2323,6 +2359,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_END,
 					7,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[
@@ -2332,6 +2370,8 @@ public function provideParseData(): array
 					Lexer::TOKEN_END,
 					7,
 					Lexer::TOKEN_IDENTIFIER,
+					null,
+					null,
 				),
 			],
 			[

From 5504a3b6d9ddaeed8cf7961c5d4e60d7c1b9e721 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 14:44:28 +0200
Subject: [PATCH 24/45] parseOptionalDescription - always specify
 `$limitStartToken`

---
 src/Parser/PhpDocParser.php | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index e4aa77b3..35a79adf 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -463,7 +463,7 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
 				case '@psalm-template-contravariant':
 					$tagValue = $this->typeParser->parseTemplateTagValue(
 						$tokens,
-						fn ($tokens) => $this->parseOptionalDescription($tokens),
+						fn ($tokens) => $this->parseOptionalDescription($tokens, true),
 					);
 					break;
 
@@ -536,7 +536,7 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
 
 		} catch (ParserException $e) {
 			$tokens->rollback();
-			$tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e);
+			$tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens, false), $e);
 		}
 
 		return $this->enrichWithAttributes($tokens, $tagValue, $startLine, $startIndex);
@@ -865,7 +865,7 @@ private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTag
 		$isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE);
 		$isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC);
 		$parameterName = $this->parseRequiredVariableName($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 
 		if ($type !== null) {
 			return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference);
@@ -878,7 +878,7 @@ private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTag
 	private function parseParamImmediatelyInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode
 	{
 		$parameterName = $this->parseRequiredVariableName($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 
 		return new Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode($parameterName, $description);
 	}
@@ -887,7 +887,7 @@ private function parseParamImmediatelyInvokedCallableTagValue(TokenIterator $tok
 	private function parseParamLaterInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode
 	{
 		$parameterName = $this->parseRequiredVariableName($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 
 		return new Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode($parameterName, $description);
 	}
@@ -897,7 +897,7 @@ private function parseParamClosureThisTagValue(TokenIterator $tokens): Ast\PhpDo
 	{
 		$type = $this->typeParser->parse($tokens);
 		$parameterName = $this->parseRequiredVariableName($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 
 		return new Ast\PhpDoc\ParamClosureThisTagValueNode($type, $parameterName, $description);
 	}
@@ -950,7 +950,7 @@ private function parseRequireImplementsTagValue(TokenIterator $tokens): Ast\PhpD
 
 	private function parseDeprecatedTagValue(TokenIterator $tokens): Ast\PhpDoc\DeprecatedTagValueNode
 	{
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 		return new Ast\PhpDoc\DeprecatedTagValueNode($description);
 	}
 
@@ -959,7 +959,7 @@ private function parsePropertyTagValue(TokenIterator $tokens): Ast\PhpDoc\Proper
 	{
 		$type = $this->typeParser->parse($tokens);
 		$parameterName = $this->parseRequiredVariableName($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 		return new Ast\PhpDoc\PropertyTagValueNode($type, $parameterName, $description);
 	}
 
@@ -1018,7 +1018,7 @@ private function parseMethodTagValue(TokenIterator $tokens): Ast\PhpDoc\MethodTa
 		}
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
 
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 		return new Ast\PhpDoc\MethodTagValueNode($isStatic, $returnType, $methodName, $parameters, $description, $templateTypes);
 	}
 
@@ -1071,7 +1071,7 @@ private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): A
 			$this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex),
 		);
 
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
 
 		switch ($tagName) {
 			case '@extends':
@@ -1112,7 +1112,7 @@ private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeA
 
 			return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
 		} catch (ParserException $e) {
-			$this->parseOptionalDescription($tokens);
+			$this->parseOptionalDescription($tokens, false);
 			return new Ast\PhpDoc\TypeAliasTagValueNode(
 				$alias,
 				$this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex),
@@ -1156,7 +1156,7 @@ private function parseAssertTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTa
 		$isEquality = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
 		$type = $this->typeParser->parse($tokens);
 		$parameter = $this->parseAssertParameter($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 
 		if (array_key_exists('method', $parameter)) {
 			return new Ast\PhpDoc\AssertTagMethodValueNode($type, $parameter['parameter'], $parameter['method'], $isNegated, $description, $isEquality);
@@ -1201,7 +1201,7 @@ private function parseAssertParameter(TokenIterator $tokens): array
 	private function parseSelfOutTagValue(TokenIterator $tokens): Ast\PhpDoc\SelfOutTagValueNode
 	{
 		$type = $this->typeParser->parse($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
 
 		return new Ast\PhpDoc\SelfOutTagValueNode($type, $description);
 	}
@@ -1210,7 +1210,7 @@ private function parseParamOutTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamO
 	{
 		$type = $this->typeParser->parse($tokens);
 		$parameterName = $this->parseRequiredVariableName($tokens);
-		$description = $this->parseOptionalDescription($tokens);
+		$description = $this->parseOptionalDescription($tokens, false);
 
 		return new Ast\PhpDoc\ParamOutTagValueNode($type, $parameterName, $description);
 	}
@@ -1240,7 +1240,10 @@ private function parseRequiredVariableName(TokenIterator $tokens): string
 		return $parameterName;
 	}
 
-	private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = false): string
+	/**
+	 * @param bool $limitStartToken true should be used when the description immediately follows a parsed type
+	 */
+	private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken): string
 	{
 		if ($limitStartToken) {
 			foreach (self::DISALLOWED_DESCRIPTION_START_TOKENS as $disallowedStartToken) {

From 0fe292d7fa9deb3a869a4a74363497e1ae527a54 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 14:49:48 +0200
Subject: [PATCH 25/45] [BCB] `ArrayShapeItemNode` and `ObjectShapeItemNode`
 are not standalone TypeNode, just Node

---
 UPGRADING.md                            |  1 +
 src/Ast/Type/ArrayShapeItemNode.php     |  3 +-
 src/Ast/Type/ObjectShapeItemNode.php    |  3 +-
 src/Printer/Printer.php                 | 52 ++++++++++++-------------
 tests/PHPStan/Parser/TypeParserTest.php |  4 +-
 5 files changed, 33 insertions(+), 30 deletions(-)

diff --git a/UPGRADING.md b/UPGRADING.md
index 51f947d0..b673bccf 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -126,3 +126,4 @@ The line with `some text in the middle` in phpdoc-parser 2.0 is now part of the
 * Constructor parameter `$isReference` in `TypelessParamTagValueNode` made required
 * Constructor parameter `$templateTypes` in `CallableTypeNode` made required
 * Constructor parameters `$expectedTokenValue` and `$currentTokenLine` in `ParserException` made required
+* `ArrayShapeItemNode` and `ObjectShapeItemNode` are not standalone TypeNode, just Node
diff --git a/src/Ast/Type/ArrayShapeItemNode.php b/src/Ast/Type/ArrayShapeItemNode.php
index 2c47eda2..bed62381 100644
--- a/src/Ast/Type/ArrayShapeItemNode.php
+++ b/src/Ast/Type/ArrayShapeItemNode.php
@@ -4,10 +4,11 @@
 
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
+use PHPStan\PhpDocParser\Ast\Node;
 use PHPStan\PhpDocParser\Ast\NodeAttributes;
 use function sprintf;
 
-class ArrayShapeItemNode implements TypeNode
+class ArrayShapeItemNode implements Node
 {
 
 	use NodeAttributes;
diff --git a/src/Ast/Type/ObjectShapeItemNode.php b/src/Ast/Type/ObjectShapeItemNode.php
index d64b92e1..f7aa9efb 100644
--- a/src/Ast/Type/ObjectShapeItemNode.php
+++ b/src/Ast/Type/ObjectShapeItemNode.php
@@ -3,10 +3,11 @@
 namespace PHPStan\PhpDocParser\Ast\Type;
 
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
+use PHPStan\PhpDocParser\Ast\Node;
 use PHPStan\PhpDocParser\Ast\NodeAttributes;
 use function sprintf;
 
-class ObjectShapeItemNode implements TypeNode
+class ObjectShapeItemNode implements Node
 {
 
 	use NodeAttributes;
diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php
index 915ab39b..7834dc04 100644
--- a/src/Printer/Printer.php
+++ b/src/Printer/Printer.php
@@ -248,6 +248,30 @@ function (PhpDocChildNode $child): string {
 		if ($node instanceof DoctrineArrayItem) {
 			return (string) $node;
 		}
+		if ($node instanceof ArrayShapeItemNode) {
+			if ($node->keyName !== null) {
+				return sprintf(
+					'%s%s: %s',
+					$this->print($node->keyName),
+					$node->optional ? '?' : '',
+					$this->printType($node->valueType),
+				);
+			}
+
+			return $this->printType($node->valueType);
+		}
+		if ($node instanceof ObjectShapeItemNode) {
+			if ($node->keyName !== null) {
+				return sprintf(
+					'%s%s: %s',
+					$this->print($node->keyName),
+					$node->optional ? '?' : '',
+					$this->printType($node->valueType),
+				);
+			}
+
+			return $this->printType($node->valueType);
+		}
 
 		throw new LogicException(sprintf('Unknown node type %s', get_class($node)));
 	}
@@ -364,7 +388,7 @@ private function printTagValue(PhpDocTagValueNode $node): string
 	private function printType(TypeNode $node): string
 	{
 		if ($node instanceof ArrayShapeNode) {
-			$items = array_map(fn (ArrayShapeItemNode $item): string => $this->printType($item), $node->items);
+			$items = array_map(fn (ArrayShapeItemNode $item): string => $this->print($item), $node->items);
 
 			if (! $node->sealed) {
 				$items[] = '...' . ($node->unsealedType === null ? '' : $this->print($node->unsealedType));
@@ -372,18 +396,6 @@ private function printType(TypeNode $node): string
 
 			return $node->kind . '{' . implode(', ', $items) . '}';
 		}
-		if ($node instanceof ArrayShapeItemNode) {
-			if ($node->keyName !== null) {
-				return sprintf(
-					'%s%s: %s',
-					$this->print($node->keyName),
-					$node->optional ? '?' : '',
-					$this->printType($node->valueType),
-				);
-			}
-
-			return $this->printType($node->valueType);
-		}
 		if ($node instanceof ArrayTypeNode) {
 			return $this->printOffsetAccessType($node->type) . '[]';
 		}
@@ -469,22 +481,10 @@ private function printType(TypeNode $node): string
 			return '?' . $this->printType($node->type);
 		}
 		if ($node instanceof ObjectShapeNode) {
-			$items = array_map(fn (ObjectShapeItemNode $item): string => $this->printType($item), $node->items);
+			$items = array_map(fn (ObjectShapeItemNode $item): string => $this->print($item), $node->items);
 
 			return 'object{' . implode(', ', $items) . '}';
 		}
-		if ($node instanceof ObjectShapeItemNode) {
-			if ($node->keyName !== null) {
-				return sprintf(
-					'%s%s: %s',
-					$this->print($node->keyName),
-					$node->optional ? '?' : '',
-					$this->printType($node->valueType),
-				);
-			}
-
-			return $this->printType($node->valueType);
-		}
 		if ($node instanceof OffsetAccessTypeNode) {
 			return $this->printOffsetAccessType($node->type) . '[' . $this->printType($node->offset) . ']';
 		}
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index a05bdbfa..ad19f10f 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -2888,7 +2888,7 @@ public function dataLinesAndIndexes(): iterable
 					6,
 				],
 				[
-					static fn (ArrayShapeNode $typeNode): TypeNode => $typeNode->items[0],
+					static fn (ArrayShapeNode $typeNode): Node => $typeNode->items[0],
 					'foo: int',
 					1,
 					1,
@@ -2924,7 +2924,7 @@ public function dataLinesAndIndexes(): iterable
 					6,
 				],
 				[
-					static fn (ObjectShapeNode $typeNode): TypeNode => $typeNode->items[0],
+					static fn (ObjectShapeNode $typeNode): Node => $typeNode->items[0],
 					'foo: int',
 					1,
 					1,

From 5d9ed0826ede15c989db0c91c5bb7e14899c41f0 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 15 Sep 2024 21:18:15 +0200
Subject: [PATCH 26/45] Return to the more original behaviour

---
 src/Parser/PhpDocParser.php               | 38 +--------------------
 tests/PHPStan/Parser/PhpDocParserTest.php | 40 +++++++++++------------
 2 files changed, 21 insertions(+), 57 deletions(-)

diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index 35a79adf..f5a6feba 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -155,7 +155,7 @@ private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
 
 		$startLine = $tokens->currentTokenLine();
 		$startIndex = $tokens->currentTokenIndex();
-		$text = $this->parsePhpDocTextNode($tokens);
+		$text = $this->parseText($tokens);
 
 		return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex);
 	}
@@ -313,42 +313,6 @@ private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens)
 	}
 
 
-	private function parsePhpDocTextNode(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode
-	{
-		$text = '';
-
-		$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
-
-		// if the next token is EOL, everything below is skipped and empty string is returned
-		while (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
-			$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
-			$text .= $tmpText;
-
-			// stop if we're not at EOL - meaning it's the end of PHPDoc
-			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC)) {
-				break;
-			}
-
-			$tokens->pushSavePoint();
-			$tokens->next();
-
-			// if we're at EOL, check what's next
-			// if next is a PHPDoc tag, EOL, or end of PHPDoc, stop
-			if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) {
-				$tokens->rollback();
-				break;
-			}
-
-			// otherwise if the next is text, continue building the description string
-
-			$tokens->dropSavePoint();
-			$text .= $tokens->getDetectedNewline() ?? "\n";
-		}
-
-		return new Ast\PhpDoc\PhpDocTextNode(trim($text, " \t"));
-	}
-
-
 	public function parseTag(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagNode
 	{
 		$tag = $tokens->currentTokenValue();
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index cd680543..d352da4b 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -3968,8 +3968,8 @@ public function provideMultiLinePhpDocData(): iterable
 			' *' . PHP_EOL .
 			' */',
 			new PhpDocNode([
-				new PhpDocTextNode(''),
 				new PhpDocTextNode(
+					PHP_EOL .
 					'MultiLine' . PHP_EOL .
 					'description',
 				),
@@ -5073,13 +5073,13 @@ public function providerDebug(): Iterator
 			'OK class line',
 			$sample,
 			new PhpDocNode([
-				new PhpDocTextNode('Returns the schema for the field.'),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('This method is static because the field schema information is needed on
+				new PhpDocTextNode('Returns the schema for the field.
+
+This method is static because the field schema information is needed on
 creation of the field. FieldItemInterface objects instantiated at that
-time are not reliable as field settings might be missing.'),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('Computed fields having no schema should return an empty array.'),
+time are not reliable as field settings might be missing.
+
+Computed fields having no schema should return an empty array.'),
 			]),
 		];
 	}
@@ -5127,13 +5127,13 @@ public function provideRealWorldExampleData(): Iterator
 			'OK FieldItemInterface::schema',
 			$sample,
 			new PhpDocNode([
-				new PhpDocTextNode('Returns the schema for the field.'),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('This method is static because the field schema information is needed on
+				new PhpDocTextNode('Returns the schema for the field.
+
+This method is static because the field schema information is needed on
 creation of the field. FieldItemInterface objects instantiated at that
-time are not reliable as field settings might be missing.'),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('Computed fields having no schema should return an empty array.'),
+time are not reliable as field settings might be missing.
+
+Computed fields having no schema should return an empty array.'),
 				new PhpDocTextNode(''),
 				new PhpDocTagNode(
 					'@param',
@@ -5201,9 +5201,9 @@ public function provideRealWorldExampleData(): Iterator
 			'OK AbstractChunkedController::parseChunkedRequest',
 			$sample,
 			new PhpDocNode([
-				new PhpDocTextNode('Parses a chunked request and return relevant information.'),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode('This function must return an array containing the following
+				new PhpDocTextNode('Parses a chunked request and return relevant information.
+
+ This function must return an array containing the following
  keys and their corresponding values:
    - last: Wheter this is the last chunk of the uploaded file
    - uuid: A unique id which distinguishes two uploaded files
@@ -5247,9 +5247,9 @@ public function provideRealWorldExampleData(): Iterator
 			  * </code>
 			  */",
 			new PhpDocNode([
-				new PhpDocTextNode('Finder allows searching through directory trees using iterator.'),
-				new PhpDocTextNode(''),
-				new PhpDocTextNode("<code>
+				new PhpDocTextNode("Finder allows searching through directory trees using iterator.
+
+<code>
 Finder::findFiles('*.php')
     ->size('> 10kB')
     ->from('.')
@@ -7563,8 +7563,8 @@ public function dataTextBetweenTagsBelongsToDescription(): iterable
 			' *' . PHP_EOL .
 			' */',
 			new PhpDocNode([
-				new PhpDocTextNode(''),
 				new PhpDocTextNode(
+					PHP_EOL .
 					'MultiLine' . PHP_EOL .
 					'description',
 				),

From b78c136dfac6522ec816b40da2c4c75188b7e27a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 16 Sep 2024 09:19:36 +0200
Subject: [PATCH 27/45] Test 2.0-ready Slevomat CS branch

---
 .github/workflows/test-slevomat-coding-standard.yml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.github/workflows/test-slevomat-coding-standard.yml b/.github/workflows/test-slevomat-coding-standard.yml
index 389b4581..e81c206f 100644
--- a/.github/workflows/test-slevomat-coding-standard.yml
+++ b/.github/workflows/test-slevomat-coding-standard.yml
@@ -17,7 +17,6 @@ jobs:
       fail-fast: false
       matrix:
         php-version:
-          - "7.3"
           - "7.4"
           - "8.0"
           - "8.1"
@@ -34,7 +33,7 @@ jobs:
         with:
           repository: slevomat/coding-standard
           path: slevomat-cs
-          ref: ad4eab6f2cf9aa099c8853e4e3a250a9ae5fb4bd
+          ref: phpdoc-parser-2
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"

From e104033df04ecfdf99e1923d13a1478143bac5a1 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 26 Sep 2024 09:29:34 +0200
Subject: [PATCH 28/45] Fix PHPStan failures

---
 phpstan.neon                                           |  3 +++
 src/Ast/NodeVisitor/CloningVisitor.php                 |  2 +-
 src/Parser/PhpDocParser.php                            |  3 +--
 src/Printer/Printer.php                                | 10 +++++++++-
 .../Printer/IntegrationPrinterWithPhpParserTest.php    |  5 ++++-
 5 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/phpstan.neon b/phpstan.neon
index 58ab0c1a..92ffe420 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -11,3 +11,6 @@ parameters:
 	level: 8
 	ignoreErrors:
 		- '#^Dynamic call to static method PHPUnit\\Framework\\Assert#'
+		-
+			identifier: return.unusedType
+			path: tests/*
diff --git a/src/Ast/NodeVisitor/CloningVisitor.php b/src/Ast/NodeVisitor/CloningVisitor.php
index 7200f3af..486e2aae 100644
--- a/src/Ast/NodeVisitor/CloningVisitor.php
+++ b/src/Ast/NodeVisitor/CloningVisitor.php
@@ -9,7 +9,7 @@
 final class CloningVisitor extends AbstractNodeVisitor
 {
 
-	public function enterNode(Node $originalNode)
+	public function enterNode(Node $originalNode): Node
 	{
 		$node = clone $originalNode;
 		$node->setAttribute(Attribute::ORIGINAL_NODE, $originalNode);
diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index abfb2a75..ac717b95 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -13,7 +13,6 @@
 use PHPStan\PhpDocParser\ParserConfig;
 use PHPStan\ShouldNotHappenException;
 use function array_key_exists;
-use function array_values;
 use function count;
 use function rtrim;
 use function str_replace;
@@ -120,7 +119,7 @@ public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
 			return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0);
 		}
 
-		return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode(array_values($children)), 1, 0);
+		return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode($children), 1, 0);
 	}
 
 
diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php
index a6c9c388..8febe826 100644
--- a/src/Printer/Printer.php
+++ b/src/Printer/Printer.php
@@ -554,7 +554,11 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 				if (!$newNode instanceof Node || !$originalNode instanceof Node) {
 					return null;
 				}
+
+				/** @var int $itemStartPos */
 				$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
+
+				/** @var int $itemEndPos */
 				$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
 				if ($itemStartPos < 0 || $itemEndPos < 0 || $itemStartPos < $tokenIndex) {
 					throw new LogicException();
@@ -617,6 +621,7 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 					continue;
 				}
 
+				/** @var int $itemEndPos */
 				$itemEndPos = $tokenIndex - 1;
 				if ($insertNewline) {
 					$result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
@@ -642,7 +647,10 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 					return null;
 				}
 
+				/** @var int $itemStartPos */
 				$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
+
+				/** @var int $itemEndPos */
 				$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
 				if ($itemStartPos < 0 || $itemEndPos < 0) {
 					throw new LogicException();
@@ -702,7 +710,7 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 	}
 
 	/**
-	 * @param Node[] $nodes
+	 * @param array<Node|null> $nodes
 	 * @return array{bool, string, string}
 	 */
 	private function isMultiline(int $initialIndex, array $nodes, TokenIterator $originalTokens): array
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index 6d5b02aa..232bd145 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -2,6 +2,7 @@
 
 namespace PHPStan\PhpDocParser\Printer;
 
+use LogicException;
 use PhpParser\Comment\Doc;
 use PhpParser\Node as PhpNode;
 use PhpParser\NodeTraverser as PhpParserNodeTraverser;
@@ -78,8 +79,10 @@ public function testPrint(string $file, string $expectedFile, NodeVisitor $visit
 			$this->fail('Could not read ' . $file);
 		}
 
-		/** @var PhpNode[] $oldStmts */
 		$oldStmts = $phpParser->parse($fileContents);
+		if ($oldStmts === null) {
+			throw new LogicException();
+		}
 		$oldTokens = $phpParser->getTokens();
 
 		$phpTraverser2 = new PhpParserNodeTraverser();

From 52603177a27455823e3baf79fe69371bb1fc5021 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 8 Oct 2024 09:07:16 +0200
Subject: [PATCH 29/45] Send PR to 2.0.x

---
 .github/workflows/send-pr.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/send-pr.yml b/.github/workflows/send-pr.yml
index ab26eb3e..8f389817 100644
--- a/.github/workflows/send-pr.yml
+++ b/.github/workflows/send-pr.yml
@@ -23,7 +23,7 @@ jobs:
           repository: phpstan/phpstan-src
           path: phpstan-src
           token: ${{ secrets.PHPSTAN_BOT_TOKEN }}
-          ref: 1.12.x
+          ref: 2.0.x
 
       - name: "Install dependencies"
         working-directory: ./phpstan-src

From a131a159f27f8d8ff4d79577050370feff93e582 Mon Sep 17 00:00:00 2001
From: Markus Staab <markus.staab@redaxo.de>
Date: Wed, 9 Oct 2024 10:53:42 +0200
Subject: [PATCH 30/45] Fix IntegrationPrinterWithPhpParserTest

* Fix IntegrationPrinterWithPhpParserTest

* rais min php-parser version

* Update PhpPrinterIndentationDetectorVisitor.php

* Update phpstan-baseline.neon

* manual cs fix
---
 composer.json                                 |  2 +-
 phpstan-baseline.neon                         | 15 +++--
 .../IntegrationPrinterWithPhpParserTest.php   | 12 +++-
 tests/PHPStan/Printer/PhpPrinter.php          | 58 -------------------
 .../PhpPrinterIndentationDetectorVisitor.php  |  4 +-
 5 files changed, 24 insertions(+), 67 deletions(-)
 delete mode 100644 tests/PHPStan/Printer/PhpPrinter.php

diff --git a/composer.json b/composer.json
index bf3e95ec..8047c49e 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
 	},
 	"require-dev": {
 		"doctrine/annotations": "^2.0",
-		"nikic/php-parser": "^5.1",
+		"nikic/php-parser": "^5.3.0",
 		"php-parallel-lint/php-parallel-lint": "^1.2",
 		"phpstan/extension-installer": "^1.0",
 		"phpstan/phpstan": "^2.0",
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index d772636c..3bc8ae89 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,26 +1,31 @@
 parameters:
 	ignoreErrors:
 		-
-			message: "#^Method PHPStan\\\\PhpDocParser\\\\Ast\\\\ConstExpr\\\\ConstExprStringNode\\:\\:escapeDoubleQuotedString\\(\\) should return string but returns string\\|null\\.$#"
+			message: '#^Method PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode\:\:escapeDoubleQuotedString\(\) should return string but returns string\|null\.$#'
+			identifier: return.type
 			count: 1
 			path: src/Ast/ConstExpr/ConstExprStringNode.php
 
 		-
-			message: "#^Cannot use array destructuring on array\\<int, array\\<PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\>\\|int\\|string\\>\\|null\\.$#"
+			message: '#^Cannot use array destructuring on list\<array\<PHPStan\\PhpDocParser\\Ast\\Node\>\|int\|string\>\|null\.$#'
+			identifier: offsetAccess.nonArray
 			count: 1
 			path: src/Ast/NodeTraverser.php
 
 		-
-			message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#"
+			message: '#^Variable property access on PHPStan\\PhpDocParser\\Ast\\Node\.$#'
+			identifier: property.dynamicName
 			count: 1
 			path: src/Ast/NodeTraverser.php
 
 		-
-			message: "#^Method PHPStan\\\\PhpDocParser\\\\Parser\\\\StringUnescaper\\:\\:parseEscapeSequences\\(\\) should return string but returns string\\|null\\.$#"
+			message: '#^Method PHPStan\\PhpDocParser\\Parser\\StringUnescaper\:\:parseEscapeSequences\(\) should return string but returns string\|null\.$#'
+			identifier: return.type
 			count: 1
 			path: src/Parser/StringUnescaper.php
 
 		-
-			message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#"
+			message: '#^Variable property access on PHPStan\\PhpDocParser\\Ast\\Node\.$#'
+			identifier: property.dynamicName
 			count: 2
 			path: src/Printer/Printer.php
diff --git a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
index 232bd145..045156e8 100644
--- a/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
+++ b/tests/PHPStan/Printer/IntegrationPrinterWithPhpParserTest.php
@@ -4,11 +4,13 @@
 
 use LogicException;
 use PhpParser\Comment\Doc;
+use PhpParser\Internal\TokenStream;
 use PhpParser\Node as PhpNode;
 use PhpParser\NodeTraverser as PhpParserNodeTraverser;
 use PhpParser\NodeVisitor\CloningVisitor as PhpParserCloningVisitor;
 use PhpParser\NodeVisitorAbstract;
 use PhpParser\ParserFactory;
+use PhpParser\PrettyPrinter\Standard;
 use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
 use PHPStan\PhpDocParser\Ast\Node;
 use PHPStan\PhpDocParser\Ast\NodeTraverser;
@@ -25,10 +27,13 @@
 use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
 use function file_get_contents;
+use function str_repeat;
 
 class IntegrationPrinterWithPhpParserTest extends TestCase
 {
 
+	private const TAB_WIDTH = 4;
+
 	/**
 	 * @return iterable<array{string, string, NodeVisitor}>
 	 */
@@ -73,7 +78,6 @@ public function testPrint(string $file, string $expectedFile, NodeVisitor $visit
 		$phpTraverser = new PhpParserNodeTraverser();
 		$phpTraverser->addVisitor(new PhpParserCloningVisitor());
 
-		$printer = new PhpPrinter();
 		$fileContents = file_get_contents($file);
 		if ($fileContents === false) {
 			$this->fail('Could not read ' . $file);
@@ -85,6 +89,11 @@ public function testPrint(string $file, string $expectedFile, NodeVisitor $visit
 		}
 		$oldTokens = $phpParser->getTokens();
 
+		$phpTraverserIndent = new PhpParserNodeTraverser();
+		$indentDetector = new PhpPrinterIndentationDetectorVisitor(new TokenStream($oldTokens, self::TAB_WIDTH));
+		$phpTraverserIndent->addVisitor($indentDetector);
+		$phpTraverserIndent->traverse($oldStmts);
+
 		$phpTraverser2 = new PhpParserNodeTraverser();
 		$phpTraverser2->addVisitor(new class ($visitor) extends NodeVisitorAbstract {
 
@@ -134,6 +143,7 @@ public function enterNode(PhpNode $phpNode)
 		$newStmts = $phpTraverser->traverse($oldStmts);
 		$newStmts = $phpTraverser2->traverse($newStmts);
 
+		$printer = new Standard(['indent' => str_repeat($indentDetector->indentCharacter, $indentDetector->indentSize)]);
 		$newCode = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
 		$this->assertStringEqualsFile($expectedFile, $newCode);
 	}
diff --git a/tests/PHPStan/Printer/PhpPrinter.php b/tests/PHPStan/Printer/PhpPrinter.php
deleted file mode 100644
index e904313d..00000000
--- a/tests/PHPStan/Printer/PhpPrinter.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\PhpDocParser\Printer;
-
-use PhpParser\NodeTraverser;
-use PhpParser\PrettyPrinter\Standard;
-use function str_repeat;
-
-class PhpPrinter extends Standard
-{
-
-	private string $indentCharacter = ' ';
-
-	private int $indentSize = 4;
-
-	protected function resetState(): void
-	{
-		parent::resetState();
-		$this->indentCharacter = ' ';
-		$this->indentSize = 4;
-	}
-
-	protected function preprocessNodes(array $nodes): void
-	{
-		parent::preprocessNodes($nodes);
-		if ($this->origTokens === null) {
-			return;
-		}
-
-		$traverser = new NodeTraverser();
-
-		$visitor = new PhpPrinterIndentationDetectorVisitor($this->origTokens);
-		$traverser->addVisitor($visitor);
-		$traverser->traverse($nodes);
-
-		$this->indentCharacter = $visitor->indentCharacter;
-		$this->indentSize = $visitor->indentSize;
-	}
-
-	protected function setIndentLevel(int $level): void
-	{
-		$this->indentLevel = $level;
-		$this->nl = "\n" . str_repeat($this->indentCharacter, $level);
-	}
-
-	protected function indent(): void
-	{
-		$this->indentLevel += $this->indentSize;
-		$this->nl = "\n" . str_repeat($this->indentCharacter, $this->indentLevel);
-	}
-
-	protected function outdent(): void
-	{
-		$this->indentLevel -= $this->indentSize;
-		$this->nl = "\n" . str_repeat($this->indentCharacter, $this->indentLevel);
-	}
-
-}
diff --git a/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php b/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php
index d0da8f46..49987eb3 100644
--- a/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php
+++ b/tests/PHPStan/Printer/PhpPrinterIndentationDetectorVisitor.php
@@ -4,7 +4,7 @@
 
 use PhpParser\Internal\TokenStream;
 use PhpParser\Node;
-use PhpParser\NodeTraverser;
+use PhpParser\NodeVisitor;
 use PhpParser\NodeVisitorAbstract;
 use function count;
 use function preg_match;
@@ -71,7 +71,7 @@ public function enterNode(Node $node)
 			$this->indentCharacter = $char;
 			$this->indentSize = $size;
 
-			return NodeTraverser::STOP_TRAVERSAL;
+			return NodeVisitor::STOP_TRAVERSAL;
 		}
 
 		return null;

From 04c8de0d350731bccfa216a5b5e9b3e1c26e4e24 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 7 Nov 2024 11:15:44 +0100
Subject: [PATCH 31/45] Slevomat CS merged PR about 2.0

---
 .github/workflows/test-slevomat-coding-standard.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/test-slevomat-coding-standard.yml b/.github/workflows/test-slevomat-coding-standard.yml
index e81c206f..be6353b9 100644
--- a/.github/workflows/test-slevomat-coding-standard.yml
+++ b/.github/workflows/test-slevomat-coding-standard.yml
@@ -33,7 +33,7 @@ jobs:
         with:
           repository: slevomat/coding-standard
           path: slevomat-cs
-          ref: phpdoc-parser-2
+          ref: 88602f9ae5450a933133108d052eeb2edee0a4b7
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"

From 7adaab73e4dd25461b8195dae8360b3ee8648cec Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 15 Jan 2025 09:22:47 +0100
Subject: [PATCH 32/45] Test inline `@link`

---
 tests/PHPStan/Parser/PhpDocParserTest.php | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 7626580e..8e98d639 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -122,6 +122,7 @@ protected function setUp(): void
 	 * @dataProvider provideDoctrineData
 	 * @dataProvider provideDoctrineWithoutDoctrineCheckData
 	 * @dataProvider provideCommentLikeDescriptions
+	 * @dataProvider provideInlineTags
 	 */
 	public function testParse(
 		string $label,
@@ -6024,6 +6025,27 @@ public function provideCommentLikeDescriptions(): Iterator
 		];
 	}
 
+	public function provideInlineTags(): Iterator
+	{
+		yield [
+			'Inline @link tag in @copyright',
+			'/**' . PHP_EOL .
+			' * Unit tests for stored_progress_bar_cleanup' . PHP_EOL .
+			' *' . PHP_EOL .
+			' * @package   core' . PHP_EOL .
+			' * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}' . PHP_EOL .
+			' * @\ORM\Entity() 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTextNode('Unit tests for stored_progress_bar_cleanup'),
+				new PhpDocTextNode(''),
+				new PhpDocTagNode('@package', new GenericTagValueNode('core')),
+				new PhpDocTagNode('@copyright', new GenericTagValueNode('2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}')),
+				new PhpDocTagNode('@\ORM\Entity', new DoctrineTagValueNode(new DoctrineAnnotation('@\ORM\Entity', []), '2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}')),
+			]),
+		];
+	}
+
 	public function provideParamOutTagsData(): Iterator
 	{
 		yield [

From 81de606627b438a92020bc3465beaeaf6abac179 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Tue, 28 Jan 2025 10:25:56 +0100
Subject: [PATCH 33/45] Update LICENSE

---
 LICENSE | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/LICENSE b/LICENSE
index 98a854e4..e5f34e60 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
 MIT License
 
 Copyright (c) 2016 Ondřej Mirtes
+Copyright (c) 2025 PHPStan s.r.o.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+SOFTWARE.

From 72e51f7c32c5aef7c8b462195b8c599b11199893 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ismail=20=C3=96zg=C3=BCn=20Turan?=
 <33699830+DaDeather@users.noreply.github.com>
Date: Thu, 13 Feb 2025 13:25:43 +0100
Subject: [PATCH 34/45] TypeParser: Allow multiple newlines and also allow
 multiline union and intersection types for array shapes

---
 src/Parser/TokenIterator.php              |  13 ++
 src/Parser/TypeParser.php                 | 114 +++++++----
 tests/PHPStan/Parser/PhpDocParserTest.php | 224 ++++++++++++++++++++++
 3 files changed, 309 insertions(+), 42 deletions(-)

diff --git a/src/Parser/TokenIterator.php b/src/Parser/TokenIterator.php
index 87ded21a..a9738d62 100644
--- a/src/Parser/TokenIterator.php
+++ b/src/Parser/TokenIterator.php
@@ -205,6 +205,19 @@ public function tryConsumeTokenType(int $tokenType): bool
 	}
 
 
+	/** @phpstan-impure */
+	public function skipNewLineTokens(): void
+	{
+		if (!$this->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+			return;
+		}
+
+		do {
+			$foundNewLine = $this->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		} while ($foundNewLine === true);
+	}
+
+
 	private function detectNewline(): void
 	{
 		$value = $this->currentTokenValue();
diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index 84a3880d..d5f9217c 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -40,17 +40,44 @@ public function parse(TokenIterator $tokens): Ast\Type\TypeNode
 		} else {
 			$type = $this->parseAtomic($tokens);
 
-			if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
-				$type = $this->parseUnion($tokens, $type);
+			$tokens->pushSavePoint();
+			$tokens->skipNewLineTokens();
+
+			try {
+				$enrichedType = $this->enrichTypeOnUnionOrIntersection($tokens, $type);
+
+			} catch (ParserException $parserException) {
+				$enrichedType = null;
+			}
 
-			} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
-				$type = $this->parseIntersection($tokens, $type);
+			if ($enrichedType !== null) {
+				$type = $enrichedType;
+				$tokens->dropSavePoint();
+
+			} else {
+				$tokens->rollback();
+				$type = $this->enrichTypeOnUnionOrIntersection($tokens, $type) ?? $type;
 			}
 		}
 
 		return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
 	}
 
+	/** @phpstan-impure */
+	private function enrichTypeOnUnionOrIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): ?Ast\Type\TypeNode
+	{
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
+			return $this->parseUnion($tokens, $type);
+
+		}
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
+			return $this->parseIntersection($tokens, $type);
+		}
+
+		return null;
+	}
+
 	/**
 	 * @internal
 	 * @template T of Ast\Node
@@ -90,7 +117,7 @@ private function subParse(TokenIterator $tokens): Ast\Type\TypeNode
 			if ($tokens->isCurrentTokenValue('is')) {
 				$type = $this->parseConditional($tokens, $type);
 			} else {
-				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+				$tokens->skipNewLineTokens();
 
 				if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
 					$type = $this->subParseUnion($tokens, $type);
@@ -112,9 +139,9 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 		$startIndex = $tokens->currentTokenIndex();
 
 		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 			$type = $this->subParse($tokens);
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 
 			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
 
@@ -256,9 +283,9 @@ private function subParseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type):
 		$types = [$type];
 
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 			$types[] = $this->parseAtomic($tokens);
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 		}
 
 		return new Ast\Type\UnionTypeNode($types);
@@ -284,9 +311,9 @@ private function subParseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $
 		$types = [$type];
 
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 			$types[] = $this->parseAtomic($tokens);
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 		}
 
 		return new Ast\Type\IntersectionTypeNode($types);
@@ -306,15 +333,15 @@ private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subj
 
 		$targetType = $this->parse($tokens);
 
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 		$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$ifType = $this->parse($tokens);
 
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$elseType = $this->subParse($tokens);
 
@@ -335,15 +362,15 @@ private function parseConditionalForParameter(TokenIterator $tokens, string $par
 
 		$targetType = $this->parse($tokens);
 
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 		$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$ifType = $this->parse($tokens);
 
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$elseType = $this->subParse($tokens);
 
@@ -409,8 +436,11 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode
 		$variances = [];
 
 		$isFirst = true;
-		while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		while (
+			$isFirst
+			|| $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)
+		) {
+			$tokens->skipNewLineTokens();
 
 			// trailing comma case
 			if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
@@ -419,7 +449,7 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode
 			$isFirst = false;
 
 			[$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens);
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 		}
 
 		$type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances);
@@ -510,19 +540,19 @@ private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNod
 			: [];
 
 		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$parameters = [];
 		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
 			$parameters[] = $this->parseCallableParameter($tokens);
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 			while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
-				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+				$tokens->skipNewLineTokens();
 				if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
 					break;
 				}
 				$parameters[] = $this->parseCallableParameter($tokens);
-				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+				$tokens->skipNewLineTokens();
 			}
 		}
 
@@ -550,7 +580,7 @@ private function parseCallableTemplates(TokenIterator $tokens): array
 
 		$isFirst = true;
 		while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 
 			// trailing comma case
 			if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
@@ -559,7 +589,7 @@ private function parseCallableTemplates(TokenIterator $tokens): array
 			$isFirst = false;
 
 			$templates[] = $this->parseCallableTemplateArgument($tokens);
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 		}
 
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
@@ -830,7 +860,7 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 		$unsealedType = null;
 
 		do {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
 				return Ast\Type\ArrayShapeNode::createSealed($items, $kind);
@@ -839,14 +869,14 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC)) {
 				$sealed = false;
 
-				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+				$tokens->skipNewLineTokens();
 				if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
 					if ($kind === Ast\Type\ArrayShapeNode::KIND_ARRAY) {
 						$unsealedType = $this->parseArrayShapeUnsealedType($tokens);
 					} else {
 						$unsealedType = $this->parseListShapeUnsealedType($tokens);
 					}
-					$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+					$tokens->skipNewLineTokens();
 				}
 
 				$tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA);
@@ -855,10 +885,10 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 
 			$items[] = $this->parseArrayShapeItem($tokens);
 
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 		} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
 
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
 
 		if ($sealed) {
@@ -945,18 +975,18 @@ private function parseArrayShapeUnsealedType(TokenIterator $tokens): Ast\Type\Ar
 		$startIndex = $tokens->currentTokenIndex();
 
 		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$valueType = $this->parse($tokens);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$keyType = null;
 		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 
 			$keyType = $valueType;
 			$valueType = $this->parse($tokens);
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 		}
 
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
@@ -978,10 +1008,10 @@ private function parseListShapeUnsealedType(TokenIterator $tokens): Ast\Type\Arr
 		$startIndex = $tokens->currentTokenIndex();
 
 		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$valueType = $this->parse($tokens);
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
 
@@ -1003,7 +1033,7 @@ private function parseObjectShape(TokenIterator $tokens): Ast\Type\ObjectShapeNo
 		$items = [];
 
 		do {
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
 				return new Ast\Type\ObjectShapeNode($items);
@@ -1011,10 +1041,10 @@ private function parseObjectShape(TokenIterator $tokens): Ast\Type\ObjectShapeNo
 
 			$items[] = $this->parseObjectShapeItem($tokens);
 
-			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$tokens->skipNewLineTokens();
 		} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
 
-		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->skipNewLineTokens();
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
 
 		return new Ast\Type\ObjectShapeNode($items);
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 8e98d639..2cbde3c6 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -62,6 +62,8 @@
 use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode;
+use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode;
 use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
 use PHPStan\PhpDocParser\Lexer\Lexer;
@@ -4027,6 +4029,228 @@ public function provideMultiLinePhpDocData(): iterable
 				new PhpDocTextNode(''),
 			]),
 		];
+
+		yield [
+			'Multiline PHPDoc with new line across generic type declaration',
+			'/**' . PHP_EOL .
+			' * @param array<string, array{' . PHP_EOL .
+			' *     foo: int,' . PHP_EOL .
+			' *     bar?: array<string,' . PHP_EOL .
+			' *         array{foo1: int, bar1?: true}' . PHP_EOL .
+			' *         | array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
+			' *     >,' . PHP_EOL .
+			' * }> $a' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new ParamTagValueNode(
+					new GenericTypeNode(
+						new IdentifierTypeNode('array'),
+						[
+							new IdentifierTypeNode('string'),
+							ArrayShapeNode::createSealed([
+								new ArrayShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
+								new ArrayShapeItemNode(new IdentifierTypeNode('bar'), true, new GenericTypeNode(
+									new IdentifierTypeNode('array'),
+									[
+										new IdentifierTypeNode('string'),
+										new UnionTypeNode([
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('true')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+										]),
+									],
+									[
+										GenericTypeNode::VARIANCE_INVARIANT,
+										GenericTypeNode::VARIANCE_INVARIANT,
+									],
+								)),
+							]),
+						],
+						[
+							GenericTypeNode::VARIANCE_INVARIANT,
+							GenericTypeNode::VARIANCE_INVARIANT,
+						],
+					),
+					false,
+					'$a',
+					'',
+					false,
+				)),
+			]),
+		];
+
+		yield [
+			'Multiline PHPDoc with new line within type declaration',
+			'/**' . PHP_EOL .
+			' * @param array<string, array{' . PHP_EOL .
+			' *     foo: int,' . PHP_EOL .
+			' * ' . PHP_EOL .
+			' * ' . PHP_EOL .
+			' *     bar?: array<string,' . PHP_EOL .
+			' * ' . PHP_EOL .
+			' *         array{foo1: int, bar1?: true}' . PHP_EOL .
+			' * ' . PHP_EOL .
+			' *         | array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
+			' *     >,' . PHP_EOL .
+			' * }> $a' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new ParamTagValueNode(
+					new GenericTypeNode(
+						new IdentifierTypeNode('array'),
+						[
+							new IdentifierTypeNode('string'),
+							ArrayShapeNode::createSealed([
+								new ArrayShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
+								new ArrayShapeItemNode(new IdentifierTypeNode('bar'), true, new GenericTypeNode(
+									new IdentifierTypeNode('array'),
+									[
+										new IdentifierTypeNode('string'),
+										new UnionTypeNode([
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('true')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+										]),
+									],
+									[
+										GenericTypeNode::VARIANCE_INVARIANT,
+										GenericTypeNode::VARIANCE_INVARIANT,
+									],
+								)),
+							]),
+						],
+						[
+							GenericTypeNode::VARIANCE_INVARIANT,
+							GenericTypeNode::VARIANCE_INVARIANT,
+						],
+					),
+					false,
+					'$a',
+					'',
+					false,
+				)),
+			]),
+		];
+
+		yield [
+			'Multiline PHPDoc with new line within type declaration including usage of braces',
+			'/**' . PHP_EOL .
+			' * @phpstan-type FactoriesConfigurationType = array<' . PHP_EOL .
+			' *      string,' . PHP_EOL .
+			' *      (class-string<Factory\FactoryInterface>|Factory\FactoryInterface)' . PHP_EOL .
+			' *      |callable(ContainerInterface,?string,array<mixed>|null):object' . PHP_EOL .
+			' * >' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTagNode('@phpstan-type', new TypeAliasTagValueNode(
+					'FactoriesConfigurationType',
+					new GenericTypeNode(
+						new IdentifierTypeNode('array'),
+						[
+							new IdentifierTypeNode('string'),
+							new UnionTypeNode([
+								new UnionTypeNode([
+									new GenericTypeNode(
+										new IdentifierTypeNode('class-string'),
+										[new IdentifierTypeNode('Factory\\FactoryInterface')],
+										[GenericTypeNode::VARIANCE_INVARIANT],
+									),
+									new IdentifierTypeNode('Factory\\FactoryInterface'),
+								]),
+								new CallableTypeNode(
+									new IdentifierTypeNode('callable'),
+									[
+										new CallableTypeParameterNode(new IdentifierTypeNode('ContainerInterface'), false, false, '', false),
+										new CallableTypeParameterNode(
+											new NullableTypeNode(
+												new IdentifierTypeNode('string'),
+											),
+											false,
+											false,
+											'',
+											false,
+										),
+										new CallableTypeParameterNode(
+											new UnionTypeNode([
+												new GenericTypeNode(
+													new IdentifierTypeNode('array'),
+													[new IdentifierTypeNode('mixed')],
+													[GenericTypeNode::VARIANCE_INVARIANT],
+												),
+												new IdentifierTypeNode('null'),
+											]),
+											false,
+											false,
+											'',
+											false,
+										),
+									],
+									new IdentifierTypeNode('object'),
+									[],
+								),
+							]),
+						],
+						[
+							GenericTypeNode::VARIANCE_INVARIANT,
+							GenericTypeNode::VARIANCE_INVARIANT,
+						],
+					),
+				)),
+			]),
+		];
+
+		/**
+		 * @return object{
+		 *   a: int,
+		 *
+		 *   b: int,
+		 * }
+		 */
+
+		yield [
+			'Multiline PHPDoc with new line within object type declaration',
+			'/**' . PHP_EOL .
+			' * @return object{' . PHP_EOL .
+			' *   a: int,' . PHP_EOL .
+			' *' . PHP_EOL .
+			' *   b: int,' . PHP_EOL .
+			' * }' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@return',
+					new ReturnTagValueNode(
+						new ObjectShapeNode(
+							[
+								new ObjectShapeItemNode(
+									new IdentifierTypeNode('a'),
+									false,
+									new IdentifierTypeNode('int'),
+								),
+								new ObjectShapeItemNode(
+									new IdentifierTypeNode('b'),
+									false,
+									new IdentifierTypeNode('int'),
+								),
+							],
+						),
+						'',
+					),
+				),
+			]),
+		];
 	}
 
 	public function provideTemplateTagsData(): Iterator

From 19ecfcb76d89197f41b71c3b07de8784f121e108 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 13 Feb 2025 13:27:46 +0100
Subject: [PATCH 35/45] Send PR to 2.1.x

---
 .github/workflows/send-pr.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/send-pr.yml b/.github/workflows/send-pr.yml
index 8f389817..2503256e 100644
--- a/.github/workflows/send-pr.yml
+++ b/.github/workflows/send-pr.yml
@@ -23,7 +23,7 @@ jobs:
           repository: phpstan/phpstan-src
           path: phpstan-src
           token: ${{ secrets.PHPSTAN_BOT_TOKEN }}
-          ref: 2.0.x
+          ref: 2.1.x
 
       - name: "Install dependencies"
         working-directory: ./phpstan-src

From 5404716a001cfbf51756eacab507cab73e73553b Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 15 Feb 2025 21:51:37 +0100
Subject: [PATCH 36/45] Prepare 2.1.x

---
 .github/workflows/apiref.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/apiref.yml b/.github/workflows/apiref.yml
index 530d3fb4..27d3fb92 100644
--- a/.github/workflows/apiref.yml
+++ b/.github/workflows/apiref.yml
@@ -5,7 +5,7 @@ name: "Deploy API Reference"
 on:
   push:
     branches:
-      - "2.0.x"
+      - "2.1.x"
 
 concurrency:
   group: "pages"
@@ -21,6 +21,7 @@ jobs:
         branch:
           - "1.23.x"
           - "2.0.x"
+          - "2.1.x"
 
     steps:
       - name: "Checkout"

From 19d74a8f1a2cb0ac8e843a299e2622f1258651db Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 15 Feb 2025 21:51:03 +0100
Subject: [PATCH 37/45] Open 2.1.x

---
 .github/workflows/backward-compatibility.yml        | 2 +-
 .github/workflows/build.yml                         | 2 +-
 .github/workflows/test-slevomat-coding-standard.yml | 2 +-
 README.md                                           | 4 ++--
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/backward-compatibility.yml b/.github/workflows/backward-compatibility.yml
index 49af305c..ecfb7fbd 100644
--- a/.github/workflows/backward-compatibility.yml
+++ b/.github/workflows/backward-compatibility.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "2.0.x"
+      - "2.1.x"
 
 jobs:
   backward-compatibility:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0c05365c..9118fafd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "2.0.x"
+      - "2.1.x"
 
 jobs:
   lint:
diff --git a/.github/workflows/test-slevomat-coding-standard.yml b/.github/workflows/test-slevomat-coding-standard.yml
index be6353b9..8ce55469 100644
--- a/.github/workflows/test-slevomat-coding-standard.yml
+++ b/.github/workflows/test-slevomat-coding-standard.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "2.0.x"
+      - "2.1.x"
 
 jobs:
   tests:
diff --git a/README.md b/README.md
index ff6e808e..2dae7b2d 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,9 @@ For the complete list of supported PHPDoc features check out PHPStan documentati
 
 * [PHPDoc Basics](https://phpstan.org/writing-php-code/phpdocs-basics) (list of PHPDoc tags)
 * [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) (list of PHPDoc types)
-* [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/2.0.x/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc.
+* [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/2.1.x/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc.
 
-This parser also supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/2.0.x/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html).
+This parser also supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/2.1.x/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html).
 
 ## Installation
 

From dc7c4699f97407ab8cc99413cf32670d2f33a992 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 15 Feb 2025 21:50:02 +0100
Subject: [PATCH 38/45] PrinterTest - nowdoc helper for nicer formatting

---
 tests/PHPStan/Printer/PrinterTest.php | 885 +++++++++++++++-----------
 1 file changed, 517 insertions(+), 368 deletions(-)

diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 7c0e7a9b..93e78bcb 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -50,12 +50,21 @@
 use PHPStan\PhpDocParser\Parser\TypeParser;
 use PHPStan\PhpDocParser\ParserConfig;
 use PHPUnit\Framework\TestCase;
+use function array_map;
 use function array_pop;
+use function array_slice;
 use function array_splice;
 use function array_unshift;
 use function array_values;
 use function assert;
 use function count;
+use function implode;
+use function preg_match;
+use function preg_replace_callback;
+use function preg_split;
+use function str_repeat;
+use function str_replace;
+use function strlen;
 use const PHP_EOL;
 
 class PrinterTest extends TestCase
@@ -96,12 +105,14 @@ public function dataPrintFormatPreserving(): iterable
 			$noopVisitor,
 		];
 		yield [
-			'/**
- * @param Foo $foo
- */',
-			'/**
- * @param Foo $foo
- */',
+			self::nowdoc('
+				/**
+				 * @param Foo $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param Foo $foo
+				 */'),
 			$noopVisitor,
 		];
 
@@ -143,33 +154,39 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/**
- * @param Foo $foo
- */',
-			'/**
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 */'),
 			$removeFirst,
 		];
 
 		yield [
-			'/**
- * @param Foo $foo
- * @param Bar $bar
- */',
-			'/**
- * @param Bar $bar
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Bar $bar
+			 */'),
 			$removeFirst,
 		];
 
 		yield [
-			'/**
-     * @param Foo $foo
-     * @param Bar $bar
-     */',
-			'/**
-     * @param Bar $bar
-     */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Bar $bar
+			 */'),
 			$removeFirst,
 		];
 
@@ -189,13 +206,15 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Foo $foo
- * @param Bar $bar
- */',
-			'/**
- * @param Foo $foo
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
 			$removeLast,
 		];
 
@@ -216,39 +235,45 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Foo $foo
- * @param Bar $bar
- */',
-			'/**
- * @param Foo $foo
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
 			$removeSecond,
 		];
 
 		yield [
-			'/**
- * @param Foo $foo
- * @param Bar $bar
- * @param Baz $baz
- */',
-			'/**
- * @param Foo $foo
- * @param Baz $baz
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 * @param Baz $baz
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Baz $baz
+			 */'),
 			$removeSecond,
 		];
 
 		yield [
-			'/**
-     * @param Foo $foo
-     * @param Bar $bar
-     * @param Baz $baz
-     */',
-			'/**
-     * @param Foo $foo
-     * @param Baz $baz
-     */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 * @param Baz $baz
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Baz $baz
+			 */'),
 			$removeSecond,
 		];
 
@@ -280,58 +305,66 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/**
-* @return Foo
-* @param Foo $foo
-* @param Bar $bar
-*/',
-			'/**
-* @return Bar
-* @param Foo $foo
-* @param Bar $bar
-*/',
+			self::nowdoc('
+			/**
+			 * @return Foo
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return Bar
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
 			$changeReturnType,
 		];
 
 		yield [
-			'/**
-* @param Foo $foo
-* @return Foo
-* @param Bar $bar
-*/',
-			'/**
-* @param Foo $foo
-* @return Bar
-* @param Bar $bar
-*/',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @return Foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @return Bar
+			 * @param Bar $bar
+			 */'),
 			$changeReturnType,
 		];
 
 		yield [
-			'/**
-* @return Foo
-* @param Foo $foo
-* @param Bar $bar
-*/',
-			'/**
-* @return Bar
-* @param Foo $foo
-* @param Bar $bar
-*/',
+			self::nowdoc('
+			/**
+			 * @return Foo
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return Bar
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
 			$changeReturnType,
 		];
 
 		yield [
-			'/**
-* @param Foo $foo Foo description
-* @return Foo Foo return description
-* @param Bar $bar Bar description
-*/',
-			'/**
-* @param Foo $foo Foo description
-* @return Bar Foo return description
-* @param Bar $bar Bar description
-*/',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo Foo description
+			 * @return Foo Foo return description
+			 * @param Bar $bar Bar description
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo Foo description
+			 * @return Bar Foo return description
+			 * @param Bar $bar Bar description
+			 */'),
 			$changeReturnType,
 		];
 
@@ -356,22 +389,26 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/**
- * @param Foo $foo
- */',
-			'/**
- * @param Baz $a
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Baz $a
+			 */'),
 			$replaceFirst,
 		];
 
 		yield [
-			'/**
-     * @param Foo $foo
-     */',
-			'/**
-     * @param Baz $a
-     */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Baz $a
+			 */'),
 			$replaceFirst,
 		];
 
@@ -391,24 +428,28 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Foo $foo
- */',
-			'/**
- * @param Baz $a
- * @param Foo $foo
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Baz $a
+			 * @param Foo $foo
+			 */'),
 			$insertFirst,
 		];
 
 		yield [
-			'/**
-     * @param Foo $foo
-     */',
-			'/**
-     * @param Baz $a
-     * @param Foo $foo
-     */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Baz $a
+			 * @param Foo $foo
+			 */'),
 			$insertFirst,
 		];
 
@@ -430,52 +471,60 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Foo $foo
- */',
-			'/**
- * @param Foo $foo
- * @param Baz $a
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Baz $a
+			 */'),
 			$insertSecond,
 		];
 
 		yield [
-			'/**
- * @param Foo $foo
- * @param Bar $bar
- */',
-			'/**
- * @param Foo $foo
- * @param Baz $a
- * @param Bar $bar
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Baz $a
+			 * @param Bar $bar
+			 */'),
 			$insertSecond,
 		];
 
 		yield [
-			'/**
-     * @param Foo $foo
-     * @param Bar $bar
-     */',
-			'/**
-     * @param Foo $foo
-     * @param Baz $a
-     * @param Bar $bar
-     */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Baz $a
+			 * @param Bar $bar
+			 */'),
 			$insertSecond,
 		];
 
 		yield [
-			'/**
-	 * @param Foo $foo
-	 * @param Bar $bar
-	 */',
-			'/**
-	 * @param Foo $foo
-	 * @param Baz $a
-	 * @param Bar $bar
-	 */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Baz $a
+			 * @param Bar $bar
+			 */'),
 			$insertSecond,
 		];
 
@@ -494,24 +543,28 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Foo $foo
- */',
-			'/**
- * @param Baz $a
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Baz $a
+			 */'),
 			$replaceLast,
 		];
 
 		yield [
-			'/**
- * @param Foo $foo
- * @param Bar $bar
- */',
-			'/**
- * @param Foo $foo
- * @param Baz $a
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo $foo
+			 * @param Baz $a
+			 */'),
 			$replaceLast,
 		];
 
@@ -529,24 +582,28 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Bar|Baz $foo
- */',
-			'/**
- * @param Foo|Bar|Baz $foo
- */',
+			self::nowdoc('
+			/**
+			 * @param Bar|Baz $foo
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo|Bar|Baz $foo
+			 */'),
 			$insertFirstTypeInUnionType,
 		];
 
 		yield [
-			'/**
- * @param Bar|Baz $foo
- * @param Foo $bar
- */',
-			'/**
- * @param Foo|Bar|Baz $foo
- * @param Foo $bar
- */',
+			self::nowdoc('
+			/**
+			 * @param Bar|Baz $foo
+			 * @param Foo $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo|Bar|Baz $foo
+			 * @param Foo $bar
+			 */'),
 			$insertFirstTypeInUnionType,
 		];
 
@@ -567,12 +624,14 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Foo|Bar $bar
- */',
-			'/**
- * @param Lorem|Ipsum $bar
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo|Bar $bar
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Lorem|Ipsum $bar
+			 */'),
 			$replaceTypesInUnionType,
 		];
 
@@ -622,12 +681,14 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/**
- * @param callable(): void $cb
- */',
-			'/**
- * @param callable(Foo $foo, Bar $bar): void $cb
- */',
+			self::nowdoc('
+			/**
+			 * @param callable(): void $cb
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param callable(Foo $foo, Bar $bar): void $cb
+			 */'),
 			$replaceParametersInCallableType,
 		];
 
@@ -645,12 +706,14 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param callable(Foo $foo, Bar $bar): void $cb
- */',
-			'/**
- * @param callable(): void $cb
- */',
+			self::nowdoc('
+			/**
+			 * @param callable(Foo $foo, Bar $bar): void $cb
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param callable(): void $cb
+			 */'),
 			$removeParametersInCallableType,
 		];
 
@@ -668,14 +731,16 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param callable(Foo $foo, Bar $bar): void $cb
- * @param callable(): void $cb2
- */',
-			'/**
- * @param Closure(Foo $foo, Bar $bar): void $cb
- * @param Closure(): void $cb2
- */',
+			self::nowdoc('
+			/**
+			 * @param callable(Foo $foo, Bar $bar): void $cb
+			 * @param callable(): void $cb2
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Closure(Foo $foo, Bar $bar): void $cb
+			 * @param Closure(): void $cb2
+			 */'),
 			$changeCallableTypeIdentifier,
 		];
 
@@ -706,120 +771,134 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/**
- * @return array{float, Foo}
- */',
-			'/**
- * @return array{float, int, Foo, string}
- */',
+			self::nowdoc('
+			/**
+			 * @return array{float, Foo}
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{float, int, Foo, string}
+			 */'),
 			$addItemsToArrayShape,
 		];
 
 		yield [
-			'/**
- * @return array{
- *   float,
- *   Foo,
- * }
- */',
-			'/**
- * @return array{
- *   float,
- *   int,
- *   Foo,
- *   string,
- * }
- */',
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   Foo,
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   int,
+			 *   Foo,
+			 *   string,
+			 * }
+			 */'),
 			$addItemsToArrayShape,
 		];
 
 		yield [
-			'/**
- * @return array{
- *   float,
- *   Foo
- * }
- */',
-			'/**
- * @return array{
- *   float,
- *   int,
- *   Foo,
- *   string
- * }
- */',
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   Foo
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   int,
+			 *   Foo,
+			 *   string
+			 * }
+			 */'),
 			$addItemsToArrayShape,
 		];
 
 		yield [
-			'/**
- * @return array{
- *     float,
- *     Foo
- * }
- */',
-			'/**
- * @return array{
- *     float,
- *     int,
- *     Foo,
- *     string
- * }
- */',
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *     float,
+			 *     Foo
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *     float,
+			 *     int,
+			 *     Foo,
+			 *     string
+			 * }
+			 */'),
 			$addItemsToArrayShape,
 		];
 
 		yield [
-			'/**
-	 * @return array{
-	 *   float,
-	 *   Foo,
-	 * }
-	 */',
-			'/**
-	 * @return array{
-	 *   float,
-	 *   int,
-	 *   Foo,
-	 *   string,
-	 * }
-	 */',
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   Foo,
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   int,
+			 *   Foo,
+			 *   string,
+			 * }
+			 */'),
 			$addItemsToArrayShape,
 		];
 
 		yield [
-			'/**
-     * @return array{
-     *   float,
-     *   Foo
-     * }
-     */',
-			'/**
-     * @return array{
-     *   float,
-     *   int,
-     *   Foo,
-     *   string
-     * }
-     */',
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   Foo
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   float,
+			 *   int,
+			 *   Foo,
+			 *   string
+			 * }
+			 */'),
 			$addItemsToArrayShape,
 		];
 
 		yield [
-			'/**
-	 * @return array{
-	 *     float,
-	 *     Foo
-	 * }
-	 */',
-			'/**
-	 * @return array{
-	 *     float,
-	 *     int,
-	 *     Foo,
-	 *     string
-	 * }
-	 */',
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *     float,
+			 *     Foo
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *     float,
+			 *     int,
+			 *     Foo,
+			 *     string
+			 * }
+			 */'),
 			$addItemsToArrayShape,
 		];
 
@@ -837,22 +916,38 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @return object{}
- */',
-			'/**
- * @return object{foo: int}
- */',
+			self::nowdoc('
+				/**
+				 * @return object{}
+				 */'),
+			self::nowdoc('
+				/**
+				 * @return object{foo: int}
+				 */'),
 			$addItemsToObjectShape,
 		];
 
 		yield [
-			'/**
- * @return object{bar: string}
- */',
-			'/**
- * @return object{bar: string, foo: int}
- */',
+			self::nowdoc('
+				/**
+				 * @return object{bar: string}
+				 */'),
+			self::nowdoc('
+				/**
+				 * @return object{bar: string, foo: int}
+				 */'),
+			$addItemsToObjectShape,
+		];
+
+		yield [
+			self::nowdoc('
+			/**
+			 * @return object{bar: string}
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return object{bar: string, foo: int}
+			 */'),
 			$addItemsToObjectShape,
 		];
 
@@ -1096,36 +1191,42 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/**
- * @param int $a
- */',
-			'/**
- * @param int $bz
- */',
+			self::nowdoc('
+			/**
+			 * @param int $a
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param int $bz
+			 */'),
 			$changeParameterName,
 		];
 
 		yield [
-			'/**
- * @param int $a
- * @return string
- */',
-			'/**
- * @param int $bz
- * @return string
- */',
+			self::nowdoc('
+			/**
+			 * @param int $a
+			 * @return string
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param int $bz
+			 * @return string
+			 */'),
 			$changeParameterName,
 		];
 
 		yield [
-			'/**
- * @param int $a haha description
- * @return string
- */',
-			'/**
- * @param int $bz haha description
- * @return string
- */',
+			self::nowdoc('
+			/**
+			 * @param int $a haha description
+			 * @return string
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param int $bz haha description
+			 * @return string
+			 */'),
 			$changeParameterName,
 		];
 
@@ -1161,12 +1262,14 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/**
- * @param int $a haha
- */',
-			'/**
- * @param int $a hehe
- */',
+			self::nowdoc('
+			/**
+			 * @param int $a haha
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param int $a hehe
+			 */'),
 			$changeParameterDescription,
 		];
 
@@ -1184,12 +1287,14 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @param Foo[awesome] $a haha
- */',
-			'/**
- * @param Foo[baz] $a haha
- */',
+			self::nowdoc('
+			/**
+			 * @param Foo[awesome] $a haha
+			 */'),
+			self::nowdoc('
+			/**
+			 * @param Foo[baz] $a haha
+			 */'),
 			$changeOffsetAccess,
 		];
 
@@ -1207,12 +1312,14 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @phpstan-import-type TypeAlias from AnotherClass as DifferentAlias
- */',
-			'/**
- * @phpstan-import-type TypeAlias from AnotherClass as Ciao
- */',
+			self::nowdoc('
+			/**
+			 * @phpstan-import-type TypeAlias from AnotherClass as DifferentAlias
+			 */'),
+			self::nowdoc('
+			/**
+			 * @phpstan-import-type TypeAlias from AnotherClass as Ciao
+			 */'),
 			$changeTypeAliasImportAs,
 		];
 
@@ -1230,12 +1337,14 @@ public function enterNode(Node $node)
 		};
 
 		yield [
-			'/**
- * @phpstan-import-type TypeAlias from AnotherClass as DifferentAlias
- */',
-			'/**
- * @phpstan-import-type TypeAlias from AnotherClass
- */',
+			self::nowdoc('
+			/**
+			 * @phpstan-import-type TypeAlias from AnotherClass as DifferentAlias
+			 */'),
+			self::nowdoc('
+			/**
+			 * @phpstan-import-type TypeAlias from AnotherClass
+			 */'),
 			$removeImportAs,
 		];
 
@@ -1638,15 +1747,19 @@ public function enterNode(Node $node)
 		];
 
 		yield [
-			'/** @Foo(
-			  *     1,
-			  *     2,
-			  *  ) */',
-			'/** @Foo(
-			  *     1,
-			  *     2,
-			  *     3,
-			  *  ) */',
+			self::nowdoc('
+			/** @Foo(
+			 *     1,
+			 *     2,
+			 *  )
+			 */'),
+			self::nowdoc('
+			/** @Foo(
+			 *     1,
+			 *     2,
+			 *     3,
+			 *  )
+			 */'),
 			new class extends AbstractNodeVisitor {
 
 				public function enterNode(Node $node)
@@ -2160,4 +2273,40 @@ public function testPrintPhpDocNode(PhpDocNode $node, string $expectedResult): v
 		);
 	}
 
+	public static function nowdoc(string $str): string
+	{
+		$lines = preg_split('/\\n/', $str);
+
+		if ($lines === false) {
+			return '';
+		}
+
+		if (count($lines) < 2) {
+			return '';
+		}
+
+		// Toss out the first line
+		$lines = array_slice($lines, 1, count($lines) - 1);
+
+		// normalize any tabs to spaces
+		$lines = array_map(static fn ($line) => preg_replace_callback('/(\t+)/m', static function ($matches) {
+			$fixed = str_repeat('  ', strlen($matches[1]));
+			return $fixed;
+		}, $line), $lines);
+
+		// take the ws from the first line and subtract them from all lines
+		$matches = [];
+
+		if (preg_match('/(^[ \t]+)/', $lines[0] ?? '', $matches) !== 1) {
+			return '';
+		}
+
+		$numLines = count($lines);
+		for ($i = 0; $i < $numLines; ++$i) {
+			$lines[$i] = str_replace($matches[0], '', $lines[$i] ?? '');
+		}
+
+		return implode("\n", $lines);
+	}
+
 }

From 51087f87dcce2663e1fed4dfd4e56eccd580297e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ismail=20=C3=96zg=C3=BCn=20Turan?=
 <oezguen.turan@dadadev.com>
Date: Sun, 16 Feb 2025 14:48:34 +0100
Subject: [PATCH 39/45] Allow multiple new lines in union and intersection
 definition

---
 src/Parser/TypeParser.php                 |  16 ++
 tests/PHPStan/Parser/PhpDocParserTest.php | 172 ++++++++++++++++++++++
 2 files changed, 188 insertions(+)

diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index d5f9217c..b24561b6 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -271,6 +271,14 @@ private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast
 
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
 			$types[] = $this->parseAtomic($tokens);
+			$tokens->pushSavePoint();
+			$tokens->skipNewLineTokens();
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
+				$tokens->rollback();
+				break;
+			}
+
+			$tokens->dropSavePoint();
 		}
 
 		return new Ast\Type\UnionTypeNode($types);
@@ -299,6 +307,14 @@ private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $typ
 
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
 			$types[] = $this->parseAtomic($tokens);
+			$tokens->pushSavePoint();
+			$tokens->skipNewLineTokens();
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
+				$tokens->rollback();
+				break;
+			}
+
+			$tokens->dropSavePoint();
 		}
 
 		return new Ast\Type\IntersectionTypeNode($types);
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 2cbde3c6..49ae1873 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -60,6 +60,7 @@
 use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
 use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode;
@@ -71,6 +72,7 @@
 use PHPUnit\Framework\TestCase;
 use function count;
 use function sprintf;
+use const DIRECTORY_SEPARATOR;
 use const PHP_EOL;
 
 class PhpDocParserTest extends TestCase
@@ -4211,6 +4213,176 @@ public function provideMultiLinePhpDocData(): iterable
 			]),
 		];
 
+		yield [
+			'Multiline PHPDoc with multiple new line within union type declaration',
+			'/**' . PHP_EOL .
+			' * @param array<string, array{' . PHP_EOL .
+			' *     foo: int,' . PHP_EOL .
+			' *     bar?: array<string,' . PHP_EOL .
+			' *         array{foo1: int, bar1?: true}' . PHP_EOL .
+			' *         | array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
+			' *         | array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
+			' *         | array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
+			' *     >,' . PHP_EOL .
+			' * }> $a' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new ParamTagValueNode(
+					new GenericTypeNode(
+						new IdentifierTypeNode('array'),
+						[
+							new IdentifierTypeNode('string'),
+							ArrayShapeNode::createSealed([
+								new ArrayShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
+								new ArrayShapeItemNode(new IdentifierTypeNode('bar'), true, new GenericTypeNode(
+									new IdentifierTypeNode('array'),
+									[
+										new IdentifierTypeNode('string'),
+										new UnionTypeNode([
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('true')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('bool')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar2'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('true')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar3'), true, new IdentifierTypeNode('false')),
+											]),
+										]),
+									],
+									[
+										GenericTypeNode::VARIANCE_INVARIANT,
+										GenericTypeNode::VARIANCE_INVARIANT,
+									],
+								)),
+							]),
+						],
+						[
+							GenericTypeNode::VARIANCE_INVARIANT,
+							GenericTypeNode::VARIANCE_INVARIANT,
+						],
+					),
+					false,
+					'$a',
+					'',
+					false,
+				)),
+			]),
+		];
+
+		yield [
+			'Multiline PHPDoc with multiple new line within intersection type declaration',
+			'/**' . PHP_EOL .
+			' * @param array<string, array{' . PHP_EOL .
+			' *     foo: int,' . PHP_EOL .
+			' *     bar?: array<string,' . PHP_EOL .
+			' *         array{foo1: int, bar1?: true}' . PHP_EOL .
+			' *         & array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
+			' *         & array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
+			' *         & array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
+			' *     >,' . PHP_EOL .
+			' * }> $a' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new ParamTagValueNode(
+					new GenericTypeNode(
+						new IdentifierTypeNode('array'),
+						[
+							new IdentifierTypeNode('string'),
+							ArrayShapeNode::createSealed([
+								new ArrayShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int')),
+								new ArrayShapeItemNode(new IdentifierTypeNode('bar'), true, new GenericTypeNode(
+									new IdentifierTypeNode('array'),
+									[
+										new IdentifierTypeNode('string'),
+										new IntersectionTypeNode([
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('true')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar1'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo2'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('bool')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar2'), true, new IdentifierTypeNode('true')),
+											]),
+											ArrayShapeNode::createSealed([
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo1'), false, new IdentifierTypeNode('int')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('foo3'), false, new IdentifierTypeNode('true')),
+												new ArrayShapeItemNode(new IdentifierTypeNode('bar3'), true, new IdentifierTypeNode('false')),
+											]),
+										]),
+									],
+									[
+										GenericTypeNode::VARIANCE_INVARIANT,
+										GenericTypeNode::VARIANCE_INVARIANT,
+									],
+								)),
+							]),
+						],
+						[
+							GenericTypeNode::VARIANCE_INVARIANT,
+							GenericTypeNode::VARIANCE_INVARIANT,
+						],
+					),
+					false,
+					'$a',
+					'',
+					false,
+				)),
+			]),
+		];
+
+		yield [
+			'Multiline PHPDoc with multiple new line being invalid due to union and intersection type declaration',
+			'/**' . PHP_EOL .
+			' * @param array<string, array{' . PHP_EOL .
+			' *     foo: int,' . PHP_EOL .
+			' *     bar?: array<string,' . PHP_EOL .
+			' *         array{foo1: int, bar1?: true}' . PHP_EOL .
+			' *         & array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
+			' *         | array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
+			' *         & array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
+			' *     >,' . PHP_EOL .
+			' * }> $a' . PHP_EOL .
+			' */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new InvalidTagValueNode(
+					'array<string, array{' . PHP_EOL .
+					'    foo: int,' . PHP_EOL .
+					'    bar?: array<string,' . PHP_EOL .
+					'        array{foo1: int, bar1?: true}' . PHP_EOL .
+					'        & array{foo1: int, foo2: true, bar1?: true}' . PHP_EOL .
+					'        | array{foo2: int, foo3: bool, bar2?: true}' . PHP_EOL .
+					'        & array{foo1: int, foo3: true, bar3?: false}' . PHP_EOL .
+					'    >,' . PHP_EOL .
+					'}> $a',
+					new ParserException(
+						'?',
+						Lexer::TOKEN_NULLABLE,
+						DIRECTORY_SEPARATOR === '\\' ? 65 : 62,
+						Lexer::TOKEN_CLOSE_CURLY_BRACKET,
+						null,
+						4,
+					),
+				)),
+			]),
+		];
+
 		/**
 		 * @return object{
 		 *   a: int,

From 9b30d6fd026b2c132b3985ce6b23bec09ab3aa68 Mon Sep 17 00:00:00 2001
From: Max Loeb <shmax@shmax.com>
Date: Sat, 15 Feb 2025 21:50:22 +0100
Subject: [PATCH 40/45] TypeParser - support comments at EOL with `//`

---
 README.md                                 |   2 +-
 src/Ast/Attribute.php                     |   2 +
 src/Ast/Comment.php                       |  28 +
 src/Ast/NodeAttributes.php                |   4 +
 src/Lexer/Lexer.php                       |   4 +
 src/Parser/PhpDocParser.php               |  10 +
 src/Parser/TokenIterator.php              |  62 ++-
 src/Parser/TypeParser.php                 | 112 ++--
 src/ParserConfig.php                      |   5 +-
 src/Printer/Printer.php                   |  85 ++-
 tests/PHPStan/Parser/PhpDocParserTest.php |  43 ++
 tests/PHPStan/Parser/TypeParserTest.php   | 233 ++++++++-
 tests/PHPStan/Printer/PrinterTest.php     | 601 +++++++++++++++++++++-
 13 files changed, 1110 insertions(+), 81 deletions(-)
 create mode 100644 src/Ast/Comment.php

diff --git a/README.md b/README.md
index 2dae7b2d..15ac9a97 100644
--- a/README.md
+++ b/README.md
@@ -83,7 +83,7 @@ use PHPStan\PhpDocParser\Printer\Printer;
 
 // basic setup with enabled required lexer attributes
 
-$config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true]);
+$config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true, 'comments' => true]);
 $lexer = new Lexer($config);
 $constExprParser = new ConstExprParser($config);
 $typeParser = new TypeParser($config, $constExprParser);
diff --git a/src/Ast/Attribute.php b/src/Ast/Attribute.php
index cd3a0a29..1f770ded 100644
--- a/src/Ast/Attribute.php
+++ b/src/Ast/Attribute.php
@@ -13,4 +13,6 @@ final class Attribute
 
 	public const ORIGINAL_NODE = 'originalNode';
 
+	public const COMMENTS = 'comments';
+
 }
diff --git a/src/Ast/Comment.php b/src/Ast/Comment.php
new file mode 100644
index 00000000..79e24ebb
--- /dev/null
+++ b/src/Ast/Comment.php
@@ -0,0 +1,28 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast;
+
+use function trim;
+
+class Comment
+{
+
+	public string $text;
+
+	public int $startLine;
+
+	public int $startIndex;
+
+	public function __construct(string $text, int $startLine = -1, int $startIndex = -1)
+	{
+		$this->text = $text;
+		$this->startLine = $startLine;
+		$this->startIndex = $startIndex;
+	}
+
+	public function getReformattedText(): string
+	{
+		return trim($this->text);
+	}
+
+}
diff --git a/src/Ast/NodeAttributes.php b/src/Ast/NodeAttributes.php
index a7ddec39..4f2ca76d 100644
--- a/src/Ast/NodeAttributes.php
+++ b/src/Ast/NodeAttributes.php
@@ -15,6 +15,10 @@ trait NodeAttributes
 	 */
 	public function setAttribute(string $key, $value): void
 	{
+		if ($value === null) {
+			unset($this->attributes[$key]);
+			return;
+		}
 		$this->attributes[$key] = $value;
 	}
 
diff --git a/src/Lexer/Lexer.php b/src/Lexer/Lexer.php
index be85fa9f..b2669131 100644
--- a/src/Lexer/Lexer.php
+++ b/src/Lexer/Lexer.php
@@ -51,6 +51,8 @@ class Lexer
 	public const TOKEN_NEGATED = 35;
 	public const TOKEN_ARROW = 36;
 
+	public const TOKEN_COMMENT = 37;
+
 	public const TOKEN_LABELS = [
 		self::TOKEN_REFERENCE => '\'&\'',
 		self::TOKEN_UNION => '\'|\'',
@@ -66,6 +68,7 @@ class Lexer
 		self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'',
 		self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'',
 		self::TOKEN_COMMA => '\',\'',
+		self::TOKEN_COMMENT => '\'//\'',
 		self::TOKEN_COLON => '\':\'',
 		self::TOKEN_VARIADIC => '\'...\'',
 		self::TOKEN_DOUBLE_COLON => '\'::\'',
@@ -160,6 +163,7 @@ private function generateRegexp(): string
 			self::TOKEN_CLOSE_CURLY_BRACKET => '\\}',
 
 			self::TOKEN_COMMA => ',',
+			self::TOKEN_COMMENT => '\/\/[^\\r\\n]*(?=\n|\r|\*/)',
 			self::TOKEN_VARIADIC => '\\.\\.\\.',
 			self::TOKEN_DOUBLE_COLON => '::',
 			self::TOKEN_DOUBLE_ARROW => '=>',
diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index ac717b95..559d8fd5 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -116,9 +116,19 @@ public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
 
 			$tokens->forwardToTheEnd();
 
+			$comments = $tokens->flushComments();
+			if ($comments !== []) {
+				throw new LogicException('Comments should already be flushed');
+			}
+
 			return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0);
 		}
 
+		$comments = $tokens->flushComments();
+		if ($comments !== []) {
+			throw new LogicException('Comments should already be flushed');
+		}
+
 		return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode($children), 1, 0);
 	}
 
diff --git a/src/Parser/TokenIterator.php b/src/Parser/TokenIterator.php
index a9738d62..f2be3da4 100644
--- a/src/Parser/TokenIterator.php
+++ b/src/Parser/TokenIterator.php
@@ -3,6 +3,7 @@
 namespace PHPStan\PhpDocParser\Parser;
 
 use LogicException;
+use PHPStan\PhpDocParser\Ast\Comment;
 use PHPStan\PhpDocParser\Lexer\Lexer;
 use function array_pop;
 use function assert;
@@ -19,7 +20,10 @@ class TokenIterator
 
 	private int $index;
 
-	/** @var int[] */
+	/** @var list<Comment> */
+	private array $comments = [];
+
+	/** @var list<array{int, list<Comment>}> */
 	private array $savePoints = [];
 
 	/** @var list<int> */
@@ -152,8 +156,7 @@ public function consumeTokenType(int $tokenType): void
 			}
 		}
 
-		$this->index++;
-		$this->skipIrrelevantTokens();
+		$this->next();
 	}
 
 
@@ -166,8 +169,7 @@ public function consumeTokenValue(int $tokenType, string $tokenValue): void
 			$this->throwError($tokenType, $tokenValue);
 		}
 
-		$this->index++;
-		$this->skipIrrelevantTokens();
+		$this->next();
 	}
 
 
@@ -178,12 +180,20 @@ public function tryConsumeTokenValue(string $tokenValue): bool
 			return false;
 		}
 
-		$this->index++;
-		$this->skipIrrelevantTokens();
+		$this->next();
 
 		return true;
 	}
 
+	/**
+	 * @return list<Comment>
+	 */
+	public function flushComments(): array
+	{
+		$res = $this->comments;
+		$this->comments = [];
+		return $res;
+	}
 
 	/** @phpstan-impure */
 	public function tryConsumeTokenType(int $tokenType): bool
@@ -198,14 +208,15 @@ public function tryConsumeTokenType(int $tokenType): bool
 			}
 		}
 
-		$this->index++;
-		$this->skipIrrelevantTokens();
+		$this->next();
 
 		return true;
 	}
 
 
-	/** @phpstan-impure */
+	/**
+	 * @deprecated Use skipNewLineTokensAndConsumeComments instead (when parsing a type)
+	 */
 	public function skipNewLineTokens(): void
 	{
 		if (!$this->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
@@ -218,6 +229,29 @@ public function skipNewLineTokens(): void
 	}
 
 
+	public function skipNewLineTokensAndConsumeComments(): void
+	{
+		if ($this->currentTokenType() === Lexer::TOKEN_COMMENT) {
+			$this->comments[] = new Comment($this->currentTokenValue(), $this->currentTokenLine(), $this->currentTokenIndex());
+			$this->next();
+		}
+
+		if (!$this->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+			return;
+		}
+
+		do {
+			$foundNewLine = $this->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			if ($this->currentTokenType() !== Lexer::TOKEN_COMMENT) {
+				continue;
+			}
+
+			$this->comments[] = new Comment($this->currentTokenValue(), $this->currentTokenLine(), $this->currentTokenIndex());
+			$this->next();
+		} while ($foundNewLine === true);
+	}
+
+
 	private function detectNewline(): void
 	{
 		$value = $this->currentTokenValue();
@@ -293,7 +327,7 @@ public function forwardToTheEnd(): void
 
 	public function pushSavePoint(): void
 	{
-		$this->savePoints[] = $this->index;
+		$this->savePoints[] = [$this->index, $this->comments];
 	}
 
 
@@ -305,9 +339,9 @@ public function dropSavePoint(): void
 
 	public function rollback(): void
 	{
-		$index = array_pop($this->savePoints);
-		assert($index !== null);
-		$this->index = $index;
+		$savepoint = array_pop($this->savePoints);
+		assert($savepoint !== null);
+		[$this->index, $this->comments] = $savepoint;
 	}
 
 
diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php
index b24561b6..fc225854 100644
--- a/src/Parser/TypeParser.php
+++ b/src/Parser/TypeParser.php
@@ -41,7 +41,7 @@ public function parse(TokenIterator $tokens): Ast\Type\TypeNode
 			$type = $this->parseAtomic($tokens);
 
 			$tokens->pushSavePoint();
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 
 			try {
 				$enrichedType = $this->enrichTypeOnUnionOrIntersection($tokens, $type);
@@ -91,6 +91,11 @@ public function enrichWithAttributes(TokenIterator $tokens, Ast\Node $type, int
 			$type->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
 		}
 
+		$comments = $tokens->flushComments();
+		if ($this->config->useCommentsAttributes) {
+			$type->setAttribute(Ast\Attribute::COMMENTS, $comments);
+		}
+
 		if ($this->config->useIndexAttributes) {
 			$type->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
 			$type->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
@@ -117,7 +122,7 @@ private function subParse(TokenIterator $tokens): Ast\Type\TypeNode
 			if ($tokens->isCurrentTokenValue('is')) {
 				$type = $this->parseConditional($tokens, $type);
 			} else {
-				$tokens->skipNewLineTokens();
+				$tokens->skipNewLineTokensAndConsumeComments();
 
 				if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
 					$type = $this->subParseUnion($tokens, $type);
@@ -139,9 +144,9 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
 		$startIndex = $tokens->currentTokenIndex();
 
 		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 			$type = $this->subParse($tokens);
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 
 			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
 
@@ -272,7 +277,7 @@ private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
 			$types[] = $this->parseAtomic($tokens);
 			$tokens->pushSavePoint();
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
 				$tokens->rollback();
 				break;
@@ -291,9 +296,9 @@ private function subParseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type):
 		$types = [$type];
 
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 			$types[] = $this->parseAtomic($tokens);
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 		}
 
 		return new Ast\Type\UnionTypeNode($types);
@@ -308,7 +313,7 @@ private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $typ
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
 			$types[] = $this->parseAtomic($tokens);
 			$tokens->pushSavePoint();
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
 				$tokens->rollback();
 				break;
@@ -327,9 +332,9 @@ private function subParseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $
 		$types = [$type];
 
 		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 			$types[] = $this->parseAtomic($tokens);
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 		}
 
 		return new Ast\Type\IntersectionTypeNode($types);
@@ -349,15 +354,15 @@ private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subj
 
 		$targetType = $this->parse($tokens);
 
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 		$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$ifType = $this->parse($tokens);
 
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$elseType = $this->subParse($tokens);
 
@@ -378,15 +383,15 @@ private function parseConditionalForParameter(TokenIterator $tokens, string $par
 
 		$targetType = $this->parse($tokens);
 
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 		$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$ifType = $this->parse($tokens);
 
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$elseType = $this->subParse($tokens);
 
@@ -445,6 +450,7 @@ public function isHtml(TokenIterator $tokens): bool
 	public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType): Ast\Type\GenericTypeNode
 	{
 		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$startLine = $baseType->getAttribute(Ast\Attribute::START_LINE);
 		$startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX);
@@ -456,7 +462,7 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode
 			$isFirst
 			|| $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)
 		) {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 
 			// trailing comma case
 			if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
@@ -465,7 +471,7 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode
 			$isFirst = false;
 
 			[$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens);
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 		}
 
 		$type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances);
@@ -556,19 +562,19 @@ private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNod
 			: [];
 
 		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$parameters = [];
 		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
 			$parameters[] = $this->parseCallableParameter($tokens);
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 			while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
-				$tokens->skipNewLineTokens();
+				$tokens->skipNewLineTokensAndConsumeComments();
 				if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
 					break;
 				}
 				$parameters[] = $this->parseCallableParameter($tokens);
-				$tokens->skipNewLineTokens();
+				$tokens->skipNewLineTokensAndConsumeComments();
 			}
 		}
 
@@ -596,7 +602,7 @@ private function parseCallableTemplates(TokenIterator $tokens): array
 
 		$isFirst = true;
 		while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 
 			// trailing comma case
 			if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
@@ -605,7 +611,7 @@ private function parseCallableTemplates(TokenIterator $tokens): array
 			$isFirst = false;
 
 			$templates[] = $this->parseCallableTemplateArgument($tokens);
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 		}
 
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
@@ -875,8 +881,10 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 		$sealed = true;
 		$unsealedType = null;
 
+		$done = false;
+
 		do {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
 				return Ast\Type\ArrayShapeNode::createSealed($items, $kind);
@@ -885,14 +893,14 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC)) {
 				$sealed = false;
 
-				$tokens->skipNewLineTokens();
+				$tokens->skipNewLineTokensAndConsumeComments();
 				if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
 					if ($kind === Ast\Type\ArrayShapeNode::KIND_ARRAY) {
 						$unsealedType = $this->parseArrayShapeUnsealedType($tokens);
 					} else {
 						$unsealedType = $this->parseListShapeUnsealedType($tokens);
 					}
-					$tokens->skipNewLineTokens();
+					$tokens->skipNewLineTokensAndConsumeComments();
 				}
 
 				$tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA);
@@ -900,11 +908,19 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
 			}
 
 			$items[] = $this->parseArrayShapeItem($tokens);
+			$tokens->skipNewLineTokensAndConsumeComments();
+			if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
+				$done = true;
+			}
+			if ($tokens->currentTokenType() !== Lexer::TOKEN_COMMENT) {
+				continue;
+			}
 
-			$tokens->skipNewLineTokens();
-		} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
+			$tokens->next();
 
-		$tokens->skipNewLineTokens();
+		} while (!$done);
+
+		$tokens->skipNewLineTokensAndConsumeComments();
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
 
 		if ($sealed) {
@@ -920,12 +936,17 @@ private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShape
 	{
 		$startLine = $tokens->currentTokenLine();
 		$startIndex = $tokens->currentTokenIndex();
+
+		// parse any comments above the item
+		$tokens->skipNewLineTokensAndConsumeComments();
+
 		try {
 			$tokens->pushSavePoint();
 			$key = $this->parseArrayShapeKey($tokens);
 			$optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE);
 			$tokens->consumeTokenType(Lexer::TOKEN_COLON);
 			$value = $this->parse($tokens);
+
 			$tokens->dropSavePoint();
 
 			return $this->enrichWithAttributes(
@@ -991,18 +1012,18 @@ private function parseArrayShapeUnsealedType(TokenIterator $tokens): Ast\Type\Ar
 		$startIndex = $tokens->currentTokenIndex();
 
 		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$valueType = $this->parse($tokens);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$keyType = null;
 		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 
 			$keyType = $valueType;
 			$valueType = $this->parse($tokens);
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 		}
 
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
@@ -1024,10 +1045,10 @@ private function parseListShapeUnsealedType(TokenIterator $tokens): Ast\Type\Arr
 		$startIndex = $tokens->currentTokenIndex();
 
 		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$valueType = $this->parse($tokens);
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
 
@@ -1049,7 +1070,7 @@ private function parseObjectShape(TokenIterator $tokens): Ast\Type\ObjectShapeNo
 		$items = [];
 
 		do {
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 
 			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
 				return new Ast\Type\ObjectShapeNode($items);
@@ -1057,10 +1078,10 @@ private function parseObjectShape(TokenIterator $tokens): Ast\Type\ObjectShapeNo
 
 			$items[] = $this->parseObjectShapeItem($tokens);
 
-			$tokens->skipNewLineTokens();
+			$tokens->skipNewLineTokensAndConsumeComments();
 		} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
 
-		$tokens->skipNewLineTokens();
+		$tokens->skipNewLineTokensAndConsumeComments();
 		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
 
 		return new Ast\Type\ObjectShapeNode($items);
@@ -1072,12 +1093,19 @@ private function parseObjectShapeItem(TokenIterator $tokens): Ast\Type\ObjectSha
 		$startLine = $tokens->currentTokenLine();
 		$startIndex = $tokens->currentTokenIndex();
 
+		$tokens->skipNewLineTokensAndConsumeComments();
+
 		$key = $this->parseObjectShapeKey($tokens);
 		$optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE);
 		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
 		$value = $this->parse($tokens);
 
-		return $this->enrichWithAttributes($tokens, new Ast\Type\ObjectShapeItemNode($key, $optional, $value), $startLine, $startIndex);
+		return $this->enrichWithAttributes(
+			$tokens,
+			new Ast\Type\ObjectShapeItemNode($key, $optional, $value),
+			$startLine,
+			$startIndex,
+		);
 	}
 
 	/**
diff --git a/src/ParserConfig.php b/src/ParserConfig.php
index b3a8889a..0c4377ad 100644
--- a/src/ParserConfig.php
+++ b/src/ParserConfig.php
@@ -9,13 +9,16 @@ class ParserConfig
 
 	public bool $useIndexAttributes;
 
+	public bool $useCommentsAttributes;
+
 	/**
-	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
+	 * @param array{lines?: bool, indexes?: bool, comments?: bool} $usedAttributes
 	 */
 	public function __construct(array $usedAttributes)
 	{
 		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
 		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
+		$this->useCommentsAttributes = $usedAttributes['comments'] ?? false;
 	}
 
 }
diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php
index 8febe826..af920f8d 100644
--- a/src/Printer/Printer.php
+++ b/src/Printer/Printer.php
@@ -4,6 +4,7 @@
 
 use LogicException;
 use PHPStan\PhpDocParser\Ast\Attribute;
+use PHPStan\PhpDocParser\Ast\Comment;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
 use PHPStan\PhpDocParser\Ast\Node;
@@ -66,6 +67,7 @@
 use PHPStan\PhpDocParser\Parser\TokenIterator;
 use function array_keys;
 use function array_map;
+use function assert;
 use function count;
 use function get_class;
 use function get_object_vars;
@@ -74,6 +76,7 @@
 use function is_array;
 use function preg_match_all;
 use function sprintf;
+use function str_replace;
 use function strlen;
 use function strpos;
 use function trim;
@@ -547,23 +550,30 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 
 		foreach ($diff as $i => $diffElem) {
 			$diffType = $diffElem->type;
-			$newNode = $diffElem->new;
-			$originalNode = $diffElem->old;
+			$arrItem = $diffElem->new;
+			$origArrayItem = $diffElem->old;
 			if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
 				$beforeFirstKeepOrReplace = false;
-				if (!$newNode instanceof Node || !$originalNode instanceof Node) {
+				if (!$arrItem instanceof Node || !$origArrayItem instanceof Node) {
 					return null;
 				}
 
 				/** @var int $itemStartPos */
-				$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
+				$itemStartPos = $origArrayItem->getAttribute(Attribute::START_INDEX);
 
 				/** @var int $itemEndPos */
-				$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
+				$itemEndPos = $origArrayItem->getAttribute(Attribute::END_INDEX);
+
 				if ($itemStartPos < 0 || $itemEndPos < 0 || $itemStartPos < $tokenIndex) {
 					throw new LogicException();
 				}
 
+				$comments = $arrItem->getAttribute(Attribute::COMMENTS) ?? [];
+				$origComments = $origArrayItem->getAttribute(Attribute::COMMENTS) ?? [];
+
+				$commentStartPos = count($origComments) > 0 ? $origComments[0]->startIndex : $itemStartPos;
+				assert($commentStartPos >= 0);
+
 				$result .= $originalTokens->getContentBetween($tokenIndex, $itemStartPos);
 
 				if (count($delayedAdd) > 0) {
@@ -573,6 +583,15 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 						if ($parenthesesNeeded) {
 							$result .= '(';
 						}
+
+						if ($insertNewline) {
+							$delayedAddComments = $delayedAddNode->getAttribute(Attribute::COMMENTS) ?? [];
+							if (count($delayedAddComments) > 0) {
+								$result .= $this->printComments($delayedAddComments, $beforeAsteriskIndent, $afterAsteriskIndent);
+								$result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+							}
+						}
+
 						$result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens);
 						if ($parenthesesNeeded) {
 							$result .= ')';
@@ -589,14 +608,21 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 				}
 
 				$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
-					&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true)
-					&& !in_array(get_class($originalNode), $this->parenthesesListMap[$mapKey], true);
+					&& in_array(get_class($arrItem), $this->parenthesesListMap[$mapKey], true)
+					&& !in_array(get_class($origArrayItem), $this->parenthesesListMap[$mapKey], true);
 				$addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($itemStartPos, $itemEndPos);
 				if ($addParentheses) {
 					$result .= '(';
 				}
 
-				$result .= $this->printNodeFormatPreserving($newNode, $originalTokens);
+				if ($comments !== $origComments) {
+					if (count($comments) > 0) {
+						$result .= $this->printComments($comments, $beforeAsteriskIndent, $afterAsteriskIndent);
+						$result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+					}
+				}
+
+				$result .= $this->printNodeFormatPreserving($arrItem, $originalTokens);
 				if ($addParentheses) {
 					$result .= ')';
 				}
@@ -606,36 +632,42 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 				if ($insertStr === null) {
 					return null;
 				}
-				if (!$newNode instanceof Node) {
+				if (!$arrItem instanceof Node) {
 					return null;
 				}
 
-				if ($insertStr === ', ' && $isMultiline) {
+				if ($insertStr === ', ' && $isMultiline || count($arrItem->getAttribute(Attribute::COMMENTS) ?? []) > 0) {
 					$insertStr = ',';
 					$insertNewline = true;
 				}
 
 				if ($beforeFirstKeepOrReplace) {
 					// Will be inserted at the next "replace" or "keep" element
-					$delayedAdd[] = $newNode;
+					$delayedAdd[] = $arrItem;
 					continue;
 				}
 
 				/** @var int $itemEndPos */
 				$itemEndPos = $tokenIndex - 1;
 				if ($insertNewline) {
-					$result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+					$comments = $arrItem->getAttribute(Attribute::COMMENTS) ?? [];
+					$result .= $insertStr;
+					if (count($comments) > 0) {
+						$result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+						$result .= $this->printComments($comments, $beforeAsteriskIndent, $afterAsteriskIndent);
+					}
+					$result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
 				} else {
 					$result .= $insertStr;
 				}
 
 				$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
-					&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true);
+					&& in_array(get_class($arrItem), $this->parenthesesListMap[$mapKey], true);
 				if ($parenthesesNeeded) {
 					$result .= '(';
 				}
 
-				$result .= $this->printNodeFormatPreserving($newNode, $originalTokens);
+				$result .= $this->printNodeFormatPreserving($arrItem, $originalTokens);
 				if ($parenthesesNeeded) {
 					$result .= ')';
 				}
@@ -643,15 +675,15 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 				$tokenIndex = $itemEndPos + 1;
 
 			} elseif ($diffType === DiffElem::TYPE_REMOVE) {
-				if (!$originalNode instanceof Node) {
+				if (!$origArrayItem instanceof Node) {
 					return null;
 				}
 
 				/** @var int $itemStartPos */
-				$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
+				$itemStartPos = $origArrayItem->getAttribute(Attribute::START_INDEX);
 
 				/** @var int $itemEndPos */
-				$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
+				$itemEndPos = $origArrayItem->getAttribute(Attribute::END_INDEX);
 				if ($itemStartPos < 0 || $itemEndPos < 0) {
 					throw new LogicException();
 				}
@@ -709,6 +741,20 @@ private function printArrayFormatPreserving(array $nodes, array $originalNodes,
 		return $result;
 	}
 
+	/**
+	 * @param list<Comment> $comments
+	 */
+	private function printComments(array $comments, string $beforeAsteriskIndent, string $afterAsteriskIndent): string
+	{
+		$formattedComments = [];
+
+		foreach ($comments as $comment) {
+			$formattedComments[] = str_replace("\n", "\n" . $beforeAsteriskIndent . '*' . $afterAsteriskIndent, $comment->getReformattedText());
+		}
+
+		return implode("\n$beforeAsteriskIndent*$afterAsteriskIndent", $formattedComments);
+	}
+
 	/**
 	 * @param array<Node|null> $nodes
 	 * @return array{bool, string, string}
@@ -738,7 +784,7 @@ private function isMultiline(int $initialIndex, array $nodes, TokenIterator $ori
 
 		$c = preg_match_all('~\n(?<before>[\\x09\\x20]*)\*(?<after>\\x20*)~', $allText, $matches, PREG_SET_ORDER);
 		if ($c === 0) {
-			return [$isMultiline, '', ''];
+			return [$isMultiline, ' ', '  '];
 		}
 
 		$before = '';
@@ -754,6 +800,9 @@ private function isMultiline(int $initialIndex, array $nodes, TokenIterator $ori
 			$after = $match['after'];
 		}
 
+		$before = strlen($before) === 0 ? ' ' : $before;
+		$after = strlen($after) === 0 ? '  ' : $after;
+
 		return [$isMultiline, $before, $after];
 	}
 
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index 49ae1873..abe69b8e 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -6345,6 +6345,49 @@ public function provideCommentLikeDescriptions(): Iterator
 			]),
 		];
 
+		yield [
+			'Comment after @param with https://',
+			'/** @param int $a https://phpstan.org/ */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new ParamTagValueNode(
+					new IdentifierTypeNode('int'),
+					false,
+					'$a',
+					'https://phpstan.org/',
+					false,
+				)),
+			]),
+		];
+
+		yield [
+			'Comment after @param with https:// in // comment',
+			'/** @param int $a // comment https://phpstan.org/ */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new ParamTagValueNode(
+					new IdentifierTypeNode('int'),
+					false,
+					'$a',
+					'// comment https://phpstan.org/',
+					false,
+				)),
+			]),
+		];
+
+		yield [
+			'Comment in PHPDoc tag outside of type',
+			'/** @param // comment */',
+			new PhpDocNode([
+				new PhpDocTagNode('@param', new InvalidTagValueNode('// comment', new ParserException(
+					'// comment ',
+					37,
+					11,
+					24,
+					null,
+					1,
+				))),
+			]),
+		];
+
 		yield [
 			'Comment on a separate line',
 			'/**' . PHP_EOL .
diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php
index 6306cfb8..8fe96f5f 100644
--- a/tests/PHPStan/Parser/TypeParserTest.php
+++ b/tests/PHPStan/Parser/TypeParserTest.php
@@ -3,13 +3,17 @@
 namespace PHPStan\PhpDocParser\Parser;
 
 use Exception;
+use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
 use PHPStan\PhpDocParser\Ast\Attribute;
+use PHPStan\PhpDocParser\Ast\Comment;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
 use PHPStan\PhpDocParser\Ast\Node;
 use PHPStan\PhpDocParser\Ast\NodeTraverser;
+use PHPStan\PhpDocParser\Ast\NodeVisitor\CloningVisitor;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
 use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
 use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
@@ -67,10 +71,11 @@ public function testParse(string $input, $expectedResult, int $nextTokenType = L
 
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 		$typeNode = $this->typeParser->parse($tokens);
+		$this->assertInstanceOf(TypeNode::class, $expectedResult);
 
 		$this->assertSame((string) $expectedResult, (string) $typeNode);
 		$this->assertInstanceOf(get_class($expectedResult), $typeNode);
-		$this->assertEquals($expectedResult, $typeNode);
+		$this->assertEquals($this->unsetAllAttributes($expectedResult), $this->unsetAllAttributes($typeNode));
 		$this->assertSame($nextTokenType, $tokens->currentTokenType(), Lexer::TOKEN_LABELS[$nextTokenType]);
 
 		if (strpos((string) $expectedResult, '$ref') !== false) {
@@ -117,13 +122,16 @@ public function testVerifyAttributes(string $input, $expectedResult): void
 			$this->expectExceptionMessage($expectedResult->getMessage());
 		}
 
-		$config = new ParserConfig(['lines' => true, 'indexes' => true]);
+		$config = new ParserConfig(['lines' => true, 'indexes' => true, 'comments' => true]);
 		$typeParser = new TypeParser($config, new ConstExprParser($config));
 		$tokens = new TokenIterator($this->lexer->tokenize($input));
 
+		$typeNode = $typeParser->parse($tokens);
+		$this->assertInstanceOf(TypeNode::class, $expectedResult);
+
 		$visitor = new NodeCollectingVisitor();
 		$traverser = new NodeTraverser([$visitor]);
-		$traverser->traverse([$typeParser->parse($tokens)]);
+		$traverser->traverse([$typeNode]);
 
 		foreach ($visitor->nodes as $node) {
 			$this->assertNotNull($node->getAttribute(Attribute::START_LINE), (string) $node);
@@ -131,6 +139,84 @@ public function testVerifyAttributes(string $input, $expectedResult): void
 			$this->assertNotNull($node->getAttribute(Attribute::START_INDEX), (string) $node);
 			$this->assertNotNull($node->getAttribute(Attribute::END_INDEX), (string) $node);
 		}
+
+		$this->assertEquals(
+			$this->unsetAllAttributesButComments($expectedResult),
+			$this->unsetAllAttributesButComments($typeNode),
+		);
+	}
+
+
+	private function unsetAllAttributes(Node $node): Node
+	{
+		$visitor = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				$node->setAttribute(Attribute::START_LINE, null);
+				$node->setAttribute(Attribute::END_LINE, null);
+				$node->setAttribute(Attribute::START_INDEX, null);
+				$node->setAttribute(Attribute::END_INDEX, null);
+				$node->setAttribute(Attribute::ORIGINAL_NODE, null);
+				$node->setAttribute(Attribute::COMMENTS, null);
+
+				return $node;
+			}
+
+		};
+
+		$cloningTraverser = new NodeTraverser([new CloningVisitor()]);
+		$newNodes = $cloningTraverser->traverse([$node]);
+
+		$traverser = new NodeTraverser([$visitor]);
+
+		/** @var PhpDocNode */
+		return $traverser->traverse($newNodes)[0];
+	}
+
+
+	private function unsetAllAttributesButComments(Node $node): Node
+	{
+		$visitor = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				$node->setAttribute(Attribute::START_LINE, null);
+				$node->setAttribute(Attribute::END_LINE, null);
+				$node->setAttribute(Attribute::START_INDEX, null);
+				$node->setAttribute(Attribute::END_INDEX, null);
+				$node->setAttribute(Attribute::ORIGINAL_NODE, null);
+
+				if ($node->getAttribute(Attribute::COMMENTS) === []) {
+					$node->setAttribute(Attribute::COMMENTS, null);
+				}
+
+				return $node;
+			}
+
+		};
+
+		$cloningTraverser = new NodeTraverser([new CloningVisitor()]);
+		$newNodes = $cloningTraverser->traverse([$node]);
+
+		$traverser = new NodeTraverser([$visitor]);
+
+		/** @var PhpDocNode */
+		return $traverser->traverse($newNodes)[0];
+	}
+
+
+	/**
+	 * @template TNode of Node
+	 * @param TNode $node
+	 * @return TNode
+	 */
+	public static function withComment(Node $node, string $comment, int $startLine, int $startIndex): Node
+	{
+		$comments = $node->getAttribute(Attribute::COMMENTS) ?? [];
+		$comments[] = new Comment($comment, $startLine, $startIndex);
+		$node->setAttribute(Attribute::COMMENTS, $comments);
+		return $node;
 	}
 
 
@@ -140,6 +226,100 @@ public function testVerifyAttributes(string $input, $expectedResult): void
 	public function provideParseData(): array
 	{
 		return [
+			[
+				'array{
+					// a is for apple
+					a: int,
+				}',
+				ArrayShapeNode::createSealed([
+					new ArrayShapeItemNode(
+						self::withComment(new IdentifierTypeNode('a'), '// a is for apple', 2, 3),
+						false,
+						new IdentifierTypeNode('int'),
+					),
+				]),
+			],
+			[
+				'array{
+					// a is for // apple
+					a: int,
+				}',
+				ArrayShapeNode::createSealed([
+					new ArrayShapeItemNode(
+						self::withComment(new IdentifierTypeNode('a'), '// a is for // apple', 2, 3),
+						false,
+						new IdentifierTypeNode('int'),
+					),
+				]),
+			],
+			[
+				'array{
+					// a is for * apple
+					a: int,
+				}',
+				ArrayShapeNode::createSealed([
+					new ArrayShapeItemNode(
+						self::withComment(new IdentifierTypeNode('a'), '// a is for * apple', 2, 3),
+						false,
+						new IdentifierTypeNode('int'),
+					),
+				]),
+			],
+			[
+				'array{
+					// a is for http://www.apple.com/
+					a: int,
+				}',
+				ArrayShapeNode::createSealed([
+					new ArrayShapeItemNode(
+						self::withComment(new IdentifierTypeNode('a'), '// a is for http://www.apple.com/', 2, 3),
+						false,
+						new IdentifierTypeNode('int'),
+					),
+				]),
+			],
+			[
+				'array{
+					// a is for apple
+					// a is also for awesome
+					a: int,
+				}',
+				ArrayShapeNode::createSealed([
+					new ArrayShapeItemNode(
+						self::withComment(self::withComment(new IdentifierTypeNode('a'), '// a is for apple', 2, 3), '// a is also for awesome', 3, 5),
+						false,
+						new IdentifierTypeNode('int'),
+					),
+				]),
+			],
+			[
+				'string',
+				new IdentifierTypeNode('string'),
+			],
+			[
+				'  string  ',
+				new IdentifierTypeNode('string'),
+			],
+			[
+				' ( string ) ',
+				new IdentifierTypeNode('string'),
+			],
+			[
+				'( ( string ) )',
+				new IdentifierTypeNode('string'),
+			],
+			[
+				'\\Foo\Bar\\Baz',
+				new IdentifierTypeNode('\\Foo\Bar\\Baz'),
+			],
+			[
+				'  \\Foo\Bar\\Baz  ',
+				new IdentifierTypeNode('\\Foo\Bar\\Baz'),
+			],
+			[
+				' ( \\Foo\Bar\\Baz ) ',
+				new IdentifierTypeNode('\\Foo\Bar\\Baz'),
+			],
 			[
 				'string',
 				new IdentifierTypeNode('string'),
@@ -371,6 +551,24 @@ public function provideParseData(): array
 					],
 				),
 			],
+			[
+				'array<
+					// index with an int
+					int,
+					Foo\\Bar
+				>',
+				new GenericTypeNode(
+					new IdentifierTypeNode('array'),
+					[
+						new IdentifierTypeNode('int'),
+						new IdentifierTypeNode('Foo\\Bar'),
+					],
+					[
+						GenericTypeNode::VARIANCE_INVARIANT,
+						GenericTypeNode::VARIANCE_INVARIANT,
+					],
+				),
+			],
 			[
 				'array {\'a\': int}',
 				new IdentifierTypeNode('array'),
@@ -2014,6 +2212,22 @@ public function provideParseData(): array
 					false,
 				),
 			],
+			[
+				'(
+					Foo is Bar
+					?
+					// never, I say
+					never
+					:
+					int)',
+				new ConditionalTypeNode(
+					new IdentifierTypeNode('Foo'),
+					new IdentifierTypeNode('Bar'),
+					new IdentifierTypeNode('never'),
+					new IdentifierTypeNode('int'),
+					false,
+				),
+			],
 			[
 				'(Foo is not Bar ? never : int)',
 				new ConditionalTypeNode(
@@ -2516,6 +2730,19 @@ public function provideParseData(): array
 					),
 				]),
 			],
+			[
+				'object{
+					// a is for apple
+				 	a: int,
+				 }',
+				new ObjectShapeNode([
+					new ObjectShapeItemNode(
+						self::withComment(new IdentifierTypeNode('a'), '// a is for apple', 2, 3),
+						false,
+						new IdentifierTypeNode('int'),
+					),
+				]),
+			],
 			[
 				'object{
 				 	a: int,
diff --git a/tests/PHPStan/Printer/PrinterTest.php b/tests/PHPStan/Printer/PrinterTest.php
index 93e78bcb..464b7234 100644
--- a/tests/PHPStan/Printer/PrinterTest.php
+++ b/tests/PHPStan/Printer/PrinterTest.php
@@ -4,6 +4,7 @@
 
 use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
 use PHPStan\PhpDocParser\Ast\Attribute;
+use PHPStan\PhpDocParser\Ast\Comment;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayItemNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
 use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
@@ -76,7 +77,7 @@ class PrinterTest extends TestCase
 
 	protected function setUp(): void
 	{
-		$config = new ParserConfig(['lines' => true, 'indexes' => true]);
+		$config = new ParserConfig(['lines' => true, 'indexes' => true, 'comments' => true]);
 		$constExprParser = new ConstExprParser($config);
 		$this->typeParser = new TypeParser($config, $constExprParser);
 		$this->phpDocParser = new PhpDocParser(
@@ -951,6 +952,219 @@ public function enterNode(Node $node)
 			$addItemsToObjectShape,
 		];
 
+		$addItemsWithCommentsToMultilineArrayShape = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof ArrayShapeNode) {
+					$commentedNode = new ArrayShapeItemNode(new IdentifierTypeNode('b'), false, new IdentifierTypeNode('int'));
+					$commentedNode->setAttribute(Attribute::COMMENTS, [new Comment('// bar')]);
+					array_splice($node->items, 1, 0, [
+						$commentedNode,
+					]);
+					$commentedNode = new ArrayShapeItemNode(new IdentifierTypeNode('d'), false, new IdentifierTypeNode('string'));
+					$commentedNode->setAttribute(Attribute::COMMENTS, [new Comment(
+						PrinterTest::nowdoc('
+						// first comment'),
+					)]);
+					$node->items[] = $commentedNode;
+
+					$commentedNode = new ArrayShapeItemNode(new IdentifierTypeNode('e'), false, new IdentifierTypeNode('string'));
+					$commentedNode->setAttribute(Attribute::COMMENTS, [new Comment(
+						PrinterTest::nowdoc('
+						// second comment'),
+					)]);
+					$node->items[] = $commentedNode;
+
+					$commentedNode = new ArrayShapeItemNode(new IdentifierTypeNode('f'), false, new IdentifierTypeNode('string'));
+					$commentedNode->setAttribute(Attribute::COMMENTS, [
+						new Comment('// third comment'),
+						new Comment('// fourth comment'),
+					]);
+					$node->items[] = $commentedNode;
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *  // foo
+			 *	a: int,
+			 *	c: string
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *  // foo
+			 *	a: int,
+			 *  // bar
+			 *  b: int,
+			 *	c: string,
+			 *  // first comment
+			 *  d: string,
+			 *  // second comment
+			 *  e: string,
+			 *  // third comment
+			 *  // fourth comment
+			 *  f: string
+			 * }
+			 */'),
+			$addItemsWithCommentsToMultilineArrayShape,
+		];
+
+		$prependItemsWithCommentsToMultilineArrayShape = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof ArrayShapeNode) {
+					$commentedNode = new ArrayShapeItemNode(new IdentifierTypeNode('a'), false, new IdentifierTypeNode('int'));
+					$commentedNode->setAttribute(Attribute::COMMENTS, [new Comment('// first item')]);
+					array_splice($node->items, 0, 0, [
+						$commentedNode,
+					]);
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *  b: int,
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *  // first item
+			 *  a: int,
+			 *  b: int,
+			 * }
+			 */'),
+			$prependItemsWithCommentsToMultilineArrayShape,
+		];
+
+		$changeCommentOnArrayShapeItem = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof ArrayShapeItemNode) {
+					$node->setAttribute(Attribute::COMMENTS, [new Comment('// puppies')]);
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   a: int,
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *   // puppies
+			 *   a: int,
+			 * }
+			 */'),
+			$changeCommentOnArrayShapeItem,
+		];
+
+		$addItemsWithCommentsToObjectShape = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof ObjectShapeNode) {
+					$item = new ObjectShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('int'));
+					$item->setAttribute(Attribute::COMMENTS, [new Comment('// favorite foo')]);
+					$node->items[] = $item;
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @return object{
+				 *   // your favorite bar
+				 *   bar: string
+				 * }
+				 */'),
+			self::nowdoc('
+				/**
+				 * @return object{
+				 *   // your favorite bar
+				 *   bar: string,
+				 *	 // favorite foo
+				 *	 foo: int
+				 * }
+				 */'),
+			$addItemsWithCommentsToObjectShape,
+		];
+
+		$removeItemWithComment = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if (!$node instanceof ArrayShapeNode) {
+					return null;
+				}
+
+				foreach ($node->items as $i => $item) {
+					if ($item->keyName === null) {
+						continue;
+					}
+
+					$comments = $item->keyName->getAttribute(Attribute::COMMENTS);
+					if ($comments === null) {
+						continue;
+					}
+					if ($comments === []) {
+						continue;
+					}
+
+					unset($node->items[$i]);
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *  a: string,
+			 *  // b comment
+			 *  b: int,
+			 * }
+			 */'),
+			self::nowdoc('
+			/**
+			 * @return array{
+			 *  a: string,
+			 * }
+			 */'),
+			$removeItemWithComment,
+		];
+
 		$addItemsToConstExprArray = new class extends AbstractNodeVisitor {
 
 			public function enterNode(Node $node)
@@ -2039,6 +2253,377 @@ public function enterNode(Node $node)
 
 			},
 		];
+
+		$singleCommentLineAddFront = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof ArrayShapeNode) {
+					array_unshift($node->items, PrinterTest::withComment(
+						new ArrayShapeItemNode(null, false, new IdentifierTypeNode('float')),
+						'// A fractional number',
+					));
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{float} $foo
+				 */'),
+			$singleCommentLineAddFront,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{string} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{// A fractional number
+				 *  float,
+				 *  string} $foo
+				 */'),
+			$singleCommentLineAddFront,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   string,int} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   // A fractional number
+				 *   float,
+				 *   string,int} $foo
+				 */'),
+			$singleCommentLineAddFront,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   string,
+				 *	 int
+				 * } $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   // A fractional number
+				 *   float,
+				 *   string,
+				 *   int
+				 * } $foo
+				 */'),
+			$singleCommentLineAddFront,
+		];
+
+		$singleCommentLineAddMiddle = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				$newItem = PrinterTest::withComment(
+					new ArrayShapeItemNode(null, false, new IdentifierTypeNode('float')),
+					'// A fractional number',
+				);
+
+				if ($node instanceof ArrayShapeNode) {
+					if (count($node->items) === 0) {
+						$node->items[] = $newItem;
+					} else {
+						array_splice($node->items, 1, 0, [$newItem]);
+					}
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{float} $foo
+				 */'),
+			$singleCommentLineAddMiddle,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{string} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{string,
+				 *  // A fractional number
+				 *  float} $foo
+				 */'),
+			$singleCommentLineAddMiddle,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   string,int} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   string,
+				 *   // A fractional number
+				 *   float,int} $foo
+				 */'),
+			$singleCommentLineAddMiddle,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   string,
+				 *	 int
+				 * } $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param array{
+				 *   string,
+				 *   // A fractional number
+				 *   float,
+				 *   int
+				 * } $foo
+				 */'),
+			$singleCommentLineAddMiddle,
+		];
+
+		$addCommentToCallableParamsFront = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof CallableTypeNode) {
+					array_unshift($node->parameters, PrinterTest::withComment(
+						new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '$foo', false),
+						'// never pet a burning dog',
+					));
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param callable(Bar $bar): int $a
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param callable(// never pet a burning dog
+				 *  Foo $foo,
+				 *  Bar $bar): int $a
+				 */'),
+			$addCommentToCallableParamsFront,
+		];
+
+		$addCommentToCallableParamsMiddle = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof CallableTypeNode) {
+					$node->parameters[] = PrinterTest::withComment(
+						new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '$bar', false),
+						'// never pet a burning dog',
+					);
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param callable(Foo $foo): int $a
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param callable(Foo $foo,
+				 *  // never pet a burning dog
+				 *  Bar $bar): int $a
+				 */'),
+			$addCommentToCallableParamsMiddle,
+		];
+
+		$addCommentToObjectShapeItemFront = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof ObjectShapeNode) {
+					array_unshift($node->items, PrinterTest::withComment(
+						new ObjectShapeItemNode(new IdentifierTypeNode('foo'), false, new IdentifierTypeNode('float')),
+						'// A fractional number',
+					));
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param object{bar: string} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param object{// A fractional number
+				 *  foo: float,
+				 *  bar: string} $foo
+				 */'),
+			$addCommentToObjectShapeItemFront,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   bar:string,naz:int} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   // A fractional number
+				 *   foo: float,
+				 *   bar:string,naz:int} $foo
+				 */'),
+			$addCommentToObjectShapeItemFront,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   bar:string,
+				 *	 naz:int
+				 * } $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   // A fractional number
+				 *   foo: float,
+				 *   bar:string,
+				 *   naz:int
+				 * } $foo
+				 */'),
+			$addCommentToObjectShapeItemFront,
+		];
+
+		$addCommentToObjectShapeItemMiddle = new class extends AbstractNodeVisitor {
+
+			public function enterNode(Node $node)
+			{
+				if ($node instanceof ObjectShapeNode) {
+					$newItem = PrinterTest::withComment(
+						new ObjectShapeItemNode(new IdentifierTypeNode('bar'), false, new IdentifierTypeNode('float')),
+						'// A fractional number',
+					);
+					if (count($node->items) === 0) {
+						$node->items[] = $newItem;
+					} else {
+						array_splice($node->items, 1, 0, [$newItem]);
+					}
+				}
+
+				return $node;
+			}
+
+		};
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param object{} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param object{bar: float} $foo
+				 */'),
+			$addCommentToObjectShapeItemMiddle,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param object{foo:string} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param object{foo:string,
+				 *  // A fractional number
+				 *  bar: float} $foo
+				 */'),
+			$addCommentToObjectShapeItemMiddle,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   foo:string,naz:int} $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   foo:string,
+				 *   // A fractional number
+				 *   bar: float,naz:int} $foo
+				 */'),
+			$addCommentToObjectShapeItemMiddle,
+		];
+
+		yield [
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   foo:string,
+				 *	 naz:int
+				 * } $foo
+				 */'),
+			self::nowdoc('
+				/**
+				 * @param object{
+				 *   foo:string,
+				 *   // A fractional number
+				 *   bar: float,
+				 *   naz:int
+				 * } $foo
+				 */'),
+			$addCommentToObjectShapeItemMiddle,
+		];
 	}
 
 	/**
@@ -2046,7 +2631,7 @@ public function enterNode(Node $node)
 	 */
 	public function testPrintFormatPreserving(string $phpDoc, string $expectedResult, NodeVisitor $visitor): void
 	{
-		$config = new ParserConfig([]);
+		$config = new ParserConfig(['lines' => true, 'indexes' => true, 'comments' => true]);
 		$lexer = new Lexer($config);
 		$tokens = new TokenIterator($lexer->tokenize($phpDoc));
 		$phpDocNode = $this->phpDocParser->parse($tokens);
@@ -2079,6 +2664,7 @@ public function enterNode(Node $node)
 				$node->setAttribute(Attribute::START_INDEX, null);
 				$node->setAttribute(Attribute::END_INDEX, null);
 				$node->setAttribute(Attribute::ORIGINAL_NODE, null);
+				$node->setAttribute(Attribute::COMMENTS, null);
 
 				return $node;
 			}
@@ -2273,6 +2859,17 @@ public function testPrintPhpDocNode(PhpDocNode $node, string $expectedResult): v
 		);
 	}
 
+	/**
+	 * @template TNode of Node
+	 * @param TNode $node
+	 * @return TNode
+	 */
+	public static function withComment(Node $node, string $comment): Node
+	{
+		$node->setAttribute(Attribute::COMMENTS, [new Comment($comment)]);
+		return $node;
+	}
+
 	public static function nowdoc(string $str): string
 	{
 		$lines = preg_split('/\\n/', $str);

From 3779903e441b47f8a1eec739f9d54a34d1aaa989 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 24 Mar 2025 03:45:12 +0000
Subject: [PATCH 41/45] Update metcalfc/changelog-generator action to v4.5.0

---
 .github/workflows/release.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b1a669a9..be6cad08 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -18,7 +18,7 @@ jobs:
 
       - name: Generate changelog
         id: changelog
-        uses: metcalfc/changelog-generator@v4.3.1
+        uses: metcalfc/changelog-generator@v4.5.0
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From 0a8e718713203ec86db768a5c99ce261a9810d5d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 7 Apr 2025 03:12:06 +0000
Subject: [PATCH 42/45] Update metcalfc/changelog-generator action to v4.6.2

---
 .github/workflows/release.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index be6cad08..b8c96d48 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -18,7 +18,7 @@ jobs:
 
       - name: Generate changelog
         id: changelog
-        uses: metcalfc/changelog-generator@v4.5.0
+        uses: metcalfc/changelog-generator@v4.6.2
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From 9475dab46de6b736aafcb6b9d375999723d0326d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 13 Jul 2025 08:56:51 +0200
Subject: [PATCH 43/45] Prepare 2.2.x

---
 .github/workflows/apiref.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/apiref.yml b/.github/workflows/apiref.yml
index 27d3fb92..ab0fa74e 100644
--- a/.github/workflows/apiref.yml
+++ b/.github/workflows/apiref.yml
@@ -5,7 +5,7 @@ name: "Deploy API Reference"
 on:
   push:
     branches:
-      - "2.1.x"
+      - "2.2.x"
 
 concurrency:
   group: "pages"

From 233238c89a5247d7cdd759b523112b953dbf80cf Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 13 Jul 2025 08:58:48 +0200
Subject: [PATCH 44/45] Open 2.2.x

---
 .github/workflows/apiref.yml                        | 1 +
 .github/workflows/backward-compatibility.yml        | 2 +-
 .github/workflows/build.yml                         | 2 +-
 .github/workflows/test-slevomat-coding-standard.yml | 2 +-
 README.md                                           | 2 +-
 5 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/apiref.yml b/.github/workflows/apiref.yml
index ab0fa74e..194b1f16 100644
--- a/.github/workflows/apiref.yml
+++ b/.github/workflows/apiref.yml
@@ -22,6 +22,7 @@ jobs:
           - "1.23.x"
           - "2.0.x"
           - "2.1.x"
+          - "2.2.x"
 
     steps:
       - name: "Checkout"
diff --git a/.github/workflows/backward-compatibility.yml b/.github/workflows/backward-compatibility.yml
index ecfb7fbd..77e4e399 100644
--- a/.github/workflows/backward-compatibility.yml
+++ b/.github/workflows/backward-compatibility.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "2.1.x"
+      - "2.2.x"
 
 jobs:
   backward-compatibility:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9118fafd..f20d4492 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "2.1.x"
+      - "2.2.x"
 
 jobs:
   lint:
diff --git a/.github/workflows/test-slevomat-coding-standard.yml b/.github/workflows/test-slevomat-coding-standard.yml
index 8ce55469..8767f717 100644
--- a/.github/workflows/test-slevomat-coding-standard.yml
+++ b/.github/workflows/test-slevomat-coding-standard.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "2.1.x"
+      - "2.2.x"
 
 jobs:
   tests:
diff --git a/README.md b/README.md
index 15ac9a97..794b47db 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ For the complete list of supported PHPDoc features check out PHPStan documentati
 
 * [PHPDoc Basics](https://phpstan.org/writing-php-code/phpdocs-basics) (list of PHPDoc tags)
 * [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) (list of PHPDoc types)
-* [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/2.1.x/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc.
+* [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/2.2.x/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc.
 
 This parser also supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/2.1.x/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html).
 

From b9e61a61e39e02dd90944e9115241c7f7e76bfd8 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Sun, 13 Jul 2025 08:55:11 +0200
Subject: [PATCH 45/45] Add support for `@psalm-inheritors` and
 `@phpstan-sealed` tag

---
 src/Ast/PhpDoc/PhpDocNode.php             | 11 ++++
 src/Ast/PhpDoc/SealedTagValueNode.php     | 31 +++++++++
 src/Parser/PhpDocParser.php               | 12 ++++
 src/Printer/Printer.php                   |  5 ++
 tests/PHPStan/Parser/PhpDocParserTest.php | 77 +++++++++++++++++++++++
 5 files changed, 136 insertions(+)
 create mode 100644 src/Ast/PhpDoc/SealedTagValueNode.php

diff --git a/src/Ast/PhpDoc/PhpDocNode.php b/src/Ast/PhpDoc/PhpDocNode.php
index 6abad3d0..a3dd3e4c 100644
--- a/src/Ast/PhpDoc/PhpDocNode.php
+++ b/src/Ast/PhpDoc/PhpDocNode.php
@@ -231,6 +231,17 @@ public function getRequireImplementsTagValues(string $tagName = '@phpstan-requir
 		);
 	}
 
+	/**
+	 * @return SealedTagValueNode[]
+	 */
+	public function getSealedTagValues(string $tagName = '@phpstan-sealed'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static fn (PhpDocTagValueNode $value): bool => $value instanceof SealedTagValueNode,
+		);
+	}
+
 	/**
 	 * @return DeprecatedTagValueNode[]
 	 */
diff --git a/src/Ast/PhpDoc/SealedTagValueNode.php b/src/Ast/PhpDoc/SealedTagValueNode.php
new file mode 100644
index 00000000..230cf3e8
--- /dev/null
+++ b/src/Ast/PhpDoc/SealedTagValueNode.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class SealedTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	public TypeNode $type;
+
+	/** @var string (may be empty) */
+	public string $description;
+
+	public function __construct(TypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php
index 559d8fd5..f36b9136 100644
--- a/src/Parser/PhpDocParser.php
+++ b/src/Parser/PhpDocParser.php
@@ -403,6 +403,11 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
 					$tagValue = $this->parseRequireImplementsTagValue($tokens);
 					break;
 
+				case '@psalm-inheritors':
+				case '@phpstan-sealed':
+					$tagValue = $this->parseSealedTagValue($tokens);
+					break;
+
 				case '@deprecated':
 					$tagValue = $this->parseDeprecatedTagValue($tokens);
 					break;
@@ -933,6 +938,13 @@ private function parseRequireImplementsTagValue(TokenIterator $tokens): Ast\PhpD
 		return new Ast\PhpDoc\RequireImplementsTagValueNode($type, $description);
 	}
 
+	private function parseSealedTagValue(TokenIterator $tokens): Ast\PhpDoc\SealedTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
+		return new Ast\PhpDoc\SealedTagValueNode($type, $description);
+	}
+
 	private function parseDeprecatedTagValue(TokenIterator $tokens): Ast\PhpDoc\DeprecatedTagValueNode
 	{
 		$description = $this->parseOptionalDescription($tokens, false);
diff --git a/src/Printer/Printer.php b/src/Printer/Printer.php
index af920f8d..36f6ebe1 100644
--- a/src/Printer/Printer.php
+++ b/src/Printer/Printer.php
@@ -36,6 +36,7 @@
 use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\SealedTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\SelfOutTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
@@ -327,6 +328,10 @@ private function printTagValue(PhpDocTagValueNode $node): string
 			$type = $this->printType($node->type);
 			return trim("{$type} {$node->description}");
 		}
+		if ($node instanceof SealedTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
 		if ($node instanceof ParamOutTagValueNode) {
 			$type = $this->printType($node->type);
 			return trim("{$type} {$node->parameterName} {$node->description}");
diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php
index abe69b8e..b2857b2b 100644
--- a/tests/PHPStan/Parser/PhpDocParserTest.php
+++ b/tests/PHPStan/Parser/PhpDocParserTest.php
@@ -42,6 +42,7 @@
 use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\SealedTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\SelfOutTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
 use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
@@ -108,6 +109,7 @@ protected function setUp(): void
 	 * @dataProvider provideMixinTagsData
 	 * @dataProvider provideRequireExtendsTagsData
 	 * @dataProvider provideRequireImplementsTagsData
+	 * @dataProvider provideSealedTagsData
 	 * @dataProvider provideDeprecatedTagsData
 	 * @dataProvider providePropertyTagsData
 	 * @dataProvider provideMethodTagsData
@@ -2210,6 +2212,81 @@ public function provideRequireImplementsTagsData(): Iterator
 		];
 	}
 
+	public function provideSealedTagsData(): Iterator
+	{
+		yield [
+			'OK without description',
+			'/** @phpstan-sealed Foo|Bar */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@phpstan-sealed',
+					new SealedTagValueNode(
+						new UnionTypeNode([
+							new IdentifierTypeNode('Foo'),
+							new IdentifierTypeNode('Bar'),
+						]),
+						'',
+					),
+				),
+			]),
+		];
+
+		yield [
+			'OK with description',
+			'/** @phpstan-sealed Foo|Bar optional description */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@phpstan-sealed',
+					new SealedTagValueNode(
+						new UnionTypeNode([
+							new IdentifierTypeNode('Foo'),
+							new IdentifierTypeNode('Bar'),
+						]),
+						'optional description',
+					),
+				),
+			]),
+		];
+
+		yield [
+			'OK with psalm-prefix description',
+			'/** @psalm-inheritors Foo|Bar optional description */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@psalm-inheritors',
+					new SealedTagValueNode(
+						new UnionTypeNode([
+							new IdentifierTypeNode('Foo'),
+							new IdentifierTypeNode('Bar'),
+						]),
+						'optional description',
+					),
+				),
+			]),
+		];
+
+		yield [
+			'invalid without type and description',
+			'/** @phpstan-sealed */',
+			new PhpDocNode([
+				new PhpDocTagNode(
+					'@phpstan-sealed',
+					new InvalidTagValueNode(
+						'',
+						new ParserException(
+							'*/',
+							Lexer::TOKEN_CLOSE_PHPDOC,
+							20,
+							Lexer::TOKEN_IDENTIFIER,
+							null,
+							1,
+						),
+					),
+				),
+			]),
+		];
+	}
+
 	public function provideDeprecatedTagsData(): Iterator
 	{
 		yield [
