From b34aef6eb09af21815aeda99531edfd44e752803 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= <looky.msc@gmail.com>
Date: Tue, 29 Oct 2019 21:08:07 +0100
Subject: [PATCH 001/297] Improved compatibility

---
 phpcs.xml                                        | 1 +
 phpstan.neon                                     | 4 ----
 tests/Type/Symfony/VariableTypeReportingRule.php | 8 +++-----
 3 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/phpcs.xml b/phpcs.xml
index 2ff108e3..2a8022a0 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -6,6 +6,7 @@
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation"/>
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions"/>
         <exclude name="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly"/>
+        <exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableReturnTypeHintSpecification"/>
         <exclude name="Consistence.Exceptions.ExceptionDeclaration"/>
         <exclude name="Squiz.Commenting.FunctionComment.MissingParamTag"/>
     </rule>
diff --git a/phpstan.neon b/phpstan.neon
index 2d9dae59..bb15be60 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -16,7 +16,3 @@ parameters:
 		- tests/*/request_get_content.php
 		- tests/*/request_get_session.php
 		- tests/*/serializer.php
-	ignoreErrors:
-		-
-			message: '#should be contravariant with#'
-			path: tests/Type/Symfony/VariableTypeReportingRule.php
diff --git a/tests/Type/Symfony/VariableTypeReportingRule.php b/tests/Type/Symfony/VariableTypeReportingRule.php
index 656259d1..cbac8c06 100644
--- a/tests/Type/Symfony/VariableTypeReportingRule.php
+++ b/tests/Type/Symfony/VariableTypeReportingRule.php
@@ -8,6 +8,9 @@
 use PHPStan\Rules\Rule;
 use PHPStan\Type\VerbosityLevel;
 
+/**
+ * @implements \PHPStan\Rules\Rule<\PhpParser\Node\Expr\Variable>
+ */
 final class VariableTypeReportingRule implements Rule
 {
 
@@ -16,11 +19,6 @@ public function getNodeType(): string
 		return Variable::class;
 	}
 
-	/**
-	 * @param \PhpParser\Node\Expr\Variable $node
-	 * @param \PHPStan\Analyser\Scope $scope
-	 * @return string[] errors
-	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
 		if (!is_string($node->name)) {

From 295a8a08ee823be79c1e327ec8747b87e82d34e8 Mon Sep 17 00:00:00 2001
From: Jan Nedbal <janedbal@gmail.com>
Date: Mon, 4 Nov 2019 10:03:15 +0100
Subject: [PATCH 002/297] XmlServiceMapFactory: minor error messages
 improvement

---
 src/Symfony/XmlServiceMapFactory.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 0f575891..47aa1bc9 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -26,12 +26,12 @@ public function create(): ServiceMap
 
 		$fileContents = file_get_contents($this->containerXml);
 		if ($fileContents === false) {
-			throw new XmlContainerNotExistsException(sprintf('Container %s does not exist or cannot be parsed', $this->containerXml));
+			throw new XmlContainerNotExistsException(sprintf('Container %s does not exist', $this->containerXml));
 		}
 
 		$xml = @simplexml_load_string($fileContents);
 		if ($xml === false) {
-			throw new XmlContainerNotExistsException(sprintf('Container %s does not exist or cannot be parsed', $this->containerXml));
+			throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml));
 		}
 
 		/** @var \PHPStan\Symfony\Service[] $services */

From aa3f563ef2e361cea08d28fce6a8b9c8ce369aae Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 17 Nov 2019 22:53:13 +0100
Subject: [PATCH 003/297] Fixed compatibility with prefixed PHPStan PHAR in
 NeonTest

---
 composer.json                            |  1 -
 tests/Symfony/NeonTest.php               | 57 +++++++++---------------
 tests/Symfony/config.neon                | 16 +------
 tests/Type/Symfony/ExtensionTestCase.php |  2 +
 4 files changed, 23 insertions(+), 53 deletions(-)

diff --git a/composer.json b/composer.json
index 8320f80f..18a52540 100644
--- a/composer.json
+++ b/composer.json
@@ -33,7 +33,6 @@
     "consistence/coding-standard": "^3.0.1",
     "jakub-onderka/php-parallel-lint": "^1.0",
     "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
-    "nette/di": "^3.0-stable",
     "phpunit/phpunit": "^7.0",
     "phing/phing": "^2.16.0",
     "phpstan/phpstan-strict-rules": "^0.12",
diff --git a/tests/Symfony/NeonTest.php b/tests/Symfony/NeonTest.php
index 50d80a26..ede269e4 100644
--- a/tests/Symfony/NeonTest.php
+++ b/tests/Symfony/NeonTest.php
@@ -2,53 +2,36 @@
 
 namespace PHPStan\Symfony;
 
-use Nette\DI\Compiler;
-use Nette\DI\ContainerLoader;
-use PHPStan\DependencyInjection\ParametersSchemaExtension;
-use PHPStan\DependencyInjection\RulesExtension;
+use PHPStan\DependencyInjection\ContainerFactory;
 use PHPUnit\Framework\TestCase;
-use function sprintf;
-use function unlink;
 
 final class NeonTest extends TestCase
 {
 
 	public function testExtensionNeon(): void
 	{
-		$key = '';
 		$tmpDir = __DIR__ . '/../tmp';
-		$loader = new ContainerLoader($tmpDir, true);
-		$generatedContainer = sprintf('%s/%s.php', $tmpDir, $loader->getClassName($key));
-
-		@unlink($generatedContainer);
-		self::assertFileNotExists($generatedContainer);
-
-		$class = $loader->load(function (Compiler $compiler): void {
-			$compiler->addExtension('rules', new RulesExtension());
-			$compiler->addExtension('parametersSchema', new ParametersSchemaExtension());
-			$compiler->addConfig(['parameters' => ['rootDir' => __DIR__]]);
-			$compiler->loadConfig(__DIR__ . '/../../extension.neon');
-			$compiler->loadConfig(__DIR__ . '/../../rules.neon');
-			$compiler->loadConfig(__DIR__ . '/config.neon');
-		}, $key);
-		/** @var \Nette\DI\Container $container */
-		$container = new $class();
+		$containerFactory = new ContainerFactory(__DIR__);
+		$container = $containerFactory->create($tmpDir, [
+			__DIR__ . '/../../extension.neon',
+			__DIR__ . '/../../rules.neon',
+			__DIR__ . '/config.neon',
 
+		], []);
 		$parameters = $container->getParameters();
-		unset($parameters['__parametersSchema']);
-
-		self::assertSame([
-			'rootDir' => __DIR__,
-			'symfony' => [
-				'container_xml_path' => __DIR__ . '/container.xml',
-				'constant_hassers' => true,
-				'console_application_loader' => null,
-			],
-		], $parameters);
-
-		self::assertCount(6, $container->findByTag('phpstan.rules.rule'));
-		self::assertCount(12, $container->findByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
-		self::assertCount(6, $container->findByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
+		$this->assertArrayHasKey('rootDir', $parameters);
+		$this->assertSame(realpath(__DIR__  . '/../../vendor/phpstan/phpstan'), $parameters['rootDir']);
+
+		$this->assertArrayHasKey('symfony', $parameters);
+		$this->assertSame([
+			'container_xml_path' => __DIR__ . '/container.xml',
+			'constant_hassers' => true,
+			'console_application_loader' => null,
+		] , $parameters['symfony']);
+
+		self::assertCount(6, $container->getServicesByTag('phpstan.rules.rule'));
+		self::assertCount(15, $container->getServicesByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
+		self::assertCount(6, $container->getServicesByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
 		self::assertInstanceOf(ServiceMap::class, $container->getByType(ServiceMap::class));
 	}
 
diff --git a/tests/Symfony/config.neon b/tests/Symfony/config.neon
index 9d23ce38..e4f42c64 100644
--- a/tests/Symfony/config.neon
+++ b/tests/Symfony/config.neon
@@ -1,17 +1,3 @@
 parameters:
 	symfony:
-		container_xml_path: %rootDir%/container.xml
-
-services:
-	- PhpParser\PrettyPrinter\Standard
-
-	-
-		class: PHPStan\DependencyInjection\Container
-		factory: PHPStan\DependencyInjection\Nette\NetteContainer
-
-	broker:
-		class: PHPStan\Broker\Broker
-		factory: @brokerFactory::create
-
-	brokerFactory:
-		class: PHPStan\Broker\BrokerFactory
+		container_xml_path: container.xml
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index baec9fb9..dd1232fe 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -11,6 +11,7 @@
 use PHPStan\Cache\Cache;
 use PHPStan\File\FileHelper;
 use PHPStan\Node\VirtualNode;
+use PHPStan\PhpDoc\PhpDocNodeResolver;
 use PHPStan\PhpDoc\PhpDocStringResolver;
 use PHPStan\PhpDoc\TypeNodeResolver;
 use PHPStan\Testing\TestCase;
@@ -43,6 +44,7 @@ protected function processFile(
 			new FileTypeMapper(
 				$parser,
 				$phpDocStringResolver,
+				self::getContainer()->getByType(PhpDocNodeResolver::class),
 				$this->createMock(Cache::class),
 				$this->createMock(AnonymousClassNameHelper::class),
 				$typeNodeResolver

From 2ca96eef43e4b604d2c8ee9b76f5c7078516e941 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 17 Nov 2019 22:57:07 +0100
Subject: [PATCH 004/297] Fixed compatibility with latest PHPStan

---
 phpstan.neon                                                   | 2 +-
 src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php     | 3 +++
 src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php     | 3 +++
 src/Rules/Symfony/InvalidArgumentDefaultValueRule.php          | 3 +++
 src/Rules/Symfony/InvalidOptionDefaultValueRule.php            | 3 +++
 src/Rules/Symfony/UndefinedArgumentRule.php                    | 3 +++
 src/Rules/Symfony/UndefinedOptionRule.php                      | 3 +++
 .../Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php   | 3 +++
 .../Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php | 3 +++
 .../Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php   | 3 +++
 .../Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php | 3 +++
 tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php    | 3 +++
 tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php      | 3 +++
 tests/Rules/Symfony/UndefinedArgumentRuleTest.php              | 3 +++
 tests/Rules/Symfony/UndefinedOptionRuleTest.php                | 3 +++
 tests/Symfony/DefaultServiceMapTest.php                        | 3 +++
 tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php         | 3 +++
 tests/Type/Symfony/ExtensionTestCase.php                       | 3 +--
 tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php | 3 +++
 ...InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php | 3 +++
 .../InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php  | 3 +++
 tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php      | 3 +++
 22 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/phpstan.neon b/phpstan.neon
index bb15be60..025a64af 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -3,7 +3,7 @@ includes:
 	- vendor/phpstan/phpstan-phpunit/extension.neon
 	- vendor/phpstan/phpstan-phpunit/rules.neon
 	- vendor/phpstan/phpstan-strict-rules/rules.neon
-	- vendor/phpstan/phpstan/conf/bleedingEdge.neon
+	- phar://phpstan.phar/conf/bleedingEdge.neon
 
 parameters:
 	excludes_analyse:
diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 9e4d6aa5..7982b4b1 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -11,6 +11,9 @@
 use PHPStan\Type\ObjectType;
 use function sprintf;
 
+/**
+ * @implements Rule<MethodCall>
+ */
 final class ContainerInterfacePrivateServiceRule implements Rule
 {
 
diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index 0e147982..1a0a4414 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -12,6 +12,9 @@
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
 
+/**
+ * @implements Rule<MethodCall>
+ */
 final class ContainerInterfaceUnknownServiceRule implements Rule
 {
 
diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index 9da3d536..b735dbfe 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -18,6 +18,9 @@
 use PHPStan\Type\VerbosityLevel;
 use function sprintf;
 
+/**
+ * @implements Rule<MethodCall>
+ */
 final class InvalidArgumentDefaultValueRule implements Rule
 {
 
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index ddfd576f..42d40ee0 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -21,6 +21,9 @@
 use PHPStan\Type\VerbosityLevel;
 use function sprintf;
 
+/**
+ * @implements Rule<MethodCall>
+ */
 final class InvalidOptionDefaultValueRule implements Rule
 {
 
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index 9c0f55f7..e6de90b7 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -16,6 +16,9 @@
 use function count;
 use function sprintf;
 
+/**
+ * @implements Rule<MethodCall>
+ */
 final class UndefinedArgumentRule implements Rule
 {
 
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index f8b04041..ee041929 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -16,6 +16,9 @@
 use function count;
 use function sprintf;
 
+/**
+ * @implements Rule<MethodCall>
+ */
 final class UndefinedOptionRule implements Rule
 {
 
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
index db79af6c..1d4d5436 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -6,6 +6,9 @@
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 
+/**
+ * @extends RuleTestCase<ContainerInterfacePrivateServiceRule>
+ */
 final class ContainerInterfacePrivateServiceRuleFakeTest extends RuleTestCase
 {
 
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index abe71fed..3704818e 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -6,6 +6,9 @@
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 
+/**
+ * @extends RuleTestCase<ContainerInterfacePrivateServiceRule>
+ */
 final class ContainerInterfacePrivateServiceRuleTest extends RuleTestCase
 {
 
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index cc0f2977..b9a22a5c 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -9,6 +9,9 @@
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 
+/**
+ * @extends RuleTestCase<ContainerInterfaceUnknownServiceRule>
+ */
 final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase
 {
 
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 2fcc32ed..06b06f9e 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -9,6 +9,9 @@
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 
+/**
+ * @extends RuleTestCase<ContainerInterfaceUnknownServiceRule>
+ */
 final class ContainerInterfaceUnknownServiceRuleTest extends RuleTestCase
 {
 
diff --git a/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
index cc06fc0b..5e4f2ee6 100644
--- a/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
+++ b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
@@ -5,6 +5,9 @@
 use PHPStan\Rules\Rule;
 use PHPStan\Testing\RuleTestCase;
 
+/**
+ * @extends RuleTestCase<InvalidArgumentDefaultValueRule>
+ */
 final class InvalidArgumentDefaultValueRuleTest extends RuleTestCase
 {
 
diff --git a/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php b/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php
index a8fb119b..3e5e03d4 100644
--- a/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php
+++ b/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php
@@ -5,6 +5,9 @@
 use PHPStan\Rules\Rule;
 use PHPStan\Testing\RuleTestCase;
 
+/**
+ * @extends RuleTestCase<InvalidOptionDefaultValueRule>
+ */
 final class InvalidOptionDefaultValueRuleTest extends RuleTestCase
 {
 
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
index 515c4ce2..22fee153 100644
--- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -8,6 +8,9 @@
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension;
 
+/**
+ * @extends RuleTestCase<UndefinedArgumentRule>
+ */
 final class UndefinedArgumentRuleTest extends RuleTestCase
 {
 
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
index 6ab65985..1ca949de 100644
--- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -8,6 +8,9 @@
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\Symfony\OptionTypeSpecifyingExtension;
 
+/**
+ * @extends RuleTestCase<UndefinedOptionRule>
+ */
 final class UndefinedOptionRuleTest extends RuleTestCase
 {
 
diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php
index 5a1c1801..35d6b35d 100644
--- a/tests/Symfony/DefaultServiceMapTest.php
+++ b/tests/Symfony/DefaultServiceMapTest.php
@@ -25,6 +25,9 @@ public function testGetContainerEscapedPath(): void
 		self::assertNotNull($serviceMap->getService('withClass'));
 	}
 
+	/**
+	 * @return \Iterator<mixed>
+	 */
 	public function getServiceProvider(): Iterator
 	{
 		yield [
diff --git a/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php b/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
index 0a3f9a2b..2e7d7266 100644
--- a/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
@@ -22,6 +22,9 @@ public function testAll(string $expression, string $type): void
 		);
 	}
 
+	/**
+	 * @return \Iterator<mixed>
+	 */
 	public function getProvider(): Iterator
 	{
 		yield ['$test1', 'array<' . ReceivedStamp::class . '>'];
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index dd1232fe..82f52ca3 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -46,8 +46,7 @@ protected function processFile(
 				$phpDocStringResolver,
 				self::getContainer()->getByType(PhpDocNodeResolver::class),
 				$this->createMock(Cache::class),
-				$this->createMock(AnonymousClassNameHelper::class),
-				$typeNodeResolver
+				$this->createMock(AnonymousClassNameHelper::class)
 			),
 			$fileHelper,
 			$typeSpecifier,
diff --git a/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
index 084f4aca..f4b7d4cb 100644
--- a/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
@@ -20,6 +20,9 @@ public function testGet(string $expression, string $type): void
 		);
 	}
 
+	/**
+	 * @return \Iterator<array{string, string}>
+	 */
 	public function getProvider(): Iterator
 	{
 		yield ['$test1', 'string|null'];
diff --git a/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
index 216321f4..9689018e 100644
--- a/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
@@ -21,6 +21,9 @@ public function testArgumentTypes(string $expression, string $type): void
 		);
 	}
 
+	/**
+	 * @return \Iterator<array{string, string}>
+	 */
 	public function argumentTypesProvider(): Iterator
 	{
 		yield ['$base', 'string|null'];
diff --git a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
index 6e949ce6..c95ca57f 100644
--- a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
@@ -21,6 +21,9 @@ public function testArgumentTypes(string $expression, string $type): void
 		);
 	}
 
+	/**
+	 * @return \Iterator<array{string, string}>
+	 */
 	public function argumentTypesProvider(): Iterator
 	{
 		yield ['$a', 'bool'];
diff --git a/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php b/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
index 5d266152..1554087e 100644
--- a/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
+++ b/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
@@ -7,6 +7,9 @@
 use PHPStan\Type\MethodTypeSpecifyingExtension;
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
 
+/**
+ * @extends RuleTestCase<VariableTypeReportingRule>
+ */
 final class RequestTypeSpecifyingExtensionTest extends RuleTestCase
 {
 

From f7a764a2af12e6c7ed87b515d407621bfb7cda51 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 17 Nov 2019 23:19:28 +0100
Subject: [PATCH 005/297] Build level 8

---
 build.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.xml b/build.xml
index 4c3e8284..baf66775 100644
--- a/build.xml
+++ b/build.xml
@@ -86,7 +86,7 @@
         >
             <arg value="analyse"/>
             <arg value="-l"/>
-            <arg value="7"/>
+            <arg value="8"/>
             <arg value="-c"/>
             <arg path="phpstan.neon"/>
             <arg path="src"/>

From 9bd4e0275da3f18b8acba75f8ce98cb2494b2392 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 18 Nov 2019 06:27:35 +0100
Subject: [PATCH 006/297] Fix CS

---
 tests/Symfony/NeonTest.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/Symfony/NeonTest.php b/tests/Symfony/NeonTest.php
index ede269e4..6b727046 100644
--- a/tests/Symfony/NeonTest.php
+++ b/tests/Symfony/NeonTest.php
@@ -20,14 +20,14 @@ public function testExtensionNeon(): void
 		], []);
 		$parameters = $container->getParameters();
 		$this->assertArrayHasKey('rootDir', $parameters);
-		$this->assertSame(realpath(__DIR__  . '/../../vendor/phpstan/phpstan'), $parameters['rootDir']);
+		$this->assertSame(realpath(__DIR__ . '/../../vendor/phpstan/phpstan'), $parameters['rootDir']);
 
 		$this->assertArrayHasKey('symfony', $parameters);
 		$this->assertSame([
 			'container_xml_path' => __DIR__ . '/container.xml',
 			'constant_hassers' => true,
 			'console_application_loader' => null,
-		] , $parameters['symfony']);
+		], $parameters['symfony']);
 
 		self::assertCount(6, $container->getServicesByTag('phpstan.rules.rule'));
 		self::assertCount(15, $container->getServicesByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));

From f536c8fbe1d01c0609edf895121be9de60ccbc72 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 18 Nov 2019 06:44:17 +0100
Subject: [PATCH 007/297] Fix build

---
 tests/Symfony/NeonTest.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/Symfony/NeonTest.php b/tests/Symfony/NeonTest.php
index 6b727046..a77e758b 100644
--- a/tests/Symfony/NeonTest.php
+++ b/tests/Symfony/NeonTest.php
@@ -19,11 +19,11 @@ public function testExtensionNeon(): void
 
 		], []);
 		$parameters = $container->getParameters();
-		$this->assertArrayHasKey('rootDir', $parameters);
-		$this->assertSame(realpath(__DIR__ . '/../../vendor/phpstan/phpstan'), $parameters['rootDir']);
+		self::assertArrayHasKey('rootDir', $parameters);
+		self::assertSame(realpath(__DIR__ . '/../../vendor/phpstan/phpstan'), $parameters['rootDir']);
 
-		$this->assertArrayHasKey('symfony', $parameters);
-		$this->assertSame([
+		self::assertArrayHasKey('symfony', $parameters);
+		self::assertSame([
 			'container_xml_path' => __DIR__ . '/container.xml',
 			'constant_hassers' => true,
 			'console_application_loader' => null,

From a2142062b43bd92c8b49f203f5e515ea6dc27fe7 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 18 Nov 2019 07:11:18 +0100
Subject: [PATCH 008/297] Remove ComposerRequireChecker

---
 .travis.yml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index ab4b6e40..cbb484db 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,6 +12,3 @@ before_script:
   - if [ "$dependencies" = "highest" ]; then composer update --no-interaction; fi;
 script:
   - vendor/bin/phing
-  - >
-    wget https://github.com/maglnet/ComposerRequireChecker/releases/download/1.1.0/composer-require-checker.phar
-    && php composer-require-checker.phar check composer.json

From efe5f29d602d2c534afa8d85aeca99b3ac4e3d16 Mon Sep 17 00:00:00 2001
From: Martin Hujer <mail@martinhujer.cz>
Date: Mon, 18 Nov 2019 14:58:12 +0100
Subject: [PATCH 009/297] NeonTest: fix tests on Windows

---
 tests/Symfony/NeonTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/Symfony/NeonTest.php b/tests/Symfony/NeonTest.php
index a77e758b..072ec497 100644
--- a/tests/Symfony/NeonTest.php
+++ b/tests/Symfony/NeonTest.php
@@ -24,7 +24,7 @@ public function testExtensionNeon(): void
 
 		self::assertArrayHasKey('symfony', $parameters);
 		self::assertSame([
-			'container_xml_path' => __DIR__ . '/container.xml',
+			'container_xml_path' => __DIR__ . DIRECTORY_SEPARATOR . 'container.xml',
 			'constant_hassers' => true,
 			'console_application_loader' => null,
 		], $parameters['symfony']);

From 7ad5b2c473ff34e781272bff17ac5992c4bd540e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Fejfar?= <tomas.fejfar@gmail.com>
Date: Fri, 1 Nov 2019 15:00:32 +0100
Subject: [PATCH 010/297] Tests

---
 ...nerInterfacePrivateServiceRuleFakeTest.php | 10 +++++
 ...ntainerInterfacePrivateServiceRuleTest.php | 15 +++++++
 ...nerInterfaceUnknownServiceRuleFakeTest.php | 10 +++++
 ...ntainerInterfaceUnknownServiceRuleTest.php | 15 +++++++
 .../Symfony/ExampleAbstractController.php     | 42 +++++++++++++++++++
 5 files changed, 92 insertions(+)
 create mode 100644 tests/Rules/Symfony/ExampleAbstractController.php

diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
index 1d4d5436..6aa4ae47 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -27,6 +27,16 @@ public function testGetPrivateService(): void
 		);
 	}
 
+	public function testGetPrivateServiceInAbstractController(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleAbstractController.php',
+			],
+			[]
+		);
+	}
+
 	public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 	{
 		if (!interface_exists('Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface')) {
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index 3704818e..8c17f531 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -32,6 +32,21 @@ public function testGetPrivateService(): void
 		);
 	}
 
+	public function testGetPrivateServiceInAbstractController(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleAbstractController.php',
+			],
+			[
+				[
+					'Service "private" is private.',
+					12,
+				],
+			]
+		);
+	}
+
 	public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 	{
 		if (!interface_exists('Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface')) {
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index b9a22a5c..87404430 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -40,4 +40,14 @@ public function testGetPrivateService(): void
 		);
 	}
 
+	public function testGetPrivateServiceInAbstractController(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleAbstractController.php',
+			],
+			[]
+		);
+	}
+
 }
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 06b06f9e..9869348f 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -45,4 +45,19 @@ public function testGetPrivateService(): void
 		);
 	}
 
+	public function testGetPrivateServiceInAbstractController(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleAbstractController.php',
+			],
+			[
+				[
+					'Service "unknown" is not registered in the container.',
+					24,
+				],
+			]
+		);
+	}
+
 }
diff --git a/tests/Rules/Symfony/ExampleAbstractController.php b/tests/Rules/Symfony/ExampleAbstractController.php
new file mode 100644
index 00000000..4f0d2abd
--- /dev/null
+++ b/tests/Rules/Symfony/ExampleAbstractController.php
@@ -0,0 +1,42 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+
+final class ExampleAbstractController extends AbstractController
+{
+
+	public function privateService(): void
+	{
+		$this->get('private');
+	}
+
+	public function privateServiceInTestContainer(): void
+	{
+		/** @var \Symfony\Bundle\FrameworkBundle\Test\TestContainer $container */
+		$container = doFoo();
+		$container->get('private');
+	}
+
+	public function unknownService(): void
+	{
+		$this->get('unknown');
+	}
+
+	public function unknownGuardedServiceInsideContext(): void
+	{
+		if ($this->has('unknown')) { // phpcs:ignore
+			$this->get('unknown');
+		}
+	}
+
+	public function unknownGuardedServiceOutsideOfContext(): void
+	{
+		if (!$this->has('unknown')) {
+			return;
+		}
+		$this->get('unknown');
+	}
+
+}

From 231344337f10001b06da475591a0fdccfd3cc887 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Fejfar?= <tomas.fejfar@gmail.com>
Date: Mon, 28 Oct 2019 19:14:50 +0100
Subject: [PATCH 011/297] Fix for Psr\Container\ContainerInterface type

---
 extension.neon                                            | 6 ++++++
 .../Symfony/ContainerInterfacePrivateServiceRule.php      | 8 +++++++-
 .../Symfony/ContainerInterfaceUnknownServiceRule.php      | 8 +++++++-
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/extension.neon b/extension.neon
index d9f9d5be..114ea41d 100644
--- a/extension.neon
+++ b/extension.neon
@@ -28,6 +28,9 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+	-
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface, %symfony.constant_hassers%)
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
 		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, %symfony.constant_hassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
@@ -39,6 +42,9 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(Symfony\Component\DependencyInjection\ContainerInterface)
 		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
+	-
+		factory: PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(Psr\Container\ContainerInterface)
+		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
 	-
 		factory: PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller)
 		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 7982b4b1..f97633c0 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -61,7 +61,13 @@ public function processNode(Node $node, Scope $scope): array
 		$isControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller'))->isSuperTypeOf($argType);
 		$isAbstractControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\AbstractController'))->isSuperTypeOf($argType);
 		$isContainerType = (new ObjectType('Symfony\Component\DependencyInjection\ContainerInterface'))->isSuperTypeOf($argType);
-		if (!$isControllerType->yes() && !$isAbstractControllerType->yes() && !$isContainerType->yes()) {
+		$isPsrContainerType = (new ObjectType('Psr\Container\ContainerInterface'))->isSuperTypeOf($argType);
+		if (
+			!$isControllerType->yes()
+			&& !$isAbstractControllerType->yes()
+			&& !$isContainerType->yes()
+			&& !$isPsrContainerType->yes()
+		) {
 			return [];
 		}
 
diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index 1a0a4414..2719bb4e 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -58,7 +58,13 @@ public function processNode(Node $node, Scope $scope): array
 		$isControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller'))->isSuperTypeOf($argType);
 		$isAbstractControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\AbstractController'))->isSuperTypeOf($argType);
 		$isContainerType = (new ObjectType('Symfony\Component\DependencyInjection\ContainerInterface'))->isSuperTypeOf($argType);
-		if (!$isControllerType->yes() && !$isAbstractControllerType->yes() && !$isContainerType->yes()) {
+		$isPsrContainerType = (new ObjectType('Psr\Container\ContainerInterface'))->isSuperTypeOf($argType);
+		if (
+			!$isControllerType->yes()
+			&& !$isAbstractControllerType->yes()
+			&& !$isContainerType->yes()
+			&& !$isPsrContainerType->yes()
+		) {
 			return [];
 		}
 

From 710cc632f06c0d710d53a5189573c17bc92f6411 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= <looky.msc@gmail.com>
Date: Thu, 21 Nov 2019 09:47:10 +0100
Subject: [PATCH 012/297] Fix tests

---
 composer.json                                     |  8 ++++----
 .../ContainerInterfacePrivateServiceRuleTest.php  | 15 ---------------
 .../ContainerInterfaceUnknownServiceRuleTest.php  |  2 ++
 tests/Symfony/NeonTest.php                        |  4 ++--
 .../RequestTypeSpecifyingExtensionTest.php        | 13 ++++++++++++-
 5 files changed, 20 insertions(+), 22 deletions(-)

diff --git a/composer.json b/composer.json
index 18a52540..58cd87f6 100644
--- a/composer.json
+++ b/composer.json
@@ -38,12 +38,12 @@
     "phpstan/phpstan-strict-rules": "^0.12",
     "slevomat/coding-standard": "^4.5.2",
     "phpstan/phpstan-phpunit": "^0.12",
-    "symfony/framework-bundle": "^3.0 || ^4.0",
+    "symfony/framework-bundle": "^4.0",
     "squizlabs/php_codesniffer": "^3.3.2",
-    "symfony/serializer": "^3.0 || ^4.0",
+    "symfony/serializer": "^4.0",
     "symfony/messenger": "^4.2",
-    "symfony/console": "^3.0 || ^4.0",
-    "symfony/http-foundation": "^3.0 || ^4.0"
+    "symfony/console": "^4.0",
+    "symfony/http-foundation": "^4.0"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index 8c17f531..3704818e 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -32,21 +32,6 @@ public function testGetPrivateService(): void
 		);
 	}
 
-	public function testGetPrivateServiceInAbstractController(): void
-	{
-		$this->analyse(
-			[
-				__DIR__ . '/ExampleAbstractController.php',
-			],
-			[
-				[
-					'Service "private" is private.',
-					12,
-				],
-			]
-		);
-	}
-
 	public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 	{
 		if (!interface_exists('Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface')) {
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 9869348f..776812af 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -7,6 +7,7 @@
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 
 /**
@@ -27,6 +28,7 @@ protected function getMethodTypeSpecifyingExtensions(): array
 	{
 		return [
 			new ServiceTypeSpecifyingExtension(Controller::class, new Standard()),
+			new ServiceTypeSpecifyingExtension(AbstractController::class, new Standard()),
 		];
 	}
 
diff --git a/tests/Symfony/NeonTest.php b/tests/Symfony/NeonTest.php
index 072ec497..e59779ce 100644
--- a/tests/Symfony/NeonTest.php
+++ b/tests/Symfony/NeonTest.php
@@ -30,8 +30,8 @@ public function testExtensionNeon(): void
 		], $parameters['symfony']);
 
 		self::assertCount(6, $container->getServicesByTag('phpstan.rules.rule'));
-		self::assertCount(15, $container->getServicesByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
-		self::assertCount(6, $container->getServicesByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
+		self::assertCount(16, $container->getServicesByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
+		self::assertCount(7, $container->getServicesByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
 		self::assertInstanceOf(ServiceMap::class, $container->getByType(ServiceMap::class));
 	}
 
diff --git a/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php b/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
index 1554087e..ac6f80c1 100644
--- a/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
+++ b/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
@@ -5,7 +5,10 @@
 use PHPStan\Rules\Rule;
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
+use ReflectionMethod;
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use function strpos;
 
 /**
  * @extends RuleTestCase<VariableTypeReportingRule>
@@ -28,9 +31,17 @@ protected function getMethodTypeSpecifyingExtensions(): array
 
 	public function testGetSession(): void
 	{
+		$ref = new ReflectionMethod(Request::class, 'getSession');
+		$doc = (string) $ref->getDocComment();
+
+		$checkedTypeString = SessionInterface::class;
+		if (strpos($doc, '@return SessionInterface|null') !== false) {
+			$checkedTypeString .= '|null';
+		}
+
 		$this->analyse([__DIR__ . '/request_get_session.php'], [
 			[
-				'Variable $session1 is: ' . SessionInterface::class . '|null',
+				'Variable $session1 is: ' . $checkedTypeString,
 				7,
 			],
 			[

From b1660844587671c3381f5989b8b4975f6d4da0c1 Mon Sep 17 00:00:00 2001
From: darthf1 <darthf1@users.noreply.github.com>
Date: Mon, 2 Dec 2019 16:28:03 +0100
Subject: [PATCH 013/297] `container_xml_path` for Symfony 5

Added correct `container_xml_path` for Symfony 5
---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index f45b78cd..89c02600 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,8 @@ parameters:
         container_xml_path: %rootDir%/../../../var/cache/dev/srcDevDebugProjectContainer.xml
         # or with Symfony 4.2+
         container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml'
+	# or with Symfony 5+
+	container_xml_path: '%rootDir%/../../../var/cache/dev/App_KernelDevDebugContainer.xml'
 ```
 
 ## Constant hassers

From 548bd9cddbfc374ba2fa66ca915307398ba52c4c Mon Sep 17 00:00:00 2001
From: Vasek Purchart <me@vasekpurchart.cz>
Date: Sat, 7 Dec 2019 06:51:25 +0100
Subject: [PATCH 014/297] Add stubs for (some) Symfony iterable types

---
 extension.neon                 |  5 +++++
 stubs/FormBuilderInterface.php | 11 +++++++++++
 stubs/FormInterface.php        | 11 +++++++++++
 stubs/HeaderBag.php            | 16 ++++++++++++++++
 stubs/Session.php              | 16 ++++++++++++++++
 5 files changed, 59 insertions(+)
 create mode 100644 stubs/FormBuilderInterface.php
 create mode 100644 stubs/FormInterface.php
 create mode 100644 stubs/HeaderBag.php
 create mode 100644 stubs/Session.php

diff --git a/extension.neon b/extension.neon
index 114ea41d..a7fc7bf7 100644
--- a/extension.neon
+++ b/extension.neon
@@ -3,6 +3,11 @@ parameters:
 		container_xml_path: null
 		constant_hassers: true
 		console_application_loader: null
+	stubFiles:
+		- stubs/FormBuilderInterface.php
+		- stubs/FormInterface.php
+		- stubs/HeaderBag.php
+		- stubs/Session.php
 
 parametersSchema:
 	symfony: structure([
diff --git a/stubs/FormBuilderInterface.php b/stubs/FormBuilderInterface.php
new file mode 100644
index 00000000..5173bb64
--- /dev/null
+++ b/stubs/FormBuilderInterface.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+/**
+ * @extends \Traversable<int, \Symfony\Component\Form\FormBuilderInterface>
+ */
+interface FormBuilderInterface extends \Traversable
+{
+
+}
diff --git a/stubs/FormInterface.php b/stubs/FormInterface.php
new file mode 100644
index 00000000..909a121a
--- /dev/null
+++ b/stubs/FormInterface.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+/**
+ * @extends \Traversable<int, \Symfony\Component\Form\FormInterface>
+ */
+interface FormInterface extends \Traversable
+{
+
+}
diff --git a/stubs/HeaderBag.php b/stubs/HeaderBag.php
new file mode 100644
index 00000000..cc3fe89a
--- /dev/null
+++ b/stubs/HeaderBag.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * @implements \IteratorAggregate<string, string|string[]>
+ */
+class HeaderBag implements \IteratorAggregate
+{
+
+	/**
+	 * @phpstan-return \Traversable<string, string|string[]>
+	 */
+	public function getIterator() {}
+
+}
diff --git a/stubs/Session.php b/stubs/Session.php
new file mode 100644
index 00000000..25485fe6
--- /dev/null
+++ b/stubs/Session.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * @implements \IteratorAggregate<string, mixed>
+ */
+class Session implements \IteratorAggregate
+{
+
+	/**
+	 * @phpstan-return \Traversable<string, mixed>
+	 */
+	public function getIterator() {}
+
+}

From dd10fdd71f549dce851e208964f2da547efd4a09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Votruba?= <tomas.vot@gmail.com>
Date: Mon, 16 Dec 2019 14:52:03 +0100
Subject: [PATCH 015/297] README: fix spacing

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 89c02600..eecb0cf7 100644
--- a/README.md
+++ b/README.md
@@ -50,14 +50,14 @@ includes:
 
 You have to provide a path to `srcDevDebugProjectContainer.xml` or similar XML file describing your container.
 
-```
+```yaml
 parameters:
     symfony:
         container_xml_path: %rootDir%/../../../var/cache/dev/srcDevDebugProjectContainer.xml
         # or with Symfony 4.2+
         container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml'
-	# or with Symfony 5+
-	container_xml_path: '%rootDir%/../../../var/cache/dev/App_KernelDevDebugContainer.xml'
+        # or with Symfony 5+
+        container_xml_path: '%rootDir%/../../../var/cache/dev/App_KernelDevDebugContainer.xml'
 ```
 
 ## Constant hassers

From 658feae178a5a5de4a4504c2997ced2b79f8a489 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20M=C3=B6ller?= <am@localheinz.com>
Date: Fri, 20 Dec 2019 08:56:13 +0100
Subject: [PATCH 016/297] Enhancement: Run ergebnis/composer-normalize in check
 target

---
 build.xml     | 14 ++++++++++++++
 composer.json |  3 ++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/build.xml b/build.xml
index baf66775..1044c131 100644
--- a/build.xml
+++ b/build.xml
@@ -5,6 +5,7 @@
         composer,
         lint,
         cs,
+        composer-normalize-check,
         tests,
         phpstan
     "/>
@@ -20,6 +21,19 @@
         </exec>
     </target>
 
+    <target name="composer-normalize-check">
+        <exec
+                executable="composer"
+                logoutput="true"
+                passthru="true"
+                checkreturn="true"
+        >
+            <arg value="normalize"/>
+            <arg value="--ansi"/>
+            <arg value="--dry-run"/>
+        </exec>
+    </target>
+
     <target name="lint">
         <exec
                 executable="vendor/bin/parallel-lint"
diff --git a/composer.json b/composer.json
index 58cd87f6..9b518feb 100644
--- a/composer.json
+++ b/composer.json
@@ -43,7 +43,8 @@
     "symfony/serializer": "^4.0",
     "symfony/messenger": "^4.2",
     "symfony/console": "^4.0",
-    "symfony/http-foundation": "^4.0"
+    "symfony/http-foundation": "^4.0",
+    "ergebnis/composer-normalize": "^2.0.2"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From 88ec15af8c2c63457e1dfc098d64fc5056128733 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20M=C3=B6ller?= <am@localheinz.com>
Date: Fri, 20 Dec 2019 08:56:59 +0100
Subject: [PATCH 017/297] Fix: Run 'composer normalize'

---
 composer.json | 58 +++++++++++++++++++++++++++------------------------
 1 file changed, 31 insertions(+), 27 deletions(-)

diff --git a/composer.json b/composer.json
index 9b518feb..d2411d56 100644
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,9 @@
   "name": "phpstan/phpstan-symfony",
   "type": "phpstan-extension",
   "description": "Symfony Framework extensions and rules for PHPStan",
-  "license": ["MIT"],
+  "license": [
+    "MIT"
+  ],
   "authors": [
     {
       "name": "Lukáš Unger",
@@ -10,44 +12,42 @@
       "homepage": "https://lookyman.net"
     }
   ],
-  "minimum-stability": "dev",
-  "prefer-stable": true,
-  "extra": {
-    "branch-alias": {
-      "dev-master": "0.12-dev"
-    },
-    "phpstan": {
-      "includes": [
-        "extension.neon",
-        "rules.neon"
-      ]
-    }
-  },
   "require": {
     "php": "^7.1",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12",
-    "nikic/php-parser": "^4.0"
+    "nikic/php-parser": "^4.0",
+    "phpstan/phpstan": "^0.12"
+  },
+  "conflict": {
+    "symfony/framework-bundle": "<3.0"
   },
   "require-dev": {
     "consistence/coding-standard": "^3.0.1",
-    "jakub-onderka/php-parallel-lint": "^1.0",
     "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
-    "phpunit/phpunit": "^7.0",
+    "ergebnis/composer-normalize": "^2.0.2",
+    "jakub-onderka/php-parallel-lint": "^1.0",
     "phing/phing": "^2.16.0",
+    "phpstan/phpstan-phpunit": "^0.12",
     "phpstan/phpstan-strict-rules": "^0.12",
+    "phpunit/phpunit": "^7.0",
     "slevomat/coding-standard": "^4.5.2",
-    "phpstan/phpstan-phpunit": "^0.12",
-    "symfony/framework-bundle": "^4.0",
     "squizlabs/php_codesniffer": "^3.3.2",
-    "symfony/serializer": "^4.0",
-    "symfony/messenger": "^4.2",
     "symfony/console": "^4.0",
+    "symfony/framework-bundle": "^4.0",
     "symfony/http-foundation": "^4.0",
-    "ergebnis/composer-normalize": "^2.0.2"
+    "symfony/messenger": "^4.2",
+    "symfony/serializer": "^4.0"
   },
-  "conflict": {
-    "symfony/framework-bundle": "<3.0"
+  "extra": {
+    "branch-alias": {
+      "dev-master": "0.12-dev"
+    },
+    "phpstan": {
+      "includes": [
+        "extension.neon",
+        "rules.neon"
+      ]
+    }
   },
   "autoload": {
     "psr-4": {
@@ -55,6 +55,10 @@
     }
   },
   "autoload-dev": {
-    "classmap": ["tests/"]
-  }
+    "classmap": [
+      "tests/"
+    ]
+  },
+  "minimum-stability": "dev",
+  "prefer-stable": true
 }

From b5345b596787f32d5ca906a01a3b24aa5140faad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20M=C3=B6ller?= <am@localheinz.com>
Date: Fri, 20 Dec 2019 08:58:05 +0100
Subject: [PATCH 018/297] Enhancement: Keep packages sorted in composer.json

---
 composer.json | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/composer.json b/composer.json
index d2411d56..3d534bca 100644
--- a/composer.json
+++ b/composer.json
@@ -38,6 +38,9 @@
     "symfony/messenger": "^4.2",
     "symfony/serializer": "^4.0"
   },
+  "config": {
+    "sort-packages": true
+  },
   "extra": {
     "branch-alias": {
       "dev-master": "0.12-dev"

From 837369ec1d3e9909fad86a508302bbd74ee16f9f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 1 Jan 2020 18:37:26 +0100
Subject: [PATCH 019/297] Package does not depend on nikic/php-parser

---
 composer.json | 1 -
 1 file changed, 1 deletion(-)

diff --git a/composer.json b/composer.json
index 3d534bca..00d2f82f 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,6 @@
   "require": {
     "php": "^7.1",
     "ext-simplexml": "*",
-    "nikic/php-parser": "^4.0",
     "phpstan/phpstan": "^0.12"
   },
   "conflict": {

From eb4ceaa979993da504d719ffe5ab8bb44c266f15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= <looky.msc@gmail.com>
Date: Thu, 2 Jan 2020 10:18:10 +0100
Subject: [PATCH 020/297] Fix fatal error in serializer/denormalizer extensions

---
 src/Type/Symfony/SerializerDynamicReturnTypeExtension.php | 4 ++++
 tests/Type/Symfony/ExtensionTestCase.php                  | 3 ---
 tests/Type/Symfony/denormalizer.php                       | 1 +
 tests/Type/Symfony/serializer.php                         | 1 +
 4 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
index 66c1cc44..c6c237ee 100755
--- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
@@ -39,6 +39,10 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 
 	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
 	{
+		if (!isset($methodCall->args[1])) {
+			return new MixedType();
+		}
+
 		$argType = $scope->getType($methodCall->args[1]->value);
 		if (!$argType instanceof ConstantStringType) {
 			return new MixedType();
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index 82f52ca3..c0a02e93 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -13,7 +13,6 @@
 use PHPStan\Node\VirtualNode;
 use PHPStan\PhpDoc\PhpDocNodeResolver;
 use PHPStan\PhpDoc\PhpDocStringResolver;
-use PHPStan\PhpDoc\TypeNodeResolver;
 use PHPStan\Testing\TestCase;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FileTypeMapper;
@@ -36,8 +35,6 @@ protected function processFile(
 		$typeSpecifier = $this->createTypeSpecifier(new Standard(), $broker);
 		/** @var \PHPStan\PhpDoc\PhpDocStringResolver $phpDocStringResolver */
 		$phpDocStringResolver = self::getContainer()->getByType(PhpDocStringResolver::class);
-		/** @var \PHPStan\PhpDoc\TypeNodeResolver $typeNodeResolver */
-		$typeNodeResolver = self::getContainer()->getByType(TypeNodeResolver::class);
 		$resolver = new NodeScopeResolver(
 			$broker,
 			$parser,
diff --git a/tests/Type/Symfony/denormalizer.php b/tests/Type/Symfony/denormalizer.php
index 95d56676..208483b4 100644
--- a/tests/Type/Symfony/denormalizer.php
+++ b/tests/Type/Symfony/denormalizer.php
@@ -5,5 +5,6 @@
 $first = $serializer->denormalize('bar', 'Bar', 'format');
 $second = $serializer->denormalize('bar', 'Bar[]', 'format');
 $third = $serializer->denormalize('bar', 'Bar[][]', 'format');
+$fourth = $serializer->denormalize('bar');
 
 die;
diff --git a/tests/Type/Symfony/serializer.php b/tests/Type/Symfony/serializer.php
index a19065be..fc16573e 100644
--- a/tests/Type/Symfony/serializer.php
+++ b/tests/Type/Symfony/serializer.php
@@ -5,5 +5,6 @@
 $first = $serializer->deserialize('bar', 'Bar', 'format');
 $second = $serializer->deserialize('bar', 'Bar[]', 'format');
 $third = $serializer->deserialize('bar', 'Bar[][]', 'format');
+$fourth = $serializer->deserialize('bar');
 
 die;

From 98dd02cec3a213cd778898746d9f98873481a448 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= <looky.msc@gmail.com>
Date: Thu, 2 Jan 2020 11:11:42 +0100
Subject: [PATCH 021/297] Exclude test file from analysis

---
 phpstan.neon | 1 +
 1 file changed, 1 insertion(+)

diff --git a/phpstan.neon b/phpstan.neon
index 025a64af..f0eb9494 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -16,3 +16,4 @@ parameters:
 		- tests/*/request_get_content.php
 		- tests/*/request_get_session.php
 		- tests/*/serializer.php
+		- tests/*/denormalizer.php

From ce417c1e7d6e8722e50757caaa7557f32534dfe9 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 2 Jan 2020 20:40:41 +0100
Subject: [PATCH 022/297] NeonTest was just annoying

---
 phpstan.neon               |  1 +
 tests/Symfony/NeonTest.php | 38 --------------------------------------
 2 files changed, 1 insertion(+), 38 deletions(-)
 delete mode 100644 tests/Symfony/NeonTest.php

diff --git a/phpstan.neon b/phpstan.neon
index f0eb9494..987fcc27 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,5 +1,6 @@
 includes:
 	- extension.neon
+	- rules.neon
 	- vendor/phpstan/phpstan-phpunit/extension.neon
 	- vendor/phpstan/phpstan-phpunit/rules.neon
 	- vendor/phpstan/phpstan-strict-rules/rules.neon
diff --git a/tests/Symfony/NeonTest.php b/tests/Symfony/NeonTest.php
deleted file mode 100644
index e59779ce..00000000
--- a/tests/Symfony/NeonTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Symfony;
-
-use PHPStan\DependencyInjection\ContainerFactory;
-use PHPUnit\Framework\TestCase;
-
-final class NeonTest extends TestCase
-{
-
-	public function testExtensionNeon(): void
-	{
-		$tmpDir = __DIR__ . '/../tmp';
-		$containerFactory = new ContainerFactory(__DIR__);
-		$container = $containerFactory->create($tmpDir, [
-			__DIR__ . '/../../extension.neon',
-			__DIR__ . '/../../rules.neon',
-			__DIR__ . '/config.neon',
-
-		], []);
-		$parameters = $container->getParameters();
-		self::assertArrayHasKey('rootDir', $parameters);
-		self::assertSame(realpath(__DIR__ . '/../../vendor/phpstan/phpstan'), $parameters['rootDir']);
-
-		self::assertArrayHasKey('symfony', $parameters);
-		self::assertSame([
-			'container_xml_path' => __DIR__ . DIRECTORY_SEPARATOR . 'container.xml',
-			'constant_hassers' => true,
-			'console_application_loader' => null,
-		], $parameters['symfony']);
-
-		self::assertCount(6, $container->getServicesByTag('phpstan.rules.rule'));
-		self::assertCount(16, $container->getServicesByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
-		self::assertCount(7, $container->getServicesByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
-		self::assertInstanceOf(ServiceMap::class, $container->getByType(ServiceMap::class));
-	}
-
-}

From 9251db02831216a1c6baf59a365569ef2e402be8 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 5 Jan 2020 22:08:42 +0100
Subject: [PATCH 023/297] Made ConsoleApplicationResolver lazier

---
 src/Symfony/ConsoleApplicationResolver.php | 37 ++++++++++++++++++----
 1 file changed, 30 insertions(+), 7 deletions(-)

diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 9a684d17..acd09d66 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -5,6 +5,7 @@
 use PHPStan\Reflection\ClassReflection;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Type\ObjectType;
+use Symfony\Component\Console\Application;
 use function file_exists;
 use function get_class;
 use function is_readable;
@@ -12,15 +13,15 @@
 final class ConsoleApplicationResolver
 {
 
-	/** @var \Symfony\Component\Console\Application|null */
+	/** @var string|null */
+	private $consoleApplicationLoader;
+
+	/** @var \Symfony\Component\Console\Application|false|null */
 	private $consoleApplication;
 
 	public function __construct(?string $consoleApplicationLoader)
 	{
-		if ($consoleApplicationLoader === null) {
-			return;
-		}
-		$this->consoleApplication = $this->loadConsoleApplication($consoleApplicationLoader);
+		$this->consoleApplicationLoader = $consoleApplicationLoader;
 	}
 
 	/**
@@ -38,12 +39,34 @@ private function loadConsoleApplication(string $consoleApplicationLoader)
 		return require $consoleApplicationLoader;
 	}
 
+	public function getConsoleApplication(): ?Application
+	{
+		if ($this->consoleApplication === false) {
+			return null;
+		}
+
+		if ($this->consoleApplication !== null) {
+			return $this->consoleApplication;
+		}
+
+		if ($this->consoleApplicationLoader === null) {
+			$this->consoleApplication = false;
+
+			return null;
+		}
+
+		$this->consoleApplication = $this->loadConsoleApplication($this->consoleApplicationLoader);
+
+		return $this->consoleApplication;
+	}
+
 	/**
 	 * @return \Symfony\Component\Console\Command\Command[]
 	 */
 	public function findCommands(ClassReflection $classReflection): array
 	{
-		if ($this->consoleApplication === null) {
+		$consoleApplication = $this->getConsoleApplication();
+		if ($consoleApplication === null) {
 			return [];
 		}
 
@@ -53,7 +76,7 @@ public function findCommands(ClassReflection $classReflection): array
 		}
 
 		$commands = [];
-		foreach ($this->consoleApplication->all() as $name => $command) {
+		foreach ($consoleApplication->all() as $name => $command) {
 			if (!$classType->isSuperTypeOf(new ObjectType(get_class($command)))->yes()) {
 				continue;
 			}

From f793b68b4665f223620769e8c3ea4dbf40c53ce4 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 11 Jan 2020 15:52:23 +0100
Subject: [PATCH 024/297] Renamed stub files extensions to not pollute PhpStorm
 index

---
 extension.neon                                            | 8 ++++----
 ...FormBuilderInterface.php => FormBuilderInterface.stub} | 0
 stubs/{FormInterface.php => FormInterface.stub}           | 0
 stubs/{HeaderBag.php => HeaderBag.stub}                   | 0
 stubs/{Session.php => Session.stub}                       | 0
 5 files changed, 4 insertions(+), 4 deletions(-)
 rename stubs/{FormBuilderInterface.php => FormBuilderInterface.stub} (100%)
 rename stubs/{FormInterface.php => FormInterface.stub} (100%)
 rename stubs/{HeaderBag.php => HeaderBag.stub} (100%)
 rename stubs/{Session.php => Session.stub} (100%)

diff --git a/extension.neon b/extension.neon
index a7fc7bf7..5f6d5e37 100644
--- a/extension.neon
+++ b/extension.neon
@@ -4,10 +4,10 @@ parameters:
 		constant_hassers: true
 		console_application_loader: null
 	stubFiles:
-		- stubs/FormBuilderInterface.php
-		- stubs/FormInterface.php
-		- stubs/HeaderBag.php
-		- stubs/Session.php
+		- stubs/FormBuilderInterface.stub
+		- stubs/FormInterface.stub
+		- stubs/HeaderBag.stub
+		- stubs/Session.stub
 
 parametersSchema:
 	symfony: structure([
diff --git a/stubs/FormBuilderInterface.php b/stubs/FormBuilderInterface.stub
similarity index 100%
rename from stubs/FormBuilderInterface.php
rename to stubs/FormBuilderInterface.stub
diff --git a/stubs/FormInterface.php b/stubs/FormInterface.stub
similarity index 100%
rename from stubs/FormInterface.php
rename to stubs/FormInterface.stub
diff --git a/stubs/HeaderBag.php b/stubs/HeaderBag.stub
similarity index 100%
rename from stubs/HeaderBag.php
rename to stubs/HeaderBag.stub
diff --git a/stubs/Session.php b/stubs/Session.stub
similarity index 100%
rename from stubs/Session.php
rename to stubs/Session.stub

From 1481ff31719f42c26fdb423a976efe30534633f3 Mon Sep 17 00:00:00 2001
From: dbrekelmans <dabrekelmans@gmail.com>
Date: Mon, 20 Jan 2020 18:06:07 +0100
Subject: [PATCH 025/297] Add a bunch of stubs

---
 extension.neon                      |  7 +++++++
 stubs/ChoiceLoaderInterface.stub    | 22 ++++++++++++++++++++++
 stubs/Constraint.stub               | 16 ++++++++++++++++
 stubs/ContainerBuilder.stub         |  7 +++++++
 stubs/EventSubscriberInterface.stub | 11 +++++++++++
 stubs/ExtensionInterface.stub       | 13 +++++++++++++
 stubs/FormTypeInterface.stub        | 21 +++++++++++++++++++++
 stubs/FormView.stub                 | 14 ++++++++++++++
 8 files changed, 111 insertions(+)
 create mode 100644 stubs/ChoiceLoaderInterface.stub
 create mode 100644 stubs/Constraint.stub
 create mode 100644 stubs/ContainerBuilder.stub
 create mode 100644 stubs/EventSubscriberInterface.stub
 create mode 100644 stubs/ExtensionInterface.stub
 create mode 100644 stubs/FormTypeInterface.stub
 create mode 100644 stubs/FormView.stub

diff --git a/extension.neon b/extension.neon
index 5f6d5e37..3963d3c4 100644
--- a/extension.neon
+++ b/extension.neon
@@ -4,8 +4,15 @@ parameters:
 		constant_hassers: true
 		console_application_loader: null
 	stubFiles:
+		- stubs/ChoiceLoaderInterface.stub
+		- stubs/Constraint.stub
+		- stubs/ContainerBuilder.stub
+		- stubs/EventSubscriberInterface.stub
+		- stubs/ExtensionInterface.stub
 		- stubs/FormBuilderInterface.stub
 		- stubs/FormInterface.stub
+		- stubs/FormTypeInterface.stub
+		- stubs/FormView.stub
 		- stubs/HeaderBag.stub
 		- stubs/Session.stub
 
diff --git a/stubs/ChoiceLoaderInterface.stub b/stubs/ChoiceLoaderInterface.stub
new file mode 100644
index 00000000..0388b898
--- /dev/null
+++ b/stubs/ChoiceLoaderInterface.stub
@@ -0,0 +1,22 @@
+<?php
+
+namespace Symfony\Component\Form\ChoiceList\Loader;
+
+interface ChoiceLoaderInterface
+{
+    /**
+     * @param array<string> $values
+     * @param callable|null $value
+     *
+     * @return array<mixed>
+     */
+    public function loadChoicesForValues(array $values, $value = null);
+
+    /**
+     * @param array<mixed> $choices
+     * @param callable|null $value
+     *
+     * @return array<string>
+     */
+    public function loadValuesForChoices(array $choices, $value = null);
+}
diff --git a/stubs/Constraint.stub b/stubs/Constraint.stub
new file mode 100644
index 00000000..7d875f2a
--- /dev/null
+++ b/stubs/Constraint.stub
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Component\Validator;
+
+class Constraint
+{
+    /**
+     * @return array<mixed>
+     */
+    public function getRequiredOptions();
+
+    /**
+     * @return string|array<string>
+     */
+    public function getTargets();
+}
diff --git a/stubs/ContainerBuilder.stub b/stubs/ContainerBuilder.stub
new file mode 100644
index 00000000..457734fc
--- /dev/null
+++ b/stubs/ContainerBuilder.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\DependencyInjection;
+
+class ContainerBuilder
+{
+}
diff --git a/stubs/EventSubscriberInterface.stub b/stubs/EventSubscriberInterface.stub
new file mode 100644
index 00000000..35a77c93
--- /dev/null
+++ b/stubs/EventSubscriberInterface.stub
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\EventDispatcher;
+
+interface EventSubscriberInterface
+{
+    /**
+     * @return array<string, string|array<string, int>|array<int, string|array<string, int>|array<int, string>>>
+     */
+    public static function getSubscribedEvents();
+}
diff --git a/stubs/ExtensionInterface.stub b/stubs/ExtensionInterface.stub
new file mode 100644
index 00000000..d0b04792
--- /dev/null
+++ b/stubs/ExtensionInterface.stub
@@ -0,0 +1,13 @@
+<?php
+
+namespace Symfony\Component\DependencyInjection\Extension;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+interface ExtensionInterface
+{
+    /**
+     * @param array<mixed> $configs
+     */
+    public function load(array $configs, ContainerBuilder $container): void;
+}
diff --git a/stubs/FormTypeInterface.stub b/stubs/FormTypeInterface.stub
new file mode 100644
index 00000000..7aa64c51
--- /dev/null
+++ b/stubs/FormTypeInterface.stub
@@ -0,0 +1,21 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+interface FormTypeInterface
+{
+    /**
+     * @param array<mixed> $options
+     */
+    public function buildForm(FormBuilderInterface $builder, array $options): void;
+
+    /**
+     * @param array<mixed> $options
+     */
+    public function buildView(FormView $view, FormInterface $form, array $options): void;
+
+    /**
+     * @param array<mixed> $options
+     */
+    public function finishView(FormView $view, FormInterface $form, array $options): void;
+}
diff --git a/stubs/FormView.stub b/stubs/FormView.stub
new file mode 100644
index 00000000..e7646acf
--- /dev/null
+++ b/stubs/FormView.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+use ArrayAccess;
+use IteratorAggregate;
+
+/**
+ * @implements IteratorAggregate<string, self>
+ * @implements ArrayAccess<string, self>
+ */
+class FormView implements ArrayAccess, IteratorAggregate
+{
+}

From a6d87e0068741327f99cf3ec0e9c4f6809e602e2 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 12 Feb 2020 21:38:48 +0100
Subject: [PATCH 026/297] Fixed build

---
 .../KernelInterfaceDynamicReturnTypeExtensionTest.php       | 3 +++
 .../Type/Symfony/RequestDynamicReturnTypeExtensionTest.php  | 3 +++
 .../Symfony/SerializerDynamicReturnTypeExtensionTest.php    | 3 +++
 .../Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php  | 6 ++++++
 4 files changed, 15 insertions(+)

diff --git a/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
index 8e99b04e..a5178a51 100644
--- a/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
@@ -20,6 +20,9 @@ public function testGet(string $expression, string $type): void
 		);
 	}
 
+	/**
+	 * @return Iterator<int, array{string, string}>
+	 */
 	public function getProvider(): Iterator
 	{
 		yield ['$foo', 'string'];
diff --git a/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
index 2f697e4d..22572808 100644
--- a/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
@@ -20,6 +20,9 @@ public function testGetContent(string $expression, string $type): void
 		);
 	}
 
+	/**
+	 * @return Iterator<int, array{string, string}>
+	 */
 	public function getContentProvider(): Iterator
 	{
 		yield ['$content1', 'string'];
diff --git a/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
index f4863a73..8b60042c 100644
--- a/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
@@ -39,6 +39,9 @@ public function testDenormalizerInterface(string $expression, string $type): voi
 		);
 	}
 
+	/**
+	 * @return Iterator<int, array{string, string}>
+	 */
 	public function getContentProvider(): Iterator
 	{
 		yield ['$first', 'Bar'];
diff --git a/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
index 9d77d459..9580bf91 100644
--- a/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
@@ -22,6 +22,9 @@ public function testServices(string $expression, string $type, ?string $containe
 		);
 	}
 
+	/**
+	 * @return Iterator<int, array{string, string, string|null}>
+	 */
 	public function servicesProvider(): Iterator
 	{
 		yield ['$service1', 'Foo', __DIR__ . '/container.xml'];
@@ -56,6 +59,9 @@ public function testConstantHassersOff(string $expression, string $type, ?string
 		);
 	}
 
+	/**
+	 * @return Iterator<int, array{string, string, string|null}>
+	 */
 	public function constantHassersOffProvider(): Iterator
 	{
 		yield ['$has1', 'bool', __DIR__ . '/container.xml'];

From 473b4840e7235b67a8fd35e6dc162953e0e038ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= <enumag@gmail.com>
Date: Wed, 4 Mar 2020 09:28:13 +0100
Subject: [PATCH 027/297] Fix FormInterface stub

---
 stubs/FormInterface.stub | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stubs/FormInterface.stub b/stubs/FormInterface.stub
index 909a121a..868c4ae2 100644
--- a/stubs/FormInterface.stub
+++ b/stubs/FormInterface.stub
@@ -3,7 +3,7 @@
 namespace Symfony\Component\Form;
 
 /**
- * @extends \Traversable<int, \Symfony\Component\Form\FormInterface>
+ * @extends \Traversable<string|int, \Symfony\Component\Form\FormInterface>
  */
 interface FormInterface extends \Traversable
 {

From b450ec4c7439a85c59d0623737ff59f911aa5f9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Thu, 2 Apr 2020 16:13:05 +0200
Subject: [PATCH 028/297] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index eecb0cf7..33e96578 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # PHPStan Symfony Framework extensions and rules
 
-[![Build Status](https://travis-ci.org/phpstan/phpstan-symfony.svg)](https://travis-ci.org/phpstan/phpstan-symfony)
+[![Build Status](https://travis-ci.com/phpstan/phpstan-symfony.svg?branch=master)](https://travis-ci.com/phpstan/phpstan-symfony)
 [![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-symfony/v/stable)](https://packagist.org/packages/phpstan/phpstan-symfony)
 [![License](https://poser.pugx.org/phpstan/phpstan-symfony/license)](https://packagist.org/packages/phpstan/phpstan-symfony)
 

From 063c8289357d42cceba956589bfb8b57b5ac4d40 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 9 Apr 2020 09:41:44 +0200
Subject: [PATCH 029/297] Revert "Made ConsoleApplicationResolver lazier"

This reverts commit 9251db02831216a1c6baf59a365569ef2e402be8.
---
 src/Symfony/ConsoleApplicationResolver.php | 37 ++++------------------
 1 file changed, 7 insertions(+), 30 deletions(-)

diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index acd09d66..9a684d17 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -5,7 +5,6 @@
 use PHPStan\Reflection\ClassReflection;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Type\ObjectType;
-use Symfony\Component\Console\Application;
 use function file_exists;
 use function get_class;
 use function is_readable;
@@ -13,15 +12,15 @@
 final class ConsoleApplicationResolver
 {
 
-	/** @var string|null */
-	private $consoleApplicationLoader;
-
-	/** @var \Symfony\Component\Console\Application|false|null */
+	/** @var \Symfony\Component\Console\Application|null */
 	private $consoleApplication;
 
 	public function __construct(?string $consoleApplicationLoader)
 	{
-		$this->consoleApplicationLoader = $consoleApplicationLoader;
+		if ($consoleApplicationLoader === null) {
+			return;
+		}
+		$this->consoleApplication = $this->loadConsoleApplication($consoleApplicationLoader);
 	}
 
 	/**
@@ -39,34 +38,12 @@ private function loadConsoleApplication(string $consoleApplicationLoader)
 		return require $consoleApplicationLoader;
 	}
 
-	public function getConsoleApplication(): ?Application
-	{
-		if ($this->consoleApplication === false) {
-			return null;
-		}
-
-		if ($this->consoleApplication !== null) {
-			return $this->consoleApplication;
-		}
-
-		if ($this->consoleApplicationLoader === null) {
-			$this->consoleApplication = false;
-
-			return null;
-		}
-
-		$this->consoleApplication = $this->loadConsoleApplication($this->consoleApplicationLoader);
-
-		return $this->consoleApplication;
-	}
-
 	/**
 	 * @return \Symfony\Component\Console\Command\Command[]
 	 */
 	public function findCommands(ClassReflection $classReflection): array
 	{
-		$consoleApplication = $this->getConsoleApplication();
-		if ($consoleApplication === null) {
+		if ($this->consoleApplication === null) {
 			return [];
 		}
 
@@ -76,7 +53,7 @@ public function findCommands(ClassReflection $classReflection): array
 		}
 
 		$commands = [];
-		foreach ($consoleApplication->all() as $name => $command) {
+		foreach ($this->consoleApplication->all() as $name => $command) {
 			if (!$classType->isSuperTypeOf(new ObjectType(get_class($command)))->yes()) {
 				continue;
 			}

From ba69dcd8e57c1a8580bf190e0554bea0fc37fe2f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 15 Apr 2020 22:23:28 +0200
Subject: [PATCH 030/297] Symfony Process stub

---
 extension.neon     |  1 +
 stubs/Process.stub | 11 +++++++++++
 2 files changed, 12 insertions(+)
 create mode 100644 stubs/Process.stub

diff --git a/extension.neon b/extension.neon
index 3963d3c4..e91a0af2 100644
--- a/extension.neon
+++ b/extension.neon
@@ -14,6 +14,7 @@ parameters:
 		- stubs/FormTypeInterface.stub
 		- stubs/FormView.stub
 		- stubs/HeaderBag.stub
+		- stubs/Process.stub
 		- stubs/Session.stub
 
 parametersSchema:
diff --git a/stubs/Process.stub b/stubs/Process.stub
new file mode 100644
index 00000000..771dc666
--- /dev/null
+++ b/stubs/Process.stub
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\Process;
+
+/**
+ * @implements \IteratorAggregate<string, string>
+ */
+class Process implements \IteratorAggregate
+{
+
+}

From 2319bd12ed48adc3c5b0fb3576829c31ad0c489d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Thu, 16 Apr 2020 18:14:44 +0200
Subject: [PATCH 031/297] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 33e96578..7add83c2 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 [![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-symfony/v/stable)](https://packagist.org/packages/phpstan/phpstan-symfony)
 [![License](https://poser.pugx.org/phpstan/phpstan-symfony/license)](https://packagist.org/packages/phpstan/phpstan-symfony)
 
-* [PHPStan](https://github.com/phpstan/phpstan)
+* [PHPStan](https://phpstan.org/)
 
 This extension provides following features:
 

From ddb6f3af147f12ea0807ba98933bf60df1b044bf Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 8 May 2020 13:40:44 +0200
Subject: [PATCH 032/297] Fixed build

---
 composer.json                            |  2 +-
 tests/Type/Symfony/ExtensionTestCase.php | 17 ++++++++++-------
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/composer.json b/composer.json
index 00d2f82f..a816ac9d 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12"
+    "phpstan/phpstan": "^0.12.24"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index c0a02e93..b43f1552 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -11,6 +11,7 @@
 use PHPStan\Cache\Cache;
 use PHPStan\File\FileHelper;
 use PHPStan\Node\VirtualNode;
+use PHPStan\PhpDoc\PhpDocInheritanceResolver;
 use PHPStan\PhpDoc\PhpDocNodeResolver;
 use PHPStan\PhpDoc\PhpDocStringResolver;
 use PHPStan\Testing\TestCase;
@@ -35,16 +36,18 @@ protected function processFile(
 		$typeSpecifier = $this->createTypeSpecifier(new Standard(), $broker);
 		/** @var \PHPStan\PhpDoc\PhpDocStringResolver $phpDocStringResolver */
 		$phpDocStringResolver = self::getContainer()->getByType(PhpDocStringResolver::class);
+		$fileTypeMapper = new FileTypeMapper(
+			$parser,
+			$phpDocStringResolver,
+			self::getContainer()->getByType(PhpDocNodeResolver::class),
+			$this->createMock(Cache::class),
+			$this->createMock(AnonymousClassNameHelper::class)
+		);
 		$resolver = new NodeScopeResolver(
 			$broker,
 			$parser,
-			new FileTypeMapper(
-				$parser,
-				$phpDocStringResolver,
-				self::getContainer()->getByType(PhpDocNodeResolver::class),
-				$this->createMock(Cache::class),
-				$this->createMock(AnonymousClassNameHelper::class)
-			),
+			$fileTypeMapper,
+			new PhpDocInheritanceResolver($fileTypeMapper),
 			$fileHelper,
 			$typeSpecifier,
 			true,

From 9e4f552a96cf82a99adf3fe5f5cbd8997378b53d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 8 May 2020 13:55:14 +0200
Subject: [PATCH 033/297] Updated PHPUnit

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index a816ac9d..f9c34573 100644
--- a/composer.json
+++ b/composer.json
@@ -28,7 +28,7 @@
     "phing/phing": "^2.16.0",
     "phpstan/phpstan-phpunit": "^0.12",
     "phpstan/phpstan-strict-rules": "^0.12",
-    "phpunit/phpunit": "^7.0",
+    "phpunit/phpunit": "^7.5.20",
     "slevomat/coding-standard": "^4.5.2",
     "squizlabs/php_codesniffer": "^3.3.2",
     "symfony/console": "^4.0",

From efa91f845bba364f6629c5dc8e9978f2e9fb3f8a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Fri, 8 May 2020 20:04:20 +0200
Subject: [PATCH 034/297] Fix lowest deps build

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index f9c34573..27bddf08 100644
--- a/composer.json
+++ b/composer.json
@@ -26,8 +26,8 @@
     "ergebnis/composer-normalize": "^2.0.2",
     "jakub-onderka/php-parallel-lint": "^1.0",
     "phing/phing": "^2.16.0",
-    "phpstan/phpstan-phpunit": "^0.12",
-    "phpstan/phpstan-strict-rules": "^0.12",
+    "phpstan/phpstan-phpunit": "^0.12.8",
+    "phpstan/phpstan-strict-rules": "^0.12.2",
     "phpunit/phpunit": "^7.5.20",
     "slevomat/coding-standard": "^4.5.2",
     "squizlabs/php_codesniffer": "^3.3.2",

From e5a313ab4ed86fb80814b01a0df4bc22ad37fee8 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 11 May 2020 19:53:42 +0200
Subject: [PATCH 035/297] Fix build

---
 composer.json                            | 2 +-
 tests/Type/Symfony/ExtensionTestCase.php | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 27bddf08..ad93095d 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.24"
+    "phpstan/phpstan": "^0.12.26"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index b43f1552..65fdbcc4 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -14,6 +14,7 @@
 use PHPStan\PhpDoc\PhpDocInheritanceResolver;
 use PHPStan\PhpDoc\PhpDocNodeResolver;
 use PHPStan\PhpDoc\PhpDocStringResolver;
+use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider;
 use PHPStan\Testing\TestCase;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FileTypeMapper;
@@ -37,6 +38,7 @@ protected function processFile(
 		/** @var \PHPStan\PhpDoc\PhpDocStringResolver $phpDocStringResolver */
 		$phpDocStringResolver = self::getContainer()->getByType(PhpDocStringResolver::class);
 		$fileTypeMapper = new FileTypeMapper(
+			new DirectReflectionProviderProvider($broker),
 			$parser,
 			$phpDocStringResolver,
 			self::getContainer()->getByType(PhpDocNodeResolver::class),

From 21bbd3c2d01e082f822ce883b77f5f8a3e906990 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 24 May 2020 23:29:00 +0200
Subject: [PATCH 036/297] Fix build

---
 tests/Type/Symfony/ExtensionTestCase.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index 65fdbcc4..f3080a83 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -47,6 +47,8 @@ protected function processFile(
 		);
 		$resolver = new NodeScopeResolver(
 			$broker,
+			self::getReflectors()[0],
+			$this->getClassReflectionExtensionRegistryProvider(),
 			$parser,
 			$fileTypeMapper,
 			new PhpDocInheritanceResolver($fileTypeMapper),

From 57f3a8ed58cf8f40f8411f2a01c940572ecfb4ba Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 7 Jul 2020 06:08:19 +0200
Subject: [PATCH 037/297] Update incompatible dependency

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index ad93095d..e8e6c88e 100644
--- a/composer.json
+++ b/composer.json
@@ -22,7 +22,7 @@
   },
   "require-dev": {
     "consistence/coding-standard": "^3.0.1",
-    "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
+    "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
     "ergebnis/composer-normalize": "^2.0.2",
     "jakub-onderka/php-parallel-lint": "^1.0",
     "phing/phing": "^2.16.0",

From 86bb993d4f343fd6ee5570633d3fbd08b4d4bc4a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 21 Jul 2020 16:56:43 +0200
Subject: [PATCH 038/297] Allow PHP 8

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index e8e6c88e..ee79433d 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,7 @@
     }
   ],
   "require": {
-    "php": "^7.1",
+    "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
     "phpstan/phpstan": "^0.12.26"
   },

From 34a3af9b797a6f7c2feaafc58adcf68a584e48ee Mon Sep 17 00:00:00 2001
From: Damien Debin <damien.debin@gmail.com>
Date: Fri, 21 Aug 2020 13:23:09 +0200
Subject: [PATCH 039/297] Adjusts GetOption return type if no default value.

---
 .../InputInterfaceGetOptionDynamicReturnTypeExtension.php     | 4 ++--
 .../InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
index 9aa78b2a..40a3993f 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -67,12 +67,12 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 				if (!$option->acceptValue()) {
 					$optType = new BooleanType();
 				} else {
-					$optType = TypeCombinator::union(new StringType(), new IntegerType(), new NullType());
+					$optType = TypeCombinator::union(new StringType(), new NullType());
 					if ($option->isValueRequired() && ($option->isArray() || $option->getDefault() !== null)) {
 						$optType = TypeCombinator::removeNull($optType);
 					}
 					if ($option->isArray()) {
-						$optType = new ArrayType(new IntegerType(), TypeCombinator::remove($optType, new IntegerType()));
+						$optType = new ArrayType(new IntegerType(), $optType);
 					}
 					$optType = TypeCombinator::union($optType, $scope->getTypeFromValue($option->getDefault()));
 				}
diff --git a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
index c95ca57f..affb0c62 100644
--- a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
@@ -27,8 +27,8 @@ public function testArgumentTypes(string $expression, string $type): void
 	public function argumentTypesProvider(): Iterator
 	{
 		yield ['$a', 'bool'];
-		yield ['$b', 'int|string|null'];
-		yield ['$c', 'int|string|null'];
+		yield ['$b', 'string|null'];
+		yield ['$c', 'string|null'];
 		yield ['$d', 'array<int, string|null>'];
 		yield ['$e', 'array<int, string>'];
 

From 7dfa5aff9029b0b6f9a1a28b50ecbc3ff7dfa590 Mon Sep 17 00:00:00 2001
From: Fabien Villepinte <fabien.villepinte@gmail.com>
Date: Thu, 1 Oct 2020 09:45:22 +0200
Subject: [PATCH 040/297] Use the maintained version of php-parallel-lint

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index ee79433d..b11f5874 100644
--- a/composer.json
+++ b/composer.json
@@ -24,8 +24,8 @@
     "consistence/coding-standard": "^3.0.1",
     "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
     "ergebnis/composer-normalize": "^2.0.2",
-    "jakub-onderka/php-parallel-lint": "^1.0",
     "phing/phing": "^2.16.0",
+    "php-parallel-lint/php-parallel-lint": "^1.2",
     "phpstan/phpstan-phpunit": "^0.12.8",
     "phpstan/phpstan-strict-rules": "^0.12.2",
     "phpunit/phpunit": "^7.5.20",

From 46cd7d5cc209651a3a7d7bbb18b213f6ec806e77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Sun, 4 Oct 2020 08:49:52 +0200
Subject: [PATCH 041/297] Update README.md

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 7add83c2..42129196 100644
--- a/README.md
+++ b/README.md
@@ -53,11 +53,11 @@ You have to provide a path to `srcDevDebugProjectContainer.xml` or similar XML f
 ```yaml
 parameters:
     symfony:
-        container_xml_path: %rootDir%/../../../var/cache/dev/srcDevDebugProjectContainer.xml
+        container_xml_path: var/cache/dev/srcDevDebugProjectContainer.xml
         # or with Symfony 4.2+
-        container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml'
+        container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml
         # or with Symfony 5+
-        container_xml_path: '%rootDir%/../../../var/cache/dev/App_KernelDevDebugContainer.xml'
+        container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml
 ```
 
 ## Constant hassers

From 31ccb465bab64116ed925d284ffa7d31af13be2c Mon Sep 17 00:00:00 2001
From: Thomas Landauer <thomas@landauer.at>
Date: Sat, 3 Oct 2020 15:44:50 +0200
Subject: [PATCH 042/297] Update README.md

Fixing path. If the file is in `test`, you just need to go up *one* level.
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 42129196..88d13820 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ parameters:
 For example, in a Symfony project, `console-application.php` would look something like this:
 
 ```php
-require dirname(__DIR__).'/../config/bootstrap.php';
+require __DIR__.'/../config/bootstrap.php';
 $kernel = new \App\Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
 return new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
 ```

From 2283beed7633d59ebc872151e5da5ee2b95d6c5b Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Tue, 20 Oct 2020 10:39:56 +0200
Subject: [PATCH 043/297] Travis > Test on PHP 7.4

---
 .travis.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.travis.yml b/.travis.yml
index cbb484db..170ca9d8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ php:
   - 7.1
   - 7.2
   - 7.3
+  - 7.4
 env:
   - dependencies=lowest
   - dependencies=highest

From 377972f8a143a9520e817ac2f1a4015a6f4e9c26 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Wed, 21 Oct 2020 14:04:25 +0200
Subject: [PATCH 044/297] Updated coding standard that can run on PHP 7.4

---
 composer.json                              |  6 +++---
 phpcs.xml                                  | 18 +++++++++++++++---
 src/Symfony/ConsoleApplicationResolver.php |  2 +-
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/composer.json b/composer.json
index b11f5874..5dc426af 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,7 @@
     "symfony/framework-bundle": "<3.0"
   },
   "require-dev": {
-    "consistence/coding-standard": "^3.0.1",
+    "consistence/coding-standard": "^3.10",
     "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
     "ergebnis/composer-normalize": "^2.0.2",
     "phing/phing": "^2.16.0",
@@ -29,8 +29,8 @@
     "phpstan/phpstan-phpunit": "^0.12.8",
     "phpstan/phpstan-strict-rules": "^0.12.2",
     "phpunit/phpunit": "^7.5.20",
-    "slevomat/coding-standard": "^4.5.2",
-    "squizlabs/php_codesniffer": "^3.3.2",
+    "slevomat/coding-standard": "^6.4",
+    "squizlabs/php_codesniffer": "^3.5.6",
     "symfony/console": "^4.0",
     "symfony/framework-bundle": "^4.0",
     "symfony/http-foundation": "^4.0",
diff --git a/phpcs.xml b/phpcs.xml
index 2a8022a0..e551d322 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -6,7 +6,7 @@
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation"/>
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions"/>
         <exclude name="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly"/>
-        <exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableReturnTypeHintSpecification"/>
+        <exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
         <exclude name="Consistence.Exceptions.ExceptionDeclaration"/>
         <exclude name="Squiz.Commenting.FunctionComment.MissingParamTag"/>
     </rule>
@@ -20,7 +20,7 @@
             <property name="newlinesCountBetweenOpenTagAndDeclare" value="0"/>
         </properties>
     </rule>
-    <rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration">
+    <rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint">
         <properties>
             <property name="usefulAnnotations" type="array" value="
                 @dataProvider,
@@ -28,8 +28,20 @@
             "/>
         </properties>
     </rule>
+    <rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
+        <properties>
+            <property name="enableNativeTypeHint" value="false"/>
+        </properties>
+        <exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
+    </rule>
+    <rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint">
+        <properties>
+            <property name="enableObjectTypeHint" value="false"/>
+        </properties>
+        <exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
+    </rule>
     <rule ref="SlevomatCodingStandard.ControlStructures.AssignmentInCondition"/>
-    <rule ref="SlevomatCodingStandard.ControlStructures.DisallowEqualOperators"/>
+    <rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators"/>
     <rule ref="SlevomatCodingStandard.ControlStructures.EarlyExit"/>
     <rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming"/>
     <rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming"/>
diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 9a684d17..88ee908f 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -25,7 +25,7 @@ public function __construct(?string $consoleApplicationLoader)
 
 	/**
 	 * @return \Symfony\Component\Console\Application|null
-	 * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
+	 * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint
 	 */
 	private function loadConsoleApplication(string $consoleApplicationLoader)
 	{

From 454fe8baa3128658e7ee94f3de2cc5f8c7016cee Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Wed, 21 Oct 2020 16:24:36 +0200
Subject: [PATCH 045/297] Bump minimum versions to prevent deprecations while
 running in lowest mode

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 5dc426af..e3ee387a 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,7 @@
     "consistence/coding-standard": "^3.10",
     "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
     "ergebnis/composer-normalize": "^2.0.2",
-    "phing/phing": "^2.16.0",
+    "phing/phing": "^2.16.2",
     "php-parallel-lint/php-parallel-lint": "^1.2",
     "phpstan/phpstan-phpunit": "^0.12.8",
     "phpstan/phpstan-strict-rules": "^0.12.2",

From 9da032e0874ea6bb21aa04ddf009e36ff595478f Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Wed, 21 Oct 2020 17:19:54 +0200
Subject: [PATCH 046/297] Add support for InputBag (Symfony 5+)

Co-authored-by: raalderink <33425670+raalderink@users.noreply.github.com>
---
 extension.neon                                |  5 ++
 phpstan.neon                                  |  1 +
 .../InputBagDynamicReturnTypeExtension.php    | 46 +++++++++++++++++++
 ...InputBagDynamicReturnTypeExtensionTest.php | 38 +++++++++++++++
 tests/Type/Symfony/input_bag_get.php          | 10 ++++
 5 files changed, 100 insertions(+)
 create mode 100644 src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
 create mode 100644 tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
 create mode 100644 tests/Type/Symfony/input_bag_get.php

diff --git a/extension.neon b/extension.neon
index e91a0af2..61a2f819 100644
--- a/extension.neon
+++ b/extension.neon
@@ -75,6 +75,11 @@ services:
 		factory: PHPStan\Type\Symfony\RequestTypeSpecifyingExtension
 		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
 
+	# InputBag::get() return type
+	-
+		factory: PHPStan\Type\Symfony\InputBagDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
 	# HeaderBag::get() return type
 	-
 		factory: PHPStan\Type\Symfony\HeaderBagDynamicReturnTypeExtension
diff --git a/phpstan.neon b/phpstan.neon
index 987fcc27..f1ef1861 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -13,6 +13,7 @@ parameters:
 		- tests/*/console_application_loader.php
 		- tests/*/envelope_all.php
 		- tests/*/header_bag_get.php
+		- tests/*/input_bag_get.php
 		- tests/*/kernel_interface.php
 		- tests/*/request_get_content.php
 		- tests/*/request_get_session.php
diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..747a2312
--- /dev/null
+++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
@@ -0,0 +1,46 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\NullType;
+use PHPStan\Type\StringType;
+use PHPStan\Type\Type;
+
+final class InputBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\HttpFoundation\InputBag';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'get';
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		if (isset($methodCall->args[1])) {
+			$argType = $scope->getType($methodCall->args[1]->value);
+			$isNull = (new NullType())->isSuperTypeOf($argType);
+			$isString = (new StringType())->isSuperTypeOf($argType);
+			$compare = $isNull->compareTo($isString);
+			if ($compare === $isString) {
+				return new StringType();
+			}
+		}
+
+		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+	}
+
+}
diff --git a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
new file mode 100644
index 00000000..7e8f1d2a
--- /dev/null
+++ b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
@@ -0,0 +1,38 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Iterator;
+
+final class InputBagDynamicReturnTypeExtensionTest extends ExtensionTestCase
+{
+
+	/**
+	 * @dataProvider getProvider
+	 */
+	public function testGet(string $expression, string $type): void
+	{
+		if (!class_exists('Symfony\\Component\\HttpFoundation\\InputBag')) {
+			self::markTestSkipped('The test needs Symfony\Component\HttpFoundation\InputBag class.');
+		}
+
+		$this->processFile(
+			__DIR__ . '/input_bag_get.php',
+			$expression,
+			$type,
+			new InputBagDynamicReturnTypeExtension()
+		);
+	}
+
+	/**
+	 * @return \Iterator<array{string, string}>
+	 */
+	public function getProvider(): Iterator
+	{
+		yield ['$test1', 'string|null'];
+		yield ['$test2', 'string|null'];
+		yield ['$test3', 'string'];
+		yield ['$test4', 'string'];
+	}
+
+}
diff --git a/tests/Type/Symfony/input_bag_get.php b/tests/Type/Symfony/input_bag_get.php
new file mode 100644
index 00000000..756ff2af
--- /dev/null
+++ b/tests/Type/Symfony/input_bag_get.php
@@ -0,0 +1,10 @@
+<?php declare(strict_types = 1);
+
+$bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar']);
+
+$test1 = $bag->get('foo');
+$test2 = $bag->get('foo', null);
+$test3 = $bag->get('foo', '');
+$test4 = $bag->get('foo', 'baz');
+
+die;

From 675703d820235cac1abe08e8bfb12e5509a3b169 Mon Sep 17 00:00:00 2001
From: Philipp Scheit <62871710+pscheit-lillydoo@users.noreply.github.com>
Date: Thu, 22 Oct 2020 12:47:37 +0200
Subject: [PATCH 047/297] Add FormTypeExtensionInterface stub

---
 extension.neon                        |  1 +
 stubs/FormTypeExtensionInterface.stub | 21 +++++++++++++++++++++
 stubs/FormTypeInterface.stub          |  6 +++---
 3 files changed, 25 insertions(+), 3 deletions(-)
 create mode 100644 stubs/FormTypeExtensionInterface.stub

diff --git a/extension.neon b/extension.neon
index 61a2f819..9b0c264c 100644
--- a/extension.neon
+++ b/extension.neon
@@ -11,6 +11,7 @@ parameters:
 		- stubs/ExtensionInterface.stub
 		- stubs/FormBuilderInterface.stub
 		- stubs/FormInterface.stub
+		- stubs/FormTypeExtensionInterface.stub
 		- stubs/FormTypeInterface.stub
 		- stubs/FormView.stub
 		- stubs/HeaderBag.stub
diff --git a/stubs/FormTypeExtensionInterface.stub b/stubs/FormTypeExtensionInterface.stub
new file mode 100644
index 00000000..25b6f25d
--- /dev/null
+++ b/stubs/FormTypeExtensionInterface.stub
@@ -0,0 +1,21 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+interface FormTypeExtensionInterface
+{
+    /**
+     * @param array<string, mixed> $options
+     */
+    public function buildForm(FormBuilderInterface $builder, array $options): void;
+
+    /**
+     * @param array<string, mixed> $options
+     */
+    public function buildView(FormView $view, FormInterface $form, array $options): void;
+
+    /**
+     * @param array<string, mixed> $options
+     */
+    public function finishView(FormView $view, FormInterface $form, array $options): void;
+}
diff --git a/stubs/FormTypeInterface.stub b/stubs/FormTypeInterface.stub
index 7aa64c51..cebbc1c2 100644
--- a/stubs/FormTypeInterface.stub
+++ b/stubs/FormTypeInterface.stub
@@ -5,17 +5,17 @@ namespace Symfony\Component\Form;
 interface FormTypeInterface
 {
     /**
-     * @param array<mixed> $options
+     * @param array<string, mixed> $options
      */
     public function buildForm(FormBuilderInterface $builder, array $options): void;
 
     /**
-     * @param array<mixed> $options
+     * @param array<string, mixed> $options
      */
     public function buildView(FormView $view, FormInterface $form, array $options): void;
 
     /**
-     * @param array<mixed> $options
+     * @param array<string, mixed> $options
      */
     public function finishView(FormView $view, FormInterface $form, array $options): void;
 }

From e928cfcaf87aab9fa4ece128007599f30da03bf3 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 23 Oct 2020 00:09:28 +0200
Subject: [PATCH 048/297] Compatibility with latest PHPStan

---
 composer.json                            | 2 +-
 tests/Type/Symfony/ExtensionTestCase.php | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index e3ee387a..085c92c4 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.26"
+    "phpstan/phpstan": "^0.12.51"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index f3080a83..49c4b0fa 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -11,6 +11,7 @@
 use PHPStan\Cache\Cache;
 use PHPStan\File\FileHelper;
 use PHPStan\Node\VirtualNode;
+use PHPStan\Php\PhpVersion;
 use PHPStan\PhpDoc\PhpDocInheritanceResolver;
 use PHPStan\PhpDoc\PhpDocNodeResolver;
 use PHPStan\PhpDoc\PhpDocStringResolver;
@@ -51,6 +52,7 @@ protected function processFile(
 			$this->getClassReflectionExtensionRegistryProvider(),
 			$parser,
 			$fileTypeMapper,
+			new PhpVersion(PHP_VERSION_ID),
 			new PhpDocInheritanceResolver($fileTypeMapper),
 			$fileHelper,
 			$typeSpecifier,

From edb6aeb171980b11dca1fd27d3fa7573af89ac8e Mon Sep 17 00:00:00 2001
From: Lucas dos Santos Abreu <lucas.s.abreu@gmail.com>
Date: Fri, 30 Oct 2020 06:50:09 -0300
Subject: [PATCH 049/297] (feat): add stubs for symfony/serializer interfaces
 (#110)

* (feat): add stubs for symfony/serializer interfaces

* (fix): missing DenormalizerInterface
---
 extension.neon                               |  9 ++++++++
 stubs/ContextAwareDecoderInterface.stub      | 13 +++++++++++
 stubs/ContextAwareDenormalizerInterface.stub | 14 ++++++++++++
 stubs/ContextAwareNormalizerInterface.stub   | 14 ++++++++++++
 stubs/DecoderInterface.stub                  | 22 +++++++++++++++++++
 stubs/DenormalizableInterface.stub           | 16 ++++++++++++++
 stubs/DenormalizerInterface.stub             | 23 ++++++++++++++++++++
 stubs/EncoderInterface.stub                  | 20 +++++++++++++++++
 stubs/NormalizableInterface.stub             | 14 ++++++++++++
 stubs/NormalizerInterface.stub               | 14 ++++++++++++
 10 files changed, 159 insertions(+)
 create mode 100644 stubs/ContextAwareDecoderInterface.stub
 create mode 100644 stubs/ContextAwareDenormalizerInterface.stub
 create mode 100644 stubs/ContextAwareNormalizerInterface.stub
 create mode 100644 stubs/DecoderInterface.stub
 create mode 100644 stubs/DenormalizableInterface.stub
 create mode 100644 stubs/DenormalizerInterface.stub
 create mode 100644 stubs/EncoderInterface.stub
 create mode 100644 stubs/NormalizableInterface.stub
 create mode 100644 stubs/NormalizerInterface.stub

diff --git a/extension.neon b/extension.neon
index 9b0c264c..272f1343 100644
--- a/extension.neon
+++ b/extension.neon
@@ -7,6 +7,13 @@ parameters:
 		- stubs/ChoiceLoaderInterface.stub
 		- stubs/Constraint.stub
 		- stubs/ContainerBuilder.stub
+		- stubs/ContextAwareDecoderInterface.stub
+		- stubs/ContextAwareDenormalizerInterface.stub
+		- stubs/ContextAwareNormalizerInterface.stub
+		- stubs/DecoderInterface.stub
+		- stubs/DenormalizableInterface.stub
+		- stubs/DenormalizerInterface.stub
+		- stubs/EncoderInterface.stub
 		- stubs/EventSubscriberInterface.stub
 		- stubs/ExtensionInterface.stub
 		- stubs/FormBuilderInterface.stub
@@ -15,6 +22,8 @@ parameters:
 		- stubs/FormTypeInterface.stub
 		- stubs/FormView.stub
 		- stubs/HeaderBag.stub
+		- stubs/NormalizableInterface.stub
+		- stubs/NormalizerInterface.stub
 		- stubs/Process.stub
 		- stubs/Session.stub
 
diff --git a/stubs/ContextAwareDecoderInterface.stub b/stubs/ContextAwareDecoderInterface.stub
new file mode 100644
index 00000000..39e12d2f
--- /dev/null
+++ b/stubs/ContextAwareDecoderInterface.stub
@@ -0,0 +1,13 @@
+<?php
+
+namespace Symfony\Component\Serializer\Encoder;
+
+interface ContextAwareDecoderInterface extends DecoderInterface
+{
+    /**
+     * @param string $format
+     * @param array<mixed> $context
+     * @return bool
+     */
+    public function supportsDecoding($format, array $context = []);
+}
diff --git a/stubs/ContextAwareDenormalizerInterface.stub b/stubs/ContextAwareDenormalizerInterface.stub
new file mode 100644
index 00000000..4c3bdbc8
--- /dev/null
+++ b/stubs/ContextAwareDenormalizerInterface.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Serializer\Normalizer;
+
+interface ContextAwareDenormalizerInterface extends DenormalizerInterface
+{
+    /**
+     * @param mixed $data
+     * @param string $format
+     * @param array<mixed> $context
+     * @return bool
+     */
+    public function supportsDenormalization($data, $type, $format = null, array $context = []);
+}
diff --git a/stubs/ContextAwareNormalizerInterface.stub b/stubs/ContextAwareNormalizerInterface.stub
new file mode 100644
index 00000000..282354a8
--- /dev/null
+++ b/stubs/ContextAwareNormalizerInterface.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Serializer\Normalizer;
+
+interface ContextAwareNormalizerInterface extends NormalizerInterface
+{
+    /**
+     * @param mixed $data
+     * @param string $format
+     * @param array<mixed> $context
+     * @return bool
+     */
+    public function supportsNormalization($data, $format = null, array $context = []);
+}
diff --git a/stubs/DecoderInterface.stub b/stubs/DecoderInterface.stub
new file mode 100644
index 00000000..715dadc4
--- /dev/null
+++ b/stubs/DecoderInterface.stub
@@ -0,0 +1,22 @@
+<?php
+
+namespace Symfony\Component\Serializer\Encoder;
+
+use Symfony\Component\Serializer\Exception\UnexpectedValueException;
+
+interface DecoderInterface
+{
+    /**
+     * @param string $data
+     * @param string $format
+     * @param array<mixed> $context
+     * @return mixed
+     */
+    public function decode($data, $format, array $context = []);
+
+    /**
+     * @param string $format Format name
+     * @return bool
+     */
+    public function supportsDecoding($format);
+}
diff --git a/stubs/DenormalizableInterface.stub b/stubs/DenormalizableInterface.stub
new file mode 100644
index 00000000..17d6b118
--- /dev/null
+++ b/stubs/DenormalizableInterface.stub
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Component\Serializer\Normalizer;
+
+interface DenormalizableInterface
+{
+    /**
+     * @param DenormalizerInterface $denormalizer
+     * @param array<mixed>|string|int|float|bool $data
+     * @param string|null $format
+     * @param array<mixed> $context
+     *
+     * @return object|object[]
+     */
+    public function denormalize($denormalizer, $data, $format = null, array $context = []);
+}
diff --git a/stubs/DenormalizerInterface.stub b/stubs/DenormalizerInterface.stub
new file mode 100644
index 00000000..86ca8bec
--- /dev/null
+++ b/stubs/DenormalizerInterface.stub
@@ -0,0 +1,23 @@
+<?php
+
+namespace Symfony\Component\Serializer\Normalizer;
+
+interface DenormalizerInterface
+{
+    /**
+     * @param mixed $data
+     * @param string $type
+     * @param string $format
+     * @param array<mixed> $context
+     * @return object|array<mixed>
+     */
+    public function denormalize($data, $type, $format = null, array $context = []);
+
+    /**
+     * @param mixed $data
+     * @param string $type
+     * @param string $format
+     * @return bool
+     */
+    public function supportsDenormalization($data, $type, $format = null);
+}
diff --git a/stubs/EncoderInterface.stub b/stubs/EncoderInterface.stub
new file mode 100644
index 00000000..a3092757
--- /dev/null
+++ b/stubs/EncoderInterface.stub
@@ -0,0 +1,20 @@
+<?php
+
+namespace Symfony\Component\Serializer\Encoder;
+
+interface EncoderInterface
+{
+    /**
+     * @param mixed $data
+     * @param string $format
+     * @param array<mixed> $context
+     * @return string|int|float|bool
+     */
+    public function encode($data, $format, array $context = []);
+
+    /**
+     * @param string $format Format name
+     * @return bool
+     */
+    public function supportsEncoding($format);
+}
diff --git a/stubs/NormalizableInterface.stub b/stubs/NormalizableInterface.stub
new file mode 100644
index 00000000..c7958c55
--- /dev/null
+++ b/stubs/NormalizableInterface.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Serializer\Normalizer;
+
+interface NormalizableInterface
+{
+    /**
+     * @param NormalizerInterface $normalizer
+     * @param string|null $format
+     * @param array<mixed> $context
+     * @return array<mixed>|string|int|float|bool
+     */
+    public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []);
+}
diff --git a/stubs/NormalizerInterface.stub b/stubs/NormalizerInterface.stub
new file mode 100644
index 00000000..907e296e
--- /dev/null
+++ b/stubs/NormalizerInterface.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Serializer\Normalizer;
+
+interface NormalizerInterface
+{
+    /**
+     * @param mixed $object
+     * @param string $format
+     * @param array<mixed> $context
+     * @return array<mixed>|string|int|float|bool|null
+     */
+    public function normalize($object, $format = null, array $context = []);
+}

From 46f7b3d22e0544ca2a04a3824b0b7bdc2e41edb0 Mon Sep 17 00:00:00 2001
From: Oskar Stark <oskarstark@googlemail.com>
Date: Mon, 2 Nov 2020 14:12:36 +0100
Subject: [PATCH 050/297] Readability

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 88d13820..80d5904e 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ Be aware that it may hide genuine errors in your application.
 
 ## Console command analysis
 
-You can opt in for more advanced analysis by providing the console application from your own application. This will allow the correct argument and option types to be inferred when accessing $input->getArgument() or $input->getOption().
+You can opt in for more advanced analysis by providing the console application from your own application. This will allow the correct argument and option types to be inferred when accessing `$input->getArgument()` or `$input->getOption()`.
 
 ```
 parameters:

From 185bf764f8c943793f26edadca1b8d85d6c1e99c Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 13 Dec 2020 13:21:31 +0100
Subject: [PATCH 051/297] Use GitHub Actions instead of Travis CI

---
 .github/workflows/build.yml | 163 ++++++++++++++++++++++++++++++++++++
 .travis.yml                 |  15 ----
 README.md                   |   2 +-
 build-cs/.gitignore         |   2 +
 build-cs/composer.json      |   7 ++
 build.xml                   |  29 +++----
 composer.json               |  14 ++--
 phpcs.xml                   |   2 +-
 8 files changed, 193 insertions(+), 41 deletions(-)
 create mode 100644 .github/workflows/build.yml
 delete mode 100644 .travis.yml
 create mode 100644 build-cs/.gitignore
 create mode 100644 build-cs/composer.json

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..83f79354
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,163 @@
+# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
+
+name: "Build"
+
+on:
+  pull_request:
+  push:
+    branches:
+      - "master"
+
+jobs:
+  lint:
+    name: "Lint"
+    runs-on: "ubuntu-latest"
+
+    strategy:
+      matrix:
+        php-version:
+          - "7.1"
+          - "7.2"
+          - "7.3"
+          - "7.4"
+          - "8.0"
+
+    steps:
+      - name: "Checkout"
+        uses: "actions/checkout@v2"
+
+      - name: "Install PHP"
+        uses: "shivammathur/setup-php@v2"
+        with:
+          coverage: "none"
+          php-version: "${{ matrix.php-version }}"
+
+      - name: "Validate Composer"
+        run: "composer validate"
+
+      - name: "Install dependencies"
+        run: "composer install --no-interaction --no-progress --no-suggest"
+
+      - name: "Update PHPUnit"
+        if: matrix.php-version == '7.4' || matrix.php-version == '8.0'
+        run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
+
+
+      - name: "Lint"
+        run: "vendor/bin/phing lint"
+
+  coding-standards:
+    name: "Coding Standard"
+
+    runs-on: "ubuntu-latest"
+
+    steps:
+      - name: "Checkout"
+        uses: "actions/checkout@v2"
+
+      - name: "Install PHP"
+        uses: "shivammathur/setup-php@v2"
+        with:
+          coverage: "none"
+          php-version: "7.4"
+
+      - name: "Validate Composer"
+        run: "composer validate"
+
+      - name: "Install dependencies"
+        run: "composer install --no-interaction --no-progress --no-suggest"
+
+      - name: "Lint"
+        run: "vendor/bin/phing lint"
+
+      - name: "Coding Standard"
+        run: "vendor/bin/phing cs"
+
+  tests:
+    name: "Tests"
+    runs-on: "ubuntu-latest"
+
+    strategy:
+      fail-fast: false
+      matrix:
+        php-version:
+          - "7.1"
+          - "7.2"
+          - "7.3"
+          - "7.4"
+          - "8.0"
+        dependencies:
+          - "lowest"
+          - "highest"
+        exclude:
+          - php-version: "7.1"
+            dependencies: "highest"
+
+    steps:
+      - name: "Checkout"
+        uses: "actions/checkout@v2"
+
+      - name: "Install PHP"
+        uses: "shivammathur/setup-php@v2"
+        with:
+          coverage: "none"
+          php-version: "${{ matrix.php-version }}"
+
+      - name: "Install lowest dependencies"
+        if: ${{ matrix.dependencies == 'lowest' }}
+        run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest"
+
+      - name: "Install highest dependencies"
+        if: ${{ matrix.dependencies == 'highest' }}
+        run: "composer update --no-interaction --no-progress --no-suggest"
+
+      - name: "Update PHPUnit"
+        if: matrix.php-version == '7.4' || matrix.php-version == '8.0'
+        run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
+
+      - name: "Tests"
+        run: "vendor/bin/phing tests"
+
+  static-analysis:
+    name: "PHPStan"
+    runs-on: "ubuntu-latest"
+
+    strategy:
+      fail-fast: false
+      matrix:
+        php-version:
+          - "7.1"
+          - "7.2"
+          - "7.3"
+          - "7.4"
+          - "8.0"
+        dependencies:
+          - "lowest"
+          - "highest"
+
+    steps:
+      - name: "Checkout"
+        uses: "actions/checkout@v2"
+
+      - name: "Install PHP"
+        uses: "shivammathur/setup-php@v2"
+        with:
+          coverage: "none"
+          php-version: "${{ matrix.php-version }}"
+          extensions: mbstring
+          tools: composer:v2
+
+      - name: "Install lowest dependencies"
+        if: ${{ matrix.dependencies == 'lowest' }}
+        run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest"
+
+      - name: "Install highest dependencies"
+        if: ${{ matrix.dependencies == 'highest' }}
+        run: "composer update --no-interaction --no-progress --no-suggest"
+
+      - name: "Update PHPUnit"
+        if: matrix.php-version == '7.4' || matrix.php-version == '8.0'
+        run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
+
+      - name: "PHPStan"
+        run: "vendor/bin/phing phpstan"
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 170ca9d8..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-language: php
-php:
-  - 7.1
-  - 7.2
-  - 7.3
-  - 7.4
-env:
-  - dependencies=lowest
-  - dependencies=highest
-before_script:
-  - composer self-update
-  - if [ "$dependencies" = "lowest" ]; then composer update --prefer-lowest --no-interaction; fi;
-  - if [ "$dependencies" = "highest" ]; then composer update --no-interaction; fi;
-script:
-  - vendor/bin/phing
diff --git a/README.md b/README.md
index 80d5904e..bf0296bb 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # PHPStan Symfony Framework extensions and rules
 
-[![Build Status](https://travis-ci.com/phpstan/phpstan-symfony.svg?branch=master)](https://travis-ci.com/phpstan/phpstan-symfony)
+[![Build](https://github.com/phpstan/phpstan-symfony/workflows/Build/badge.svg)](https://github.com/phpstan/phpstan-symfony/actions)
 [![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-symfony/v/stable)](https://packagist.org/packages/phpstan/phpstan-symfony)
 [![License](https://poser.pugx.org/phpstan/phpstan-symfony/license)](https://packagist.org/packages/phpstan/phpstan-symfony)
 
diff --git a/build-cs/.gitignore b/build-cs/.gitignore
new file mode 100644
index 00000000..ff72e2d0
--- /dev/null
+++ b/build-cs/.gitignore
@@ -0,0 +1,2 @@
+/composer.lock
+/vendor
diff --git a/build-cs/composer.json b/build-cs/composer.json
new file mode 100644
index 00000000..9acd0275
--- /dev/null
+++ b/build-cs/composer.json
@@ -0,0 +1,7 @@
+{
+	"require-dev": {
+		"consistence/coding-standard": "^3.10",
+		"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+		"slevomat/coding-standard": "^6.4"
+	}
+}
diff --git a/build.xml b/build.xml
index 1044c131..75b98df1 100644
--- a/build.xml
+++ b/build.xml
@@ -5,7 +5,6 @@
         composer,
         lint,
         cs,
-        composer-normalize-check,
         tests,
         phpstan
     "/>
@@ -21,19 +20,6 @@
         </exec>
     </target>
 
-    <target name="composer-normalize-check">
-        <exec
-                executable="composer"
-                logoutput="true"
-                passthru="true"
-                checkreturn="true"
-        >
-            <arg value="normalize"/>
-            <arg value="--ansi"/>
-            <arg value="--dry-run"/>
-        </exec>
-    </target>
-
     <target name="lint">
         <exec
                 executable="vendor/bin/parallel-lint"
@@ -47,8 +33,19 @@
     </target>
 
     <target name="cs">
+		<exec
+				executable="composer"
+				logoutput="true"
+				passthru="true"
+				checkreturn="true"
+		>
+			<arg value="install"/>
+			<arg value="--working-dir"/>
+			<arg path="build-cs"/>
+			<arg value="--ansi"/>
+		</exec>
         <exec
-                executable="vendor/bin/phpcs"
+                executable="build-cs/vendor/bin/phpcs"
                 logoutput="true"
                 passthru="true"
                 checkreturn="true"
@@ -64,7 +61,7 @@
 
     <target name="cs-fix">
         <exec
-                executable="vendor/bin/phpcbf"
+                executable="build-cs/vendor/bin/phpcbf"
                 logoutput="true"
                 passthru="true"
                 checkreturn="true"
diff --git a/composer.json b/composer.json
index 085c92c4..484ea046 100644
--- a/composer.json
+++ b/composer.json
@@ -21,16 +21,11 @@
     "symfony/framework-bundle": "<3.0"
   },
   "require-dev": {
-    "consistence/coding-standard": "^3.10",
-    "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
-    "ergebnis/composer-normalize": "^2.0.2",
-    "phing/phing": "^2.16.2",
+    "phing/phing": "^2.16.3",
     "php-parallel-lint/php-parallel-lint": "^1.2",
-    "phpstan/phpstan-phpunit": "^0.12.8",
-    "phpstan/phpstan-strict-rules": "^0.12.2",
+    "phpstan/phpstan-phpunit": "^0.12.16",
+    "phpstan/phpstan-strict-rules": "^0.12.5",
     "phpunit/phpunit": "^7.5.20",
-    "slevomat/coding-standard": "^6.4",
-    "squizlabs/php_codesniffer": "^3.5.6",
     "symfony/console": "^4.0",
     "symfony/framework-bundle": "^4.0",
     "symfony/http-foundation": "^4.0",
@@ -38,6 +33,9 @@
     "symfony/serializer": "^4.0"
   },
   "config": {
+    "platform": {
+	  "php": "7.4.6"
+	},
     "sort-packages": true
   },
   "extra": {
diff --git a/phpcs.xml b/phpcs.xml
index e551d322..feb4d827 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <ruleset name="PHPStan PHPUnit extensions and rules">
-    <rule ref="vendor/consistence/coding-standard/Consistence/ruleset.xml">
+    <rule ref="build-cs/vendor/consistence/coding-standard/Consistence/ruleset.xml">
         <exclude name="Squiz.Functions.GlobalFunction.Found"/>
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword"/>
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation"/>

From fb66d5e3ee2e9e60c14931d0fb8c45263bbb0b6f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 13 Dec 2020 14:13:56 +0100
Subject: [PATCH 052/297] Automatic release workflow

---
 .github/workflows/release.yml | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 .github/workflows/release.yml

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..e04c2563
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,33 @@
+# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
+
+name: "Create release"
+
+on:
+  push:
+    tags:
+      - '*'
+
+jobs:
+  deploy:
+    name: "Deploy"
+    runs-on: "ubuntu-latest"
+
+    steps:
+      - name: "Checkout"
+        uses: "actions/checkout@v2"
+
+      - name: Generate changelog
+        id: changelog
+        uses: metcalfc/changelog-generator@v0.4.4
+        with:
+          myToken: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: "Create release"
+        id: create-release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          tag_name: ${{ github.ref }}
+          release_name: ${{ github.ref }}
+          body: ${{ steps.changelog.outputs.changelog }}

From 2f2e8a3cdb3aa07439fe3533fe7d0f2022288339 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 19 Dec 2020 16:08:31 +0100
Subject: [PATCH 053/297] Dependabot

---
 .github/dependabot.yml | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 .github/dependabot.yml

diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..15b77335
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,17 @@
+version: 2
+updates:
+  - package-ecosystem: composer
+    directory: "/"
+    schedule:
+      interval: monthly
+    open-pull-requests-limit: 10
+  - package-ecosystem: composer
+    directory: "/build-cs"
+    schedule:
+      interval: monthly
+    open-pull-requests-limit: 10
+  - package-ecosystem: github-actions
+    directory: "/"
+    schedule:
+      interval: monthly
+    open-pull-requests-limit: 10

From d8313ec2849876aff0e019725eb8afe9456be9cf Mon Sep 17 00:00:00 2001
From: Gocha Ossinkine <ossinkine@ya.ru>
Date: Thu, 7 Jan 2021 20:22:52 +0200
Subject: [PATCH 054/297] Add stub for ConstraintViolationListInterface

---
 extension.neon                              |  2 ++
 stubs/ConstraintViolationInterface.stub     |  7 +++++++
 stubs/ConstraintViolationListInterface.stub | 10 ++++++++++
 3 files changed, 19 insertions(+)
 create mode 100644 stubs/ConstraintViolationInterface.stub
 create mode 100644 stubs/ConstraintViolationListInterface.stub

diff --git a/extension.neon b/extension.neon
index 272f1343..bc03eea3 100644
--- a/extension.neon
+++ b/extension.neon
@@ -6,6 +6,8 @@ parameters:
 	stubFiles:
 		- stubs/ChoiceLoaderInterface.stub
 		- stubs/Constraint.stub
+		- stubs/ConstraintViolationInterface.stub
+		- stubs/ConstraintViolationListInterface.stub
 		- stubs/ContainerBuilder.stub
 		- stubs/ContextAwareDecoderInterface.stub
 		- stubs/ContextAwareDenormalizerInterface.stub
diff --git a/stubs/ConstraintViolationInterface.stub b/stubs/ConstraintViolationInterface.stub
new file mode 100644
index 00000000..ef3312bb
--- /dev/null
+++ b/stubs/ConstraintViolationInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Validator;
+
+interface ConstraintViolationInterface
+{
+}
diff --git a/stubs/ConstraintViolationListInterface.stub b/stubs/ConstraintViolationListInterface.stub
new file mode 100644
index 00000000..423bdfb0
--- /dev/null
+++ b/stubs/ConstraintViolationListInterface.stub
@@ -0,0 +1,10 @@
+<?php
+
+namespace Symfony\Component\Validator;
+
+/**
+ * @extends \Traversable<int, ConstraintViolationInterface>
+ */
+interface ConstraintViolationListInterface extends \Traversable
+{
+}

From 150415359b984270a7d3b2c22662a877700a5fdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edi=20Modri=C4=87?= <edi.modric@gmail.com>
Date: Mon, 11 Jan 2021 10:53:59 +0100
Subject: [PATCH 055/297] Add stubs for Twig Node and Symfony ParameterBag

---
 extension.neon          |  2 ++
 stubs/Node.stub         | 11 +++++++++++
 stubs/ParameterBag.stub | 11 +++++++++++
 3 files changed, 24 insertions(+)
 create mode 100644 stubs/Node.stub
 create mode 100644 stubs/ParameterBag.stub

diff --git a/extension.neon b/extension.neon
index bc03eea3..1d8b419d 100644
--- a/extension.neon
+++ b/extension.neon
@@ -24,8 +24,10 @@ parameters:
 		- stubs/FormTypeInterface.stub
 		- stubs/FormView.stub
 		- stubs/HeaderBag.stub
+		- stubs/Node.stub
 		- stubs/NormalizableInterface.stub
 		- stubs/NormalizerInterface.stub
+		- stubs/ParameterBag.stub
 		- stubs/Process.stub
 		- stubs/Session.stub
 
diff --git a/stubs/Node.stub b/stubs/Node.stub
new file mode 100644
index 00000000..692a0e95
--- /dev/null
+++ b/stubs/Node.stub
@@ -0,0 +1,11 @@
+<?php
+
+namespace Twig\Node;
+
+/**
+ * @implements \IteratorAggregate<string, self>
+ */
+class Node implements \IteratorAggregate
+{
+
+}
diff --git a/stubs/ParameterBag.stub b/stubs/ParameterBag.stub
new file mode 100644
index 00000000..00a40741
--- /dev/null
+++ b/stubs/ParameterBag.stub
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * @implements \IteratorAggregate<string, mixed>
+ */
+class ParameterBag implements \IteratorAggregate
+{
+
+}

From 857d95864d13de3fa06e3145307d4dd7abd6f681 Mon Sep 17 00:00:00 2001
From: Faizan Akram <hello@faizanakram.me>
Date: Mon, 25 Jan 2021 16:17:39 +0530
Subject: [PATCH 056/297] Adds stub file for PropertyPathInterface

---
 extension.neon                   |  1 +
 stubs/PropertyPathInterface.stub | 10 ++++++++++
 2 files changed, 11 insertions(+)
 create mode 100644 stubs/PropertyPathInterface.stub

diff --git a/extension.neon b/extension.neon
index 1d8b419d..bdea0f92 100644
--- a/extension.neon
+++ b/extension.neon
@@ -29,6 +29,7 @@ parameters:
 		- stubs/NormalizerInterface.stub
 		- stubs/ParameterBag.stub
 		- stubs/Process.stub
+		- stubs/PropertyPathInterface.stub
 		- stubs/Session.stub
 
 parametersSchema:
diff --git a/stubs/PropertyPathInterface.stub b/stubs/PropertyPathInterface.stub
new file mode 100644
index 00000000..d687193e
--- /dev/null
+++ b/stubs/PropertyPathInterface.stub
@@ -0,0 +1,10 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess;
+
+/**
+ * @extends \Traversable<int, string>
+ */
+interface PropertyPathInterface extends \Traversable
+{
+}

From c50afb8f4e27d4ab3b47ac370838aefac3c15e9e Mon Sep 17 00:00:00 2001
From: rgrassian <remi.grassian@gmail.com>
Date: Mon, 25 Jan 2021 10:52:39 +0100
Subject: [PATCH 057/297] add stub for Cookie

---
 extension.neon    |  1 +
 stubs/Cookie.stub | 50 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)
 create mode 100644 stubs/Cookie.stub

diff --git a/extension.neon b/extension.neon
index bdea0f92..4209bc61 100644
--- a/extension.neon
+++ b/extension.neon
@@ -12,6 +12,7 @@ parameters:
 		- stubs/ContextAwareDecoderInterface.stub
 		- stubs/ContextAwareDenormalizerInterface.stub
 		- stubs/ContextAwareNormalizerInterface.stub
+		- stubs/Cookie.stub
 		- stubs/DecoderInterface.stub
 		- stubs/DenormalizableInterface.stub
 		- stubs/DenormalizerInterface.stub
diff --git a/stubs/Cookie.stub b/stubs/Cookie.stub
new file mode 100644
index 00000000..40ee45ab
--- /dev/null
+++ b/stubs/Cookie.stub
@@ -0,0 +1,50 @@
+<?php
+
+namespace Symfony\Component\HttpFoundation;
+
+class Cookie
+{
+    public const SAMESITE_NONE = 'none';
+    public const SAMESITE_LAX = 'lax';
+    public const SAMESITE_STRICT = 'strict';
+
+    /**
+     * @param string                        $name     The name of the cookie
+     * @param string|null                   $value    The value of the cookie
+     * @param int|string|\DateTimeInterface $expire   The time the cookie expires
+     * @param string                        $path     The path on the server in which the cookie will be available on
+     * @param string|null                   $domain   The domain that the cookie is available to
+     * @param bool|null                     $secure   Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
+     * @param bool                          $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
+     * @param bool                          $raw      Whether the cookie value should be sent with no url encoding
+     * @param self::SAMESITE_*|''|null      $sameSite Whether the cookie will be available for cross-site requests
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null);
+
+    /**
+     * @param string                        $name     The name of the cookie
+     * @param string|null                   $value    The value of the cookie
+     * @param int|string|\DateTimeInterface $expire   The time the cookie expires
+     * @param string                        $path     The path on the server in which the cookie will be available on
+     * @param string|null                   $domain   The domain that the cookie is available to
+     * @param bool|null                     $secure   Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
+     * @param bool                          $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
+     * @param bool                          $raw      Whether the cookie value should be sent with no url encoding
+     * @param self::SAMESITE_*|''|null      $sameSite Whether the cookie will be available for cross-site requests
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null): self;
+
+    /**
+     * @return self::SAMESITE_*|null
+     */
+    public function getSameSite();
+
+    /**
+     * @param self::SAMESITE_*|''|null $sameSite
+     */
+    public function withSameSite(?string $sameSite): self;
+}

From f051037bfd2d6567cb48a5f09a6ba8e78c82986e Mon Sep 17 00:00:00 2001
From: Hedic Guibert <guiberthedic@gmail.com>
Date: Thu, 28 Jan 2021 11:20:16 +0100
Subject: [PATCH 058/297] Update README.md

---
 README.md | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/README.md b/README.md
index bf0296bb..f95f93dc 100644
--- a/README.md
+++ b/README.md
@@ -97,3 +97,22 @@ require __DIR__.'/../config/bootstrap.php';
 $kernel = new \App\Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
 return new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
 ```
+
+You may then encounter an error with PhpParser:
+
+```bash
+Compile Error: Cannot Declare interface PhpParser\NodeVisitor, because the name is already in use
+```
+
+If this is the case, you should create a new environment for your application that will disable inlining. In `config/packages/phpstan_env/parameters.yaml`:
+
+```yaml
+parameters:
+    container.dumper.inline_class_loader: false
+```
+
+Call the new env in your `console-application.php`:
+
+```php
+$kernel = new \App\Kernel('phpstan_env', (bool) $_SERVER['APP_DEBUG']);
+```

From 6f270710294b1b5a029464765bcb6d0f564ff3a1 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Thu, 11 Feb 2021 19:45:32 +0100
Subject: [PATCH 059/297] Support for TreeBuilder::getRootNode()

---
 composer.json                                 |  1 +
 extension.neon                                | 10 ++++
 .../TreeBuilderDynamicReturnTypeExtension.php | 56 +++++++++++++++++++
 ...rGetRootNodeDynamicReturnTypeExtension.php | 39 +++++++++++++
 src/Type/Symfony/TreeBuilderType.php          | 25 +++++++++
 .../EnvelopeReturnTypeExtensionTest.php       |  2 +-
 tests/Type/Symfony/ExtensionTestCase.php      | 13 ++++-
 ...eaderBagDynamicReturnTypeExtensionTest.php |  2 +-
 ...InputBagDynamicReturnTypeExtensionTest.php |  2 +-
 ...ArgumentDynamicReturnTypeExtensionTest.php |  2 +-
 ...etOptionDynamicReturnTypeExtensionTest.php |  2 +-
 ...nterfaceDynamicReturnTypeExtensionTest.php |  2 +-
 .../RequestDynamicReturnTypeExtensionTest.php |  2 +-
 ...rializerDynamicReturnTypeExtensionTest.php |  8 +--
 .../ServiceDynamicReturnTypeExtensionTest.php |  4 +-
 ...eBuilderDynamicReturnTypeExtensionTest.php | 39 +++++++++++++
 tests/Type/Symfony/tree_builder.php           | 29 ++++++++++
 17 files changed, 222 insertions(+), 16 deletions(-)
 create mode 100644 src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php
 create mode 100644 src/Type/Symfony/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
 create mode 100644 src/Type/Symfony/TreeBuilderType.php
 create mode 100644 tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php
 create mode 100644 tests/Type/Symfony/tree_builder.php

diff --git a/composer.json b/composer.json
index 484ea046..79e67f8e 100644
--- a/composer.json
+++ b/composer.json
@@ -27,6 +27,7 @@
     "phpstan/phpstan-strict-rules": "^0.12.5",
     "phpunit/phpunit": "^7.5.20",
     "symfony/console": "^4.0",
+    "symfony/config": "^4.2",
     "symfony/framework-bundle": "^4.0",
     "symfony/http-foundation": "^4.0",
     "symfony/messenger": "^4.2",
diff --git a/extension.neon b/extension.neon
index 4209bc61..819442f3 100644
--- a/extension.neon
+++ b/extension.neon
@@ -145,3 +145,13 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\InputInterfaceHasOptionDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# new TreeBuilder() return type
+	-
+		factory: PHPStan\Type\Symfony\TreeBuilderDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension]
+
+	# TreeBuilder::getRootNode() return type
+	-
+		factory: PHPStan\Type\Symfony\TreeBuilderGetRootNodeDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..56f29d35
--- /dev/null
+++ b/src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php
@@ -0,0 +1,56 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\StaticCall;
+use PhpParser\Node\Name;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeUtils;
+
+final class TreeBuilderDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
+{
+
+	private const MAPPING = [
+		'variable' => 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition',
+		'scalar' => 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition',
+		'boolean' => 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition',
+		'integer' => 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition',
+		'float' => 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition',
+		'array' => 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition',
+		'enum' => 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition',
+	];
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Config\Definition\Builder\TreeBuilder';
+	}
+
+	public function isStaticMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === '__construct';
+	}
+
+	public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): Type
+	{
+		if (!$methodCall->class instanceof Name) {
+			throw new \PHPStan\ShouldNotHappenException();
+		}
+
+		$className = $scope->resolveName($methodCall->class);
+
+		$type = 'array';
+
+		if (isset($methodCall->args[1])) {
+			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[1]->value));
+			if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) {
+				$type = $argStrings[0]->getValue();
+			}
+		}
+
+		return new TreeBuilderType($className, self::MAPPING[$type]);
+	}
+
+}
diff --git a/src/Type/Symfony/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..b8b00aba
--- /dev/null
+++ b/src/Type/Symfony/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
@@ -0,0 +1,39 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\Type;
+
+final class TreeBuilderGetRootNodeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Config\Definition\Builder\TreeBuilder';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getRootNode';
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		$calledOnType = $scope->getType($methodCall->var);
+		if ($calledOnType instanceof TreeBuilderType) {
+			return new ObjectType($calledOnType->getRootNodeClassName());
+		}
+
+		return $methodReflection->getVariants()[0]->getReturnType();
+	}
+
+}
diff --git a/src/Type/Symfony/TreeBuilderType.php b/src/Type/Symfony/TreeBuilderType.php
new file mode 100644
index 00000000..faf26c6a
--- /dev/null
+++ b/src/Type/Symfony/TreeBuilderType.php
@@ -0,0 +1,25 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PHPStan\Type\ObjectType;
+
+class TreeBuilderType extends ObjectType
+{
+
+	/** @var string */
+	private $rootNodeClassName;
+
+	public function __construct(string $className, string $rootNodeClassName)
+	{
+		parent::__construct($className);
+
+		$this->rootNodeClassName = $rootNodeClassName;
+	}
+
+	public function getRootNodeClassName(): string
+	{
+		return $this->rootNodeClassName;
+	}
+
+}
diff --git a/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php b/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
index 2e7d7266..ef59ccc4 100644
--- a/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
@@ -18,7 +18,7 @@ public function testAll(string $expression, string $type): void
 			__DIR__ . '/envelope_all.php',
 			$expression,
 			$type,
-			new EnvelopeReturnTypeExtension()
+			[new EnvelopeReturnTypeExtension()]
 		);
 	}
 
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index 49c4b0fa..26a805a6 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -17,21 +17,28 @@
 use PHPStan\PhpDoc\PhpDocStringResolver;
 use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider;
 use PHPStan\Testing\TestCase;
-use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FileTypeMapper;
 use PHPStan\Type\VerbosityLevel;
 
 abstract class ExtensionTestCase extends TestCase
 {
 
+	/**
+	 * @param string $file
+	 * @param string $expression
+	 * @param string $type
+	 * @param \PHPStan\Type\DynamicMethodReturnTypeExtension[] $dynamicMethodReturnTypeExtensions
+	 * @param \PHPStan\Type\DynamicStaticMethodReturnTypeExtension[] $dynamicStaticMethodReturnTypeExtensions
+	 */
 	protected function processFile(
 		string $file,
 		string $expression,
 		string $type,
-		DynamicMethodReturnTypeExtension $extension
+		array $dynamicMethodReturnTypeExtensions = [],
+		array $dynamicStaticMethodReturnTypeExtensions = []
 	): void
 	{
-		$broker = $this->createBroker([$extension]);
+		$broker = $this->createBroker($dynamicMethodReturnTypeExtensions, $dynamicStaticMethodReturnTypeExtensions);
 		$parser = $this->getParser();
 		$currentWorkingDirectory = $this->getCurrentWorkingDirectory();
 		$fileHelper = new FileHelper($currentWorkingDirectory);
diff --git a/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
index f4b7d4cb..f84f3032 100644
--- a/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
@@ -16,7 +16,7 @@ public function testGet(string $expression, string $type): void
 			__DIR__ . '/header_bag_get.php',
 			$expression,
 			$type,
-			new HeaderBagDynamicReturnTypeExtension()
+			[new HeaderBagDynamicReturnTypeExtension()]
 		);
 	}
 
diff --git a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
index 7e8f1d2a..b80b02ad 100644
--- a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
@@ -20,7 +20,7 @@ public function testGet(string $expression, string $type): void
 			__DIR__ . '/input_bag_get.php',
 			$expression,
 			$type,
-			new InputBagDynamicReturnTypeExtension()
+			[new InputBagDynamicReturnTypeExtension()]
 		);
 	}
 
diff --git a/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
index 9689018e..00d85e00 100644
--- a/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
@@ -17,7 +17,7 @@ public function testArgumentTypes(string $expression, string $type): void
 			__DIR__ . '/ExampleBaseCommand.php',
 			$expression,
 			$type,
-			new InputInterfaceGetArgumentDynamicReturnTypeExtension(new ConsoleApplicationResolver(__DiR__ . '/console_application_loader.php'))
+			[new InputInterfaceGetArgumentDynamicReturnTypeExtension(new ConsoleApplicationResolver(__DiR__ . '/console_application_loader.php'))]
 		);
 	}
 
diff --git a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
index affb0c62..ede5f68d 100644
--- a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
@@ -17,7 +17,7 @@ public function testArgumentTypes(string $expression, string $type): void
 			__DIR__ . '/ExampleOptionCommand.php',
 			$expression,
 			$type,
-			new InputInterfaceGetOptionDynamicReturnTypeExtension(new ConsoleApplicationResolver(__DiR__ . '/console_application_loader.php'))
+			[new InputInterfaceGetOptionDynamicReturnTypeExtension(new ConsoleApplicationResolver(__DiR__ . '/console_application_loader.php'))]
 		);
 	}
 
diff --git a/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
index a5178a51..40bef996 100644
--- a/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
@@ -16,7 +16,7 @@ public function testGet(string $expression, string $type): void
 			__DIR__ . '/kernel_interface.php',
 			$expression,
 			$type,
-			new KernelInterfaceDynamicReturnTypeExtension()
+			[new KernelInterfaceDynamicReturnTypeExtension()]
 		);
 	}
 
diff --git a/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
index 22572808..f0834674 100644
--- a/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
@@ -16,7 +16,7 @@ public function testGetContent(string $expression, string $type): void
 			__DIR__ . '/request_get_content.php',
 			$expression,
 			$type,
-			new RequestDynamicReturnTypeExtension()
+			[new RequestDynamicReturnTypeExtension()]
 		);
 	}
 
diff --git a/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
index 8b60042c..0857127f 100644
--- a/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
@@ -16,10 +16,10 @@ public function testSerializerInterface(string $expression, string $type): void
 			__DIR__ . '/serializer.php',
 			$expression,
 			$type,
-			new SerializerDynamicReturnTypeExtension(
+			[new SerializerDynamicReturnTypeExtension(
 				'Symfony\Component\Serializer\SerializerInterface',
 				'deserialize'
-			)
+			)]
 		);
 	}
 
@@ -32,10 +32,10 @@ public function testDenormalizerInterface(string $expression, string $type): voi
 			__DIR__ . '/denormalizer.php',
 			$expression,
 			$type,
-			new SerializerDynamicReturnTypeExtension(
+			[new SerializerDynamicReturnTypeExtension(
 				'Symfony\Component\Serializer\Normalizer\DenormalizerInterface',
 				'denormalize'
-			)
+			)]
 		);
 	}
 
diff --git a/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
index 9580bf91..8cfaa65d 100644
--- a/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
@@ -18,7 +18,7 @@ public function testServices(string $expression, string $type, ?string $containe
 			__DIR__ . '/ExampleController.php',
 			$expression,
 			$type,
-			new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory($container))->create())
+			[new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory($container))->create())]
 		);
 	}
 
@@ -55,7 +55,7 @@ public function testConstantHassersOff(string $expression, string $type, ?string
 			__DIR__ . '/ExampleController.php',
 			$expression,
 			$type,
-			new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory($container))->create())
+			[new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory($container))->create())]
 		);
 	}
 
diff --git a/tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php
new file mode 100644
index 00000000..bbb90af6
--- /dev/null
+++ b/tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php
@@ -0,0 +1,39 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Iterator;
+
+final class TreeBuilderDynamicReturnTypeExtensionTest extends ExtensionTestCase
+{
+
+	/**
+	 * @dataProvider getProvider
+	 */
+	public function testGet(string $expression, string $type): void
+	{
+		$this->processFile(
+			__DIR__ . '/tree_builder.php',
+			$expression,
+			$type,
+			[new TreeBuilderGetRootNodeDynamicReturnTypeExtension()],
+			[new TreeBuilderDynamicReturnTypeExtension()]
+		);
+	}
+
+	/**
+	 * @return \Iterator<array{string, string}>
+	 */
+	public function getProvider(): Iterator
+	{
+		yield ['$treeRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['$variableRootNode', 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition'];
+		yield ['$scalarRootNode', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
+		yield ['$booleanRootNode', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition'];
+		yield ['$integerRootNode', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition'];
+		yield ['$floatRootNode', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition'];
+		yield ['$arrayRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['$enumRootNode', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition'];
+	}
+
+}
diff --git a/tests/Type/Symfony/tree_builder.php b/tests/Type/Symfony/tree_builder.php
new file mode 100644
index 00000000..24d60d71
--- /dev/null
+++ b/tests/Type/Symfony/tree_builder.php
@@ -0,0 +1,29 @@
+<?php declare(strict_types = 1);
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+
+$treeBuilder = new TreeBuilder('my_tree');
+$treeRootNode = $treeBuilder->getRootNode();
+
+$variableTreeBuilder = new TreeBuilder('my_tree', 'variable');
+$variableRootNode = $variableTreeBuilder->getRootNode();
+
+$scalarTreeBuilder = new TreeBuilder('my_tree', 'scalar');
+$scalarRootNode = $scalarTreeBuilder->getRootNode();
+
+$booleanTreeBuilder = new TreeBuilder('my_tree', 'boolean');
+$booleanRootNode = $booleanTreeBuilder->getRootNode();
+
+$integerTreeBuilder = new TreeBuilder('my_tree', 'integer');
+$integerRootNode = $integerTreeBuilder->getRootNode();
+
+$floatTreeBuilder = new TreeBuilder('my_tree', 'float');
+$floatRootNode = $floatTreeBuilder->getRootNode();
+
+$arrayTreeBuilder = new TreeBuilder('my_tree', 'array');
+$arrayRootNode = $arrayTreeBuilder->getRootNode();
+
+$enumTreeBuilder = new TreeBuilder('my_tree', 'enum');
+$enumRootNode = $enumTreeBuilder->getRootNode();
+
+die;

From cc9d9ac264b8ac04961a03dc5b481b58ceaac248 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Sat, 13 Feb 2021 08:38:10 +0100
Subject: [PATCH 060/297] Add full support for Config/TreeBuilder

---
 README.md                                     |   1 +
 extension.neon                                |  52 ++++-
 ...ionPrototypeDynamicReturnTypeExtension.php |  77 +++++++
 ...ParentObjectDynamicReturnTypeExtension.php |  56 +++++
 ...ReturnParentDynamicReturnTypeExtension.php |  56 +++++
 .../TreeBuilderDynamicReturnTypeExtension.php |   3 +-
 ...rGetRootNodeDynamicReturnTypeExtension.php |  16 +-
 .../Config/ValueObject/ParentObjectType.php   |  26 +++
 .../ValueObject}/TreeBuilderType.php          |   2 +-
 tests/Type/Symfony/Config/TreeBuilderTest.php | 211 ++++++++++++++++++
 .../Symfony/{ => Config}/tree_builder.php     |   6 +-
 tests/Type/Symfony/ExtensionTestCase.php      |   2 +-
 ...eBuilderDynamicReturnTypeExtensionTest.php |  39 ----
 13 files changed, 495 insertions(+), 52 deletions(-)
 create mode 100644 src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
 create mode 100644 src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
 create mode 100644 src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
 rename src/Type/Symfony/{ => Config}/TreeBuilderDynamicReturnTypeExtension.php (94%)
 rename src/Type/Symfony/{ => Config}/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php (64%)
 create mode 100644 src/Type/Symfony/Config/ValueObject/ParentObjectType.php
 rename src/Type/Symfony/{ => Config/ValueObject}/TreeBuilderType.php (89%)
 create mode 100644 tests/Type/Symfony/Config/TreeBuilderTest.php
 rename tests/Type/Symfony/{ => Config}/tree_builder.php (100%)
 delete mode 100644 tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php

diff --git a/README.md b/README.md
index f95f93dc..02bfb1e2 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ This extension provides following features:
 * Provides correct return type for `Request::getContent()` method based on the `$asResource` parameter.
 * Provides correct return type for `HeaderBag::get()` method based on the `$first` parameter.
 * Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter.
+* Provides correct return types for `TreeBuilder` and `NodeDefinition` objects.
 * Notifies you when you try to get an unregistered service from the container.
 * Notifies you when you try to get a private service from the container.
 * Optionally correct return types for `InputInterface::getArgument()`, `::getOption`, `::hasArgument`, and `::hasOption`.
diff --git a/extension.neon b/extension.neon
index 819442f3..5046baa2 100644
--- a/extension.neon
+++ b/extension.neon
@@ -44,7 +44,8 @@ services:
 	# console resolver
 	-
 		factory: PHPStan\Symfony\ConsoleApplicationResolver
-		arguments: [%symfony.console_application_loader%]
+		arguments:
+		    consoleApplicationLoader: %symfony.console_application_loader%
 
 	# service map
 	symfony.serviceMapFactory:
@@ -146,12 +147,57 @@ services:
 		factory: PHPStan\Type\Symfony\InputInterfaceHasOptionDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
+	# ArrayNodeDefinition::*prototype() return type
+	-
+		factory: PHPStan\Type\Symfony\Config\ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# ExprBuilder::end() return type
+	-
+		factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+		arguments:
+		    className: Symfony\Component\Config\Definition\Builder\ExprBuilder
+		    methods: [end]
+
+	# NodeBuilder::*node() return type
+	-
+		factory: PHPStan\Type\Symfony\Config\PassParentObjectDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+		arguments:
+		    className: Symfony\Component\Config\Definition\Builder\NodeBuilder
+		    methods: [arrayNode, scalarNode, booleanNode, integerNode, floatNode, enumNode, variableNode]
+
+	# NodeBuilder::end() return type
+	-
+		factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+		arguments:
+		    className: Symfony\Component\Config\Definition\Builder\NodeBuilder
+		    methods: [end]
+
+	# NodeDefinition::children() return type
+	-
+		factory: PHPStan\Type\Symfony\Config\PassParentObjectDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+		arguments:
+		    className: Symfony\Component\Config\Definition\Builder\NodeDefinition
+		    methods: [children, validate]
+
+	# NodeDefinition::end() return type
+	-
+		factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+		arguments:
+		    className: Symfony\Component\Config\Definition\Builder\NodeDefinition
+		    methods: [end]
+
 	# new TreeBuilder() return type
 	-
-		factory: PHPStan\Type\Symfony\TreeBuilderDynamicReturnTypeExtension
+		factory: PHPStan\Type\Symfony\Config\TreeBuilderDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension]
 
 	# TreeBuilder::getRootNode() return type
 	-
-		factory: PHPStan\Type\Symfony\TreeBuilderGetRootNodeDynamicReturnTypeExtension
+		factory: PHPStan\Type\Symfony\Config\TreeBuilderGetRootNodeDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..71e4fb17
--- /dev/null
+++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
@@ -0,0 +1,77 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony\Config;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeUtils;
+use PHPStan\Type\VerbosityLevel;
+
+final class ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	private const PROTOTYPE_METHODS = [
+		'arrayPrototype',
+		'scalarPrototype',
+		'booleanPrototype',
+		'integerPrototype',
+		'floatPrototype',
+		'enumPrototype',
+		'variablePrototype',
+	];
+
+	private const MAPPING = [
+		'variable' => 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition',
+		'scalar' => 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition',
+		'boolean' => 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition',
+		'integer' => 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition',
+		'float' => 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition',
+		'array' => 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition',
+		'enum' => 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition',
+	];
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'prototype' || in_array($methodReflection->getName(), self::PROTOTYPE_METHODS, true);
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		$calledOnType = $scope->getType($methodCall->var);
+
+		$defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+
+		if ($methodReflection->getName() === 'prototype') {
+			if (!isset($methodCall->args[0])) {
+				return $defaultType;
+			}
+
+			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+			if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) {
+				$type = $argStrings[0]->getValue();
+
+				return new ParentObjectType(self::MAPPING[$type], $calledOnType);
+			}
+		}
+
+		return new ParentObjectType(
+			$defaultType->describe(VerbosityLevel::typeOnly()),
+			$calledOnType
+		);
+	}
+
+}
diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..5d19b60f
--- /dev/null
+++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
@@ -0,0 +1,56 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony\Config;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
+use PHPStan\Type\Type;
+use PHPStan\Type\VerbosityLevel;
+
+final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var string */
+	private $className;
+
+	/** @var string[] */
+	private $methods;
+
+	/**
+	 * @param string $className
+	 * @param string[] $methods
+	 */
+	public function __construct(string $className, array $methods)
+	{
+		$this->className = $className;
+		$this->methods = $methods;
+	}
+
+	public function getClass(): string
+	{
+		return $this->className;
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return in_array($methodReflection->getName(), $this->methods, true);
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		$calledOnType = $scope->getType($methodCall->var);
+
+		$defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+
+		return new ParentObjectType($defaultType->describe(VerbosityLevel::typeOnly()), $calledOnType);
+	}
+
+}
diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..8dd47a90
--- /dev/null
+++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
@@ -0,0 +1,56 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony\Config;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
+use PHPStan\Type\Type;
+
+final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var string */
+	private $className;
+
+	/** @var string[] */
+	private $methods;
+
+	/**
+	 * @param string $className
+	 * @param string[] $methods
+	 */
+	public function __construct(string $className, array $methods)
+	{
+		$this->className = $className;
+		$this->methods = $methods;
+	}
+
+	public function getClass(): string
+	{
+		return $this->className;
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return in_array($methodReflection->getName(), $this->methods, true);
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		$calledOnType = $scope->getType($methodCall->var);
+		if ($calledOnType instanceof ParentObjectType) {
+			return $calledOnType->getParent();
+		}
+
+		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+	}
+
+}
diff --git a/src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
similarity index 94%
rename from src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php
rename to src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
index 56f29d35..25cd12dc 100644
--- a/src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
@@ -1,12 +1,13 @@
 <?php declare(strict_types = 1);
 
-namespace PHPStan\Type\Symfony;
+namespace PHPStan\Type\Symfony\Config;
 
 use PhpParser\Node\Expr\StaticCall;
 use PhpParser\Node\Name;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
+use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeUtils;
 
diff --git a/src/Type/Symfony/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
similarity index 64%
rename from src/Type/Symfony/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
rename to src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
index b8b00aba..4b7c1b3a 100644
--- a/src/Type/Symfony/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
@@ -1,12 +1,14 @@
 <?php declare(strict_types = 1);
 
-namespace PHPStan\Type\Symfony;
+namespace PHPStan\Type\Symfony\Config;
 
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
-use PHPStan\Type\ObjectType;
+use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
+use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType;
 use PHPStan\Type\Type;
 
 final class TreeBuilderGetRootNodeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -29,11 +31,17 @@ public function getTypeFromMethodCall(
 	): Type
 	{
 		$calledOnType = $scope->getType($methodCall->var);
+
+		$defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+
 		if ($calledOnType instanceof TreeBuilderType) {
-			return new ObjectType($calledOnType->getRootNodeClassName());
+			return new ParentObjectType(
+				$calledOnType->getRootNodeClassName(),
+				$calledOnType
+			);
 		}
 
-		return $methodReflection->getVariants()[0]->getReturnType();
+		return $defaultType;
 	}
 
 }
diff --git a/src/Type/Symfony/Config/ValueObject/ParentObjectType.php b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
new file mode 100644
index 00000000..87435e56
--- /dev/null
+++ b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony\Config\ValueObject;
+
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\Type;
+
+class ParentObjectType extends ObjectType
+{
+
+	/** @var Type */
+	private $parent;
+
+	public function __construct(string $className, Type $parent)
+	{
+		parent::__construct($className);
+
+		$this->parent = $parent;
+	}
+
+	public function getParent(): Type
+	{
+		return $this->parent;
+	}
+
+}
diff --git a/src/Type/Symfony/TreeBuilderType.php b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
similarity index 89%
rename from src/Type/Symfony/TreeBuilderType.php
rename to src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
index faf26c6a..bacd0f0d 100644
--- a/src/Type/Symfony/TreeBuilderType.php
+++ b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
@@ -1,6 +1,6 @@
 <?php declare(strict_types = 1);
 
-namespace PHPStan\Type\Symfony;
+namespace PHPStan\Type\Symfony\Config\ValueObject;
 
 use PHPStan\Type\ObjectType;
 
diff --git a/tests/Type/Symfony/Config/TreeBuilderTest.php b/tests/Type/Symfony/Config/TreeBuilderTest.php
new file mode 100644
index 00000000..8aeb31a7
--- /dev/null
+++ b/tests/Type/Symfony/Config/TreeBuilderTest.php
@@ -0,0 +1,211 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony\Config;
+
+use Iterator;
+use PHPStan\Type\Symfony\ExtensionTestCase;
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+
+final class TreeBuilderTest extends ExtensionTestCase
+{
+
+	/**
+	 * @dataProvider getProvider
+	 */
+	public function testGet(string $expression, string $type): void
+	{
+		$arrayTreeBuilder = new TreeBuilder('my_tree', 'array');
+		$arrayRootNode = $arrayTreeBuilder->getRootNode();
+		$r = $arrayRootNode
+			->children()
+			->arrayNode('methods')
+			->prototype('scalar')
+			->validate()
+			->ifNotInArray(['one', 'two'])
+			->thenInvalid('%s is not a valid method.')
+			->end()
+			->end()
+			->end()
+			->end();
+
+		$this->processFile(
+			__DIR__ . '/tree_builder.php',
+			$expression,
+			$type,
+			[
+				new ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension(),
+				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\ExprBuilder', ['end']),
+				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeBuilder', ['end']),
+				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition', ['end']),
+				new PassParentObjectDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeBuilder', ['arrayNode', 'scalarNode', 'booleanNode', 'integerNode', 'floatNode', 'enumNode', 'variableNode']),
+				new PassParentObjectDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition', ['children', 'validate']),
+				new TreeBuilderGetRootNodeDynamicReturnTypeExtension(),
+			],
+			[new TreeBuilderDynamicReturnTypeExtension()]
+		);
+	}
+
+	/**
+	 * @return \Iterator<array{string, string}>
+	 */
+	public function getProvider(): Iterator
+	{
+		yield ['$treeRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$treeRootNode
+			->children()
+			->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$treeRootNode
+			->children()
+				->scalarNode("protocol")
+				->end()
+			->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$treeRootNode
+			->children()
+		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+					->children()
+		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+					->children()
+						->scalarNode("protocol")
+		', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+					->children()
+						->scalarNode("protocol")
+						->end()
+		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+					->children()
+						->scalarNode("protocol")
+						->end()
+					->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+					->children()
+						->scalarNode("protocol")
+						->end()
+					->end()
+				->end()
+		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+					->children()
+						->scalarNode("protocol")
+						->end()
+					->end()
+				->end()
+			->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$treeRootNode
+			->children()
+				->arrayNode("protocols")
+					->children()
+						->booleanNode("auto_connect")
+							->defaultTrue()
+						->end()
+						->scalarNode("default_connection")
+							->defaultValue("default")
+						->end()
+						->integerNode("positive_value")
+							->min(0)
+						->end()
+						->floatNode("big_value")
+							->max(5E45)
+						->end()
+						->enumNode("delivery")
+							->values(["standard", "expedited", "priority"])
+						->end()
+					->end()
+				->end()
+			->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+
+		yield ['$arrayRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['$arrayRootNode->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
+		yield ['
+		$arrayRootNode
+			->children()
+                ->arrayNode("methods")
+                    ->prototype("scalar")
+                        ->defaultNull()
+                    ->end()
+                ->end()
+            ->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$arrayRootNode
+			->children()
+                ->arrayNode("methods")
+                    ->scalarPrototype()
+                        ->defaultNull()
+                    ->end()
+                ->end()
+            ->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$arrayRootNode
+			->children()
+                ->arrayNode("methods")
+                    ->prototype("scalar")
+                        ->validate()
+                            ->ifNotInArray(["one", "two"])
+                            ->thenInvalid("%s is not a valid method.")
+                        ->end()
+                    ->end()
+                ->end()
+            ->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+
+		yield ['$variableRootNode', 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition'];
+		yield ['$variableRootNode->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
+
+		yield ['$scalarRootNode', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
+		yield ['$scalarRootNode->defaultValue("default")', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
+		yield ['$scalarRootNode->defaultValue("default")->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
+
+		yield ['$booleanRootNode', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition'];
+		yield ['$booleanRootNode->defaultTrue()', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition'];
+		yield ['$booleanRootNode->defaultTrue()->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
+
+		yield ['$integerRootNode', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition'];
+		yield ['$integerRootNode->min(0)', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition'];
+		yield ['$integerRootNode->min(0)->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
+
+		yield ['$floatRootNode', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition'];
+		yield ['$floatRootNode->max(5E45)', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition'];
+		yield ['$floatRootNode->max(5E45)->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
+
+		yield ['$enumRootNode', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition'];
+		yield ['$enumRootNode->values(["standard", "expedited", "priority"])', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition'];
+		yield ['$enumRootNode->values(["standard", "expedited", "priority"])->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
+	}
+
+}
diff --git a/tests/Type/Symfony/tree_builder.php b/tests/Type/Symfony/Config/tree_builder.php
similarity index 100%
rename from tests/Type/Symfony/tree_builder.php
rename to tests/Type/Symfony/Config/tree_builder.php
index 24d60d71..f1126608 100644
--- a/tests/Type/Symfony/tree_builder.php
+++ b/tests/Type/Symfony/Config/tree_builder.php
@@ -5,6 +5,9 @@
 $treeBuilder = new TreeBuilder('my_tree');
 $treeRootNode = $treeBuilder->getRootNode();
 
+$arrayTreeBuilder = new TreeBuilder('my_tree', 'array');
+$arrayRootNode = $arrayTreeBuilder->getRootNode();
+
 $variableTreeBuilder = new TreeBuilder('my_tree', 'variable');
 $variableRootNode = $variableTreeBuilder->getRootNode();
 
@@ -20,9 +23,6 @@
 $floatTreeBuilder = new TreeBuilder('my_tree', 'float');
 $floatRootNode = $floatTreeBuilder->getRootNode();
 
-$arrayTreeBuilder = new TreeBuilder('my_tree', 'array');
-$arrayRootNode = $arrayTreeBuilder->getRootNode();
-
 $enumTreeBuilder = new TreeBuilder('my_tree', 'enum');
 $enumRootNode = $enumTreeBuilder->getRootNode();
 
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index 26a805a6..21b95b69 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -84,7 +84,7 @@ function (Node $node, Scope $scope) use ($expression, $type, &$run): void {
 				}
 				/** @var \PhpParser\Node\Stmt\Expression $expNode */
 				$expNode = $this->getParser()->parseString(sprintf('<?php %s;', $expression))[0];
-				self::assertSame($type, $scope->getType($expNode->expr)->describe(VerbosityLevel::typeOnly()));
+				self::assertSame($type, $scope->getType($expNode->expr)->describe(VerbosityLevel::typeOnly()), sprintf('Expression "%s"', $expression));
 				$run = true;
 			}
 		);
diff --git a/tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index bbb90af6..00000000
--- a/tests/Type/Symfony/TreeBuilderDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-
-final class TreeBuilderDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider getProvider
-	 */
-	public function testGet(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/tree_builder.php',
-			$expression,
-			$type,
-			[new TreeBuilderGetRootNodeDynamicReturnTypeExtension()],
-			[new TreeBuilderDynamicReturnTypeExtension()]
-		);
-	}
-
-	/**
-	 * @return \Iterator<array{string, string}>
-	 */
-	public function getProvider(): Iterator
-	{
-		yield ['$treeRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['$variableRootNode', 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition'];
-		yield ['$scalarRootNode', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
-		yield ['$booleanRootNode', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition'];
-		yield ['$integerRootNode', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition'];
-		yield ['$floatRootNode', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition'];
-		yield ['$arrayRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['$enumRootNode', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition'];
-	}
-
-}

From d986994c32c11c67a6d565a959075d44f28a7122 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Mon, 15 Feb 2021 11:32:01 +0100
Subject: [PATCH 061/297] Fix beforeNormalization

---
 extension.neon                                |  2 +-
 tests/Type/Symfony/Config/TreeBuilderTest.php | 74 +++++++++----------
 2 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/extension.neon b/extension.neon
index 5046baa2..63d8b24f 100644
--- a/extension.neon
+++ b/extension.neon
@@ -182,7 +182,7 @@ services:
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 		arguments:
 		    className: Symfony\Component\Config\Definition\Builder\NodeDefinition
-		    methods: [children, validate]
+		    methods: [children, validate, beforeNormalization]
 
 	# NodeDefinition::end() return type
 	-
diff --git a/tests/Type/Symfony/Config/TreeBuilderTest.php b/tests/Type/Symfony/Config/TreeBuilderTest.php
index 8aeb31a7..67f77917 100644
--- a/tests/Type/Symfony/Config/TreeBuilderTest.php
+++ b/tests/Type/Symfony/Config/TreeBuilderTest.php
@@ -4,7 +4,6 @@
 
 use Iterator;
 use PHPStan\Type\Symfony\ExtensionTestCase;
-use Symfony\Component\Config\Definition\Builder\TreeBuilder;
 
 final class TreeBuilderTest extends ExtensionTestCase
 {
@@ -14,20 +13,6 @@ final class TreeBuilderTest extends ExtensionTestCase
 	 */
 	public function testGet(string $expression, string $type): void
 	{
-		$arrayTreeBuilder = new TreeBuilder('my_tree', 'array');
-		$arrayRootNode = $arrayTreeBuilder->getRootNode();
-		$r = $arrayRootNode
-			->children()
-			->arrayNode('methods')
-			->prototype('scalar')
-			->validate()
-			->ifNotInArray(['one', 'two'])
-			->thenInvalid('%s is not a valid method.')
-			->end()
-			->end()
-			->end()
-			->end();
-
 		$this->processFile(
 			__DIR__ . '/tree_builder.php',
 			$expression,
@@ -38,7 +23,7 @@ public function testGet(string $expression, string $type): void
 				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeBuilder', ['end']),
 				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition', ['end']),
 				new PassParentObjectDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeBuilder', ['arrayNode', 'scalarNode', 'booleanNode', 'integerNode', 'floatNode', 'enumNode', 'variableNode']),
-				new PassParentObjectDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition', ['children', 'validate']),
+				new PassParentObjectDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition', ['children', 'validate', 'beforeNormalization']),
 				new TreeBuilderGetRootNodeDynamicReturnTypeExtension(),
 			],
 			[new TreeBuilderDynamicReturnTypeExtension()]
@@ -153,35 +138,50 @@ public function getProvider(): Iterator
 		yield ['
 		$arrayRootNode
 			->children()
-                ->arrayNode("methods")
-                    ->prototype("scalar")
-                        ->defaultNull()
-                    ->end()
-                ->end()
-            ->end()
+				->arrayNode("methods")
+					->prototype("scalar")
+						->defaultNull()
+					->end()
+				->end()
+			->end()
 		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
 		yield ['
 		$arrayRootNode
 			->children()
-                ->arrayNode("methods")
-                    ->scalarPrototype()
-                        ->defaultNull()
-                    ->end()
-                ->end()
-            ->end()
+				->arrayNode("methods")
+					->scalarPrototype()
+						->defaultNull()
+					->end()
+				->end()
+			->end()
 		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
 		yield ['
 		$arrayRootNode
 			->children()
-                ->arrayNode("methods")
-                    ->prototype("scalar")
-                        ->validate()
-                            ->ifNotInArray(["one", "two"])
-                            ->thenInvalid("%s is not a valid method.")
-                        ->end()
-                    ->end()
-                ->end()
-            ->end()
+				->arrayNode("methods")
+					->prototype("scalar")
+						->validate()
+							->ifNotInArray(["one", "two"])
+							->thenInvalid("%s is not a valid method.")
+						->end()
+					->end()
+				->end()
+			->end()
+		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
+		yield ['
+		$arrayRootNode
+			->children()
+				->arrayNode("methods")
+					->prototype("array")
+						->beforeNormalization()
+							->ifString()
+							->then(static function ($v) {
+								return [$v];
+							})
+						->end()
+					->end()
+				->end()
+			->end()
 		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
 
 		yield ['$variableRootNode', 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition'];

From ab56a2efc564243c0962432e1d12012655e79ea7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edi=20Modri=C4=87?= <edi.modric@gmail.com>
Date: Fri, 19 Feb 2021 12:55:30 +0100
Subject: [PATCH 062/297] Add stub for GenericEvent (#135)

---
 extension.neon          |  1 +
 stubs/GenericEvent.stub | 11 +++++++++++
 2 files changed, 12 insertions(+)
 create mode 100644 stubs/GenericEvent.stub

diff --git a/extension.neon b/extension.neon
index 63d8b24f..c4e7b36c 100644
--- a/extension.neon
+++ b/extension.neon
@@ -24,6 +24,7 @@ parameters:
 		- stubs/FormTypeExtensionInterface.stub
 		- stubs/FormTypeInterface.stub
 		- stubs/FormView.stub
+		- stubs/GenericEvent.stub
 		- stubs/HeaderBag.stub
 		- stubs/Node.stub
 		- stubs/NormalizableInterface.stub
diff --git a/stubs/GenericEvent.stub b/stubs/GenericEvent.stub
new file mode 100644
index 00000000..6e57a7df
--- /dev/null
+++ b/stubs/GenericEvent.stub
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\EventDispatcher;
+
+/**
+ * @implements \IteratorAggregate<string, mixed>
+ */
+class GenericEvent implements \IteratorAggregate
+{
+
+}

From 9cd6919b8c695206dc24b5b5e8a082cd3b8f4c05 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Mon, 8 Mar 2021 09:52:56 +0100
Subject: [PATCH 063/297] Configure dynamic constant Kernel::VERSION_ID

---
 extension.neon | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/extension.neon b/extension.neon
index c4e7b36c..e8c187d6 100644
--- a/extension.neon
+++ b/extension.neon
@@ -1,4 +1,6 @@
 parameters:
+	dynamicConstantNames:
+		- Symfony\Component\HttpKernel\Kernel::VERSION_ID
 	symfony:
 		container_xml_path: null
 		constant_hassers: true

From eecfb36da14af1384e64e6e71b433d3706abc680 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 28 Mar 2021 15:02:40 +0200
Subject: [PATCH 064/297] Fix build

---
 composer.json                            | 2 +-
 tests/Type/Symfony/ExtensionTestCase.php | 4 +++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 79e67f8e..46dbf2d5 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.51"
+    "phpstan/phpstan": "^0.12.83"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index 21b95b69..18e1668d 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -67,7 +67,9 @@ protected function processFile(
 			true,
 			true,
 			[],
-			[]
+			[],
+			false,
+			true
 		);
 		$resolver->setAnalysedFiles([$fileHelper->normalizePath($file)]);
 

From 37c4d36096bd6fa5209841165eb27b99077611f8 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 28 Mar 2021 21:08:48 +0200
Subject: [PATCH 065/297] New PHPStan option was renamed to implicitThrows

---
 tests/Type/Symfony/ExtensionTestCase.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
index 18e1668d..a69c6e59 100644
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ b/tests/Type/Symfony/ExtensionTestCase.php
@@ -68,7 +68,7 @@ protected function processFile(
 			true,
 			[],
 			[],
-			false,
+			true,
 			true
 		);
 		$resolver->setAnalysedFiles([$fileHelper->normalizePath($file)]);

From 4f94ccd437d7031d3513807ad8d4aee551813e0e Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 2 Apr 2021 18:53:00 +0200
Subject: [PATCH 066/297] Forward compatibility with future PHPStan version

---
 src/Type/Symfony/Config/ValueObject/ParentObjectType.php | 6 ++++++
 src/Type/Symfony/Config/ValueObject/TreeBuilderType.php  | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/src/Type/Symfony/Config/ValueObject/ParentObjectType.php b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
index 87435e56..56aec24f 100644
--- a/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
+++ b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
@@ -4,6 +4,7 @@
 
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
+use PHPStan\Type\VerbosityLevel;
 
 class ParentObjectType extends ObjectType
 {
@@ -23,4 +24,9 @@ public function getParent(): Type
 		return $this->parent;
 	}
 
+	protected function describeAdditionalCacheKey(): string
+	{
+		return $this->parent->describe(VerbosityLevel::cache());
+	}
+
 }
diff --git a/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
index bacd0f0d..71bc4fc2 100644
--- a/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
+++ b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
@@ -22,4 +22,9 @@ public function getRootNodeClassName(): string
 		return $this->rootNodeClassName;
 	}
 
+	protected function describeAdditionalCacheKey(): string
+	{
+		return $this->getRootNodeClassName();
+	}
+
 }

From 0f59ccea9695e0604bf64e4bed8df4269ede5d7e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 1 Apr 2021 05:30:04 +0000
Subject: [PATCH 067/297] Bump metcalfc/changelog-generator from v0.4.4 to
 v1.0.0

Bumps [metcalfc/changelog-generator](https://github.com/metcalfc/changelog-generator) from v0.4.4 to v1.0.0.
- [Release notes](https://github.com/metcalfc/changelog-generator/releases)
- [Changelog](https://github.com/metcalfc/changelog-generator/blob/main/release-notes.png)
- [Commits](https://github.com/metcalfc/changelog-generator/compare/v0.4.4...e5306b306fa2e34f05258789e0e5c526c1bd4352)

Signed-off-by: dependabot[bot] <support@github.com>
---
 .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 e04c2563..225470a6 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@v0.4.4
+        uses: metcalfc/changelog-generator@v1.0.0
         with:
           myToken: ${{ secrets.GITHUB_TOKEN }}
 

From 436ee2575622b1226c404cbc962b903f2fb58bbf Mon Sep 17 00:00:00 2001
From: Gocha Ossinkine <ossinkine@ya.ru>
Date: Mon, 12 Apr 2021 16:17:19 +0300
Subject: [PATCH 068/297] Fix serializer stubs

---
 stubs/ContextAwareDenormalizerInterface.stub | 2 +-
 stubs/ContextAwareNormalizerInterface.stub   | 2 +-
 stubs/DenormalizerInterface.stub             | 4 ++--
 stubs/EncoderInterface.stub                  | 2 +-
 stubs/NormalizerInterface.stub               | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/stubs/ContextAwareDenormalizerInterface.stub b/stubs/ContextAwareDenormalizerInterface.stub
index 4c3bdbc8..a0ae8f81 100644
--- a/stubs/ContextAwareDenormalizerInterface.stub
+++ b/stubs/ContextAwareDenormalizerInterface.stub
@@ -6,7 +6,7 @@ interface ContextAwareDenormalizerInterface extends DenormalizerInterface
 {
     /**
      * @param mixed $data
-     * @param string $format
+     * @param string|null $format
      * @param array<mixed> $context
      * @return bool
      */
diff --git a/stubs/ContextAwareNormalizerInterface.stub b/stubs/ContextAwareNormalizerInterface.stub
index 282354a8..aaf97be5 100644
--- a/stubs/ContextAwareNormalizerInterface.stub
+++ b/stubs/ContextAwareNormalizerInterface.stub
@@ -6,7 +6,7 @@ interface ContextAwareNormalizerInterface extends NormalizerInterface
 {
     /**
      * @param mixed $data
-     * @param string $format
+     * @param string|null $format
      * @param array<mixed> $context
      * @return bool
      */
diff --git a/stubs/DenormalizerInterface.stub b/stubs/DenormalizerInterface.stub
index 86ca8bec..83c6ddff 100644
--- a/stubs/DenormalizerInterface.stub
+++ b/stubs/DenormalizerInterface.stub
@@ -7,7 +7,7 @@ interface DenormalizerInterface
     /**
      * @param mixed $data
      * @param string $type
-     * @param string $format
+     * @param string|null $format
      * @param array<mixed> $context
      * @return object|array<mixed>
      */
@@ -16,7 +16,7 @@ interface DenormalizerInterface
     /**
      * @param mixed $data
      * @param string $type
-     * @param string $format
+     * @param string|null $format
      * @return bool
      */
     public function supportsDenormalization($data, $type, $format = null);
diff --git a/stubs/EncoderInterface.stub b/stubs/EncoderInterface.stub
index a3092757..eb6fc97c 100644
--- a/stubs/EncoderInterface.stub
+++ b/stubs/EncoderInterface.stub
@@ -8,7 +8,7 @@ interface EncoderInterface
      * @param mixed $data
      * @param string $format
      * @param array<mixed> $context
-     * @return string|int|float|bool
+     * @return string
      */
     public function encode($data, $format, array $context = []);
 
diff --git a/stubs/NormalizerInterface.stub b/stubs/NormalizerInterface.stub
index 907e296e..7e6ce400 100644
--- a/stubs/NormalizerInterface.stub
+++ b/stubs/NormalizerInterface.stub
@@ -6,7 +6,7 @@ interface NormalizerInterface
 {
     /**
      * @param mixed $object
-     * @param string $format
+     * @param string|null $format
      * @param array<mixed> $context
      * @return array<mixed>|string|int|float|bool|null
      */

From 76d75e326a3b47587c44a2b3aaadeae24f86adcc Mon Sep 17 00:00:00 2001
From: Gocha Ossinkine <ossinkine@ya.ru>
Date: Tue, 13 Apr 2021 13:57:04 +0300
Subject: [PATCH 069/297] Add missed arguments and methods to serializer stubs

---
 stubs/ContextAwareDenormalizerInterface.stub | 1 +
 stubs/NormalizerInterface.stub               | 7 +++++++
 2 files changed, 8 insertions(+)

diff --git a/stubs/ContextAwareDenormalizerInterface.stub b/stubs/ContextAwareDenormalizerInterface.stub
index a0ae8f81..9d42b711 100644
--- a/stubs/ContextAwareDenormalizerInterface.stub
+++ b/stubs/ContextAwareDenormalizerInterface.stub
@@ -6,6 +6,7 @@ interface ContextAwareDenormalizerInterface extends DenormalizerInterface
 {
     /**
      * @param mixed $data
+     * @param string $type
      * @param string|null $format
      * @param array<mixed> $context
      * @return bool
diff --git a/stubs/NormalizerInterface.stub b/stubs/NormalizerInterface.stub
index 7e6ce400..a65b5bed 100644
--- a/stubs/NormalizerInterface.stub
+++ b/stubs/NormalizerInterface.stub
@@ -11,4 +11,11 @@ interface NormalizerInterface
      * @return array<mixed>|string|int|float|bool|null
      */
     public function normalize($object, $format = null, array $context = []);
+
+    /**
+     * @param mixed $data
+     * @param string|null $format
+     * @return bool
+     */
+    public function supportsNormalization($data, $format = null);
 }

From 3a9a45ce930acd94d45012d42ec8f8fd3ac4b1c7 Mon Sep 17 00:00:00 2001
From: Leon Gersen <leongersen@gmail.com>
Date: Tue, 13 Apr 2021 11:59:19 +0200
Subject: [PATCH 070/297] Set php line endings for windows users with
 core.autocrlf enabled

---
 .gitattributes | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.gitattributes b/.gitattributes
index 24fe5a9f..823b5a81 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,5 @@
+*.php text eol=lf
+
 tests export-ignore
 .coveralls.yml export-ignore
 .gitattributes export-ignore

From 8a062af1cb7e9609d7b2a85d9b16060b1a35cee7 Mon Sep 17 00:00:00 2001
From: Leon Gersen <leongersen@gmail.com>
Date: Tue, 13 Apr 2021 11:49:23 +0200
Subject: [PATCH 071/297] Add support for InputBag::all (Symfony 5+)

---
 README.md                                     |  2 ++
 phpstan.neon                                  |  2 +-
 .../InputBagDynamicReturnTypeExtension.php    | 34 ++++++++++++++++++-
 ...InputBagDynamicReturnTypeExtensionTest.php | 10 +++---
 .../{input_bag_get.php => input_bag.php}      |  4 ++-
 5 files changed, 45 insertions(+), 7 deletions(-)
 rename tests/Type/Symfony/{input_bag_get.php => input_bag.php} (75%)

diff --git a/README.md b/README.md
index 02bfb1e2..0081f2ad 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@ This extension provides following features:
 * Provides correct return type for `Request::getContent()` method based on the `$asResource` parameter.
 * Provides correct return type for `HeaderBag::get()` method based on the `$first` parameter.
 * Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter.
+* Provides correct return type for `InputBag::get()` method based on the `$default` parameter.
+* Provides correct return type for `InputBag::all()` method based on the `$key` parameter.
 * Provides correct return types for `TreeBuilder` and `NodeDefinition` objects.
 * Notifies you when you try to get an unregistered service from the container.
 * Notifies you when you try to get a private service from the container.
diff --git a/phpstan.neon b/phpstan.neon
index f1ef1861..bc33def1 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -13,7 +13,7 @@ parameters:
 		- tests/*/console_application_loader.php
 		- tests/*/envelope_all.php
 		- tests/*/header_bag_get.php
-		- tests/*/input_bag_get.php
+		- tests/*/input_bag.php
 		- tests/*/kernel_interface.php
 		- tests/*/request_get_content.php
 		- tests/*/request_get_session.php
diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
index 747a2312..51e5ff19 100644
--- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
@@ -6,10 +6,14 @@
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\ShouldNotHappenException;
+use PHPStan\Type\ArrayType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\MixedType;
 use PHPStan\Type\NullType;
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
+use PHPStan\Type\UnionType;
 
 final class InputBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
@@ -21,7 +25,7 @@ public function getClass(): string
 
 	public function isMethodSupported(MethodReflection $methodReflection): bool
 	{
-		return $methodReflection->getName() === 'get';
+		return in_array($methodReflection->getName(), ['get', 'all'], true);
 	}
 
 	public function getTypeFromMethodCall(
@@ -29,6 +33,23 @@ public function getTypeFromMethodCall(
 		MethodCall $methodCall,
 		Scope $scope
 	): Type
+	{
+		if ($methodReflection->getName() === 'get') {
+			return $this->getGetTypeFromMethodCall($methodReflection, $methodCall, $scope);
+		}
+
+		if ($methodReflection->getName() === 'all') {
+			return $this->getAllTypeFromMethodCall($methodCall);
+		}
+
+		throw new ShouldNotHappenException();
+	}
+
+	private function getGetTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
 	{
 		if (isset($methodCall->args[1])) {
 			$argType = $scope->getType($methodCall->args[1]->value);
@@ -43,4 +64,15 @@ public function getTypeFromMethodCall(
 		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
 	}
 
+	private function getAllTypeFromMethodCall(
+		MethodCall $methodCall
+	): Type
+	{
+		if (isset($methodCall->args[0])) {
+			return new ArrayType(new MixedType(), new StringType());
+		}
+
+		return new ArrayType(new StringType(), new UnionType([new StringType(), new ArrayType(new MixedType(), new StringType())]));
+	}
+
 }
diff --git a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
index b80b02ad..f1a2a520 100644
--- a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
@@ -8,16 +8,16 @@ final class InputBagDynamicReturnTypeExtensionTest extends ExtensionTestCase
 {
 
 	/**
-	 * @dataProvider getProvider
+	 * @dataProvider inputBagProvider
 	 */
-	public function testGet(string $expression, string $type): void
+	public function testInputBag(string $expression, string $type): void
 	{
 		if (!class_exists('Symfony\\Component\\HttpFoundation\\InputBag')) {
 			self::markTestSkipped('The test needs Symfony\Component\HttpFoundation\InputBag class.');
 		}
 
 		$this->processFile(
-			__DIR__ . '/input_bag_get.php',
+			__DIR__ . '/input_bag.php',
 			$expression,
 			$type,
 			[new InputBagDynamicReturnTypeExtension()]
@@ -27,12 +27,14 @@ public function testGet(string $expression, string $type): void
 	/**
 	 * @return \Iterator<array{string, string}>
 	 */
-	public function getProvider(): Iterator
+	public function inputBagProvider(): Iterator
 	{
 		yield ['$test1', 'string|null'];
 		yield ['$test2', 'string|null'];
 		yield ['$test3', 'string'];
 		yield ['$test4', 'string'];
+		yield ['$test5', 'array<string, array<string>|string>'];
+		yield ['$test6', 'array<string>'];
 	}
 
 }
diff --git a/tests/Type/Symfony/input_bag_get.php b/tests/Type/Symfony/input_bag.php
similarity index 75%
rename from tests/Type/Symfony/input_bag_get.php
rename to tests/Type/Symfony/input_bag.php
index 756ff2af..7a36d61f 100644
--- a/tests/Type/Symfony/input_bag_get.php
+++ b/tests/Type/Symfony/input_bag.php
@@ -1,10 +1,12 @@
 <?php declare(strict_types = 1);
 
-$bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar']);
+$bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar', 'bar' => ['x']]);
 
 $test1 = $bag->get('foo');
 $test2 = $bag->get('foo', null);
 $test3 = $bag->get('foo', '');
 $test4 = $bag->get('foo', 'baz');
+$test5 = $bag->all();
+$test6 = $bag->all('bar');
 
 die;

From a66467f750149fd022cb51457295f20d0b36be50 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Sun, 18 Apr 2021 20:36:13 +0200
Subject: [PATCH 072/297] Follow namespace for folder structure

---
 extension.neon                                | 58 +++++++++----------
 .../ContainerBuilder.stub                     |  0
 .../Extension}/ExtensionInterface.stub        |  0
 .../EventSubscriberInterface.stub             |  0
 .../EventDispatcher}/GenericEvent.stub        |  0
 .../Loader}/ChoiceLoaderInterface.stub        |  0
 .../Component/Form}/FormBuilderInterface.stub |  0
 .../Component/Form}/FormInterface.stub        |  0
 .../Form}/FormTypeExtensionInterface.stub     |  0
 .../Component/Form}/FormTypeInterface.stub    |  0
 .../Component/Form}/FormView.stub             |  0
 .../Component/HttpFoundation}/Cookie.stub     |  0
 .../Component/HttpFoundation}/HeaderBag.stub  |  0
 .../HttpFoundation}/ParameterBag.stub         |  0
 .../Component/HttpFoundation}/Session.stub    |  0
 .../Component/Process}/Process.stub           |  0
 .../PropertyPathInterface.stub                |  0
 .../ContextAwareDecoderInterface.stub         |  0
 .../Serializer/Encoder}/DecoderInterface.stub |  0
 .../Serializer/Encoder}/EncoderInterface.stub |  0
 .../ContextAwareDenormalizerInterface.stub    |  0
 .../ContextAwareNormalizerInterface.stub      |  0
 .../Normalizer}/DenormalizableInterface.stub  |  0
 .../Normalizer}/DenormalizerInterface.stub    |  0
 .../Normalizer}/NormalizableInterface.stub    |  0
 .../Normalizer}/NormalizerInterface.stub      |  0
 .../Component/Validator}/Constraint.stub      |  0
 .../ConstraintViolationInterface.stub         |  0
 .../ConstraintViolationListInterface.stub     |  0
 stubs/{ => Twig/Node}/Node.stub               |  0
 30 files changed, 29 insertions(+), 29 deletions(-)
 rename stubs/{ => Symfony/Component/DependencyInjection}/ContainerBuilder.stub (100%)
 rename stubs/{ => Symfony/Component/DependencyInjection/Extension}/ExtensionInterface.stub (100%)
 rename stubs/{ => Symfony/Component/EventDispatcher}/EventSubscriberInterface.stub (100%)
 rename stubs/{ => Symfony/Component/EventDispatcher}/GenericEvent.stub (100%)
 rename stubs/{ => Symfony/Component/Form/ChoiceList/Loader}/ChoiceLoaderInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Form}/FormBuilderInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Form}/FormInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Form}/FormTypeExtensionInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Form}/FormTypeInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Form}/FormView.stub (100%)
 rename stubs/{ => Symfony/Component/HttpFoundation}/Cookie.stub (100%)
 rename stubs/{ => Symfony/Component/HttpFoundation}/HeaderBag.stub (100%)
 rename stubs/{ => Symfony/Component/HttpFoundation}/ParameterBag.stub (100%)
 rename stubs/{ => Symfony/Component/HttpFoundation}/Session.stub (100%)
 rename stubs/{ => Symfony/Component/Process}/Process.stub (100%)
 rename stubs/{ => Symfony/Component/PropertyAccess}/PropertyPathInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Encoder}/ContextAwareDecoderInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Encoder}/DecoderInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Encoder}/EncoderInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Normalizer}/ContextAwareDenormalizerInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Normalizer}/ContextAwareNormalizerInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Normalizer}/DenormalizableInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Normalizer}/DenormalizerInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Normalizer}/NormalizableInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Serializer/Normalizer}/NormalizerInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Validator}/Constraint.stub (100%)
 rename stubs/{ => Symfony/Component/Validator}/ConstraintViolationInterface.stub (100%)
 rename stubs/{ => Symfony/Component/Validator}/ConstraintViolationListInterface.stub (100%)
 rename stubs/{ => Twig/Node}/Node.stub (100%)

diff --git a/extension.neon b/extension.neon
index e8c187d6..dfb804af 100644
--- a/extension.neon
+++ b/extension.neon
@@ -6,35 +6,35 @@ parameters:
 		constant_hassers: true
 		console_application_loader: null
 	stubFiles:
-		- stubs/ChoiceLoaderInterface.stub
-		- stubs/Constraint.stub
-		- stubs/ConstraintViolationInterface.stub
-		- stubs/ConstraintViolationListInterface.stub
-		- stubs/ContainerBuilder.stub
-		- stubs/ContextAwareDecoderInterface.stub
-		- stubs/ContextAwareDenormalizerInterface.stub
-		- stubs/ContextAwareNormalizerInterface.stub
-		- stubs/Cookie.stub
-		- stubs/DecoderInterface.stub
-		- stubs/DenormalizableInterface.stub
-		- stubs/DenormalizerInterface.stub
-		- stubs/EncoderInterface.stub
-		- stubs/EventSubscriberInterface.stub
-		- stubs/ExtensionInterface.stub
-		- stubs/FormBuilderInterface.stub
-		- stubs/FormInterface.stub
-		- stubs/FormTypeExtensionInterface.stub
-		- stubs/FormTypeInterface.stub
-		- stubs/FormView.stub
-		- stubs/GenericEvent.stub
-		- stubs/HeaderBag.stub
-		- stubs/Node.stub
-		- stubs/NormalizableInterface.stub
-		- stubs/NormalizerInterface.stub
-		- stubs/ParameterBag.stub
-		- stubs/Process.stub
-		- stubs/PropertyPathInterface.stub
-		- stubs/Session.stub
+		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
+		- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
+		- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
+		- stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
+		- stubs/Symfony/Component/EventDispatcher/GenericEvent.stub
+		- stubs/Symfony/Component/Form/FormBuilderInterface.stub
+		- stubs/Symfony/Component/Form/FormInterface.stub
+		- stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
+		- stubs/Symfony/Component/Form/FormTypeInterface.stub
+		- stubs/Symfony/Component/Form/FormView.stub
+		- stubs/Symfony/Component/HttpFoundation/Cookie.stub
+		- stubs/Symfony/Component/HttpFoundation/HeaderBag.stub
+		- stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
+		- stubs/Symfony/Component/HttpFoundation/Session.stub
+		- stubs/Symfony/Component/Process/Process.stub
+		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
+		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
+		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
+		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
+		- stubs/Symfony/Component/Serializer/Normalizer/ContextAwareDenormalizerInterface.stub
+		- stubs/Symfony/Component/Serializer/Normalizer/ContextAwareNormalizerInterface.stub
+		- stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub
+		- stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
+		- stubs/Symfony/Component/Serializer/Normalizer/NormalizableInterface.stub
+		- stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
+		- stubs/Symfony/Component/Validator/Constraint.stub
+		- stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
+		- stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
+		- stubs/Twig/Node/Node.stub
 
 parametersSchema:
 	symfony: structure([
diff --git a/stubs/ContainerBuilder.stub b/stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
similarity index 100%
rename from stubs/ContainerBuilder.stub
rename to stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
diff --git a/stubs/ExtensionInterface.stub b/stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
similarity index 100%
rename from stubs/ExtensionInterface.stub
rename to stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
diff --git a/stubs/EventSubscriberInterface.stub b/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
similarity index 100%
rename from stubs/EventSubscriberInterface.stub
rename to stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
diff --git a/stubs/GenericEvent.stub b/stubs/Symfony/Component/EventDispatcher/GenericEvent.stub
similarity index 100%
rename from stubs/GenericEvent.stub
rename to stubs/Symfony/Component/EventDispatcher/GenericEvent.stub
diff --git a/stubs/ChoiceLoaderInterface.stub b/stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
similarity index 100%
rename from stubs/ChoiceLoaderInterface.stub
rename to stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
diff --git a/stubs/FormBuilderInterface.stub b/stubs/Symfony/Component/Form/FormBuilderInterface.stub
similarity index 100%
rename from stubs/FormBuilderInterface.stub
rename to stubs/Symfony/Component/Form/FormBuilderInterface.stub
diff --git a/stubs/FormInterface.stub b/stubs/Symfony/Component/Form/FormInterface.stub
similarity index 100%
rename from stubs/FormInterface.stub
rename to stubs/Symfony/Component/Form/FormInterface.stub
diff --git a/stubs/FormTypeExtensionInterface.stub b/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
similarity index 100%
rename from stubs/FormTypeExtensionInterface.stub
rename to stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
diff --git a/stubs/FormTypeInterface.stub b/stubs/Symfony/Component/Form/FormTypeInterface.stub
similarity index 100%
rename from stubs/FormTypeInterface.stub
rename to stubs/Symfony/Component/Form/FormTypeInterface.stub
diff --git a/stubs/FormView.stub b/stubs/Symfony/Component/Form/FormView.stub
similarity index 100%
rename from stubs/FormView.stub
rename to stubs/Symfony/Component/Form/FormView.stub
diff --git a/stubs/Cookie.stub b/stubs/Symfony/Component/HttpFoundation/Cookie.stub
similarity index 100%
rename from stubs/Cookie.stub
rename to stubs/Symfony/Component/HttpFoundation/Cookie.stub
diff --git a/stubs/HeaderBag.stub b/stubs/Symfony/Component/HttpFoundation/HeaderBag.stub
similarity index 100%
rename from stubs/HeaderBag.stub
rename to stubs/Symfony/Component/HttpFoundation/HeaderBag.stub
diff --git a/stubs/ParameterBag.stub b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
similarity index 100%
rename from stubs/ParameterBag.stub
rename to stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
diff --git a/stubs/Session.stub b/stubs/Symfony/Component/HttpFoundation/Session.stub
similarity index 100%
rename from stubs/Session.stub
rename to stubs/Symfony/Component/HttpFoundation/Session.stub
diff --git a/stubs/Process.stub b/stubs/Symfony/Component/Process/Process.stub
similarity index 100%
rename from stubs/Process.stub
rename to stubs/Symfony/Component/Process/Process.stub
diff --git a/stubs/PropertyPathInterface.stub b/stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
similarity index 100%
rename from stubs/PropertyPathInterface.stub
rename to stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
diff --git a/stubs/ContextAwareDecoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
similarity index 100%
rename from stubs/ContextAwareDecoderInterface.stub
rename to stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
diff --git a/stubs/DecoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
similarity index 100%
rename from stubs/DecoderInterface.stub
rename to stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
diff --git a/stubs/EncoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
similarity index 100%
rename from stubs/EncoderInterface.stub
rename to stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
diff --git a/stubs/ContextAwareDenormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/ContextAwareDenormalizerInterface.stub
similarity index 100%
rename from stubs/ContextAwareDenormalizerInterface.stub
rename to stubs/Symfony/Component/Serializer/Normalizer/ContextAwareDenormalizerInterface.stub
diff --git a/stubs/ContextAwareNormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/ContextAwareNormalizerInterface.stub
similarity index 100%
rename from stubs/ContextAwareNormalizerInterface.stub
rename to stubs/Symfony/Component/Serializer/Normalizer/ContextAwareNormalizerInterface.stub
diff --git a/stubs/DenormalizableInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub
similarity index 100%
rename from stubs/DenormalizableInterface.stub
rename to stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub
diff --git a/stubs/DenormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
similarity index 100%
rename from stubs/DenormalizerInterface.stub
rename to stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
diff --git a/stubs/NormalizableInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizableInterface.stub
similarity index 100%
rename from stubs/NormalizableInterface.stub
rename to stubs/Symfony/Component/Serializer/Normalizer/NormalizableInterface.stub
diff --git a/stubs/NormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
similarity index 100%
rename from stubs/NormalizerInterface.stub
rename to stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
diff --git a/stubs/Constraint.stub b/stubs/Symfony/Component/Validator/Constraint.stub
similarity index 100%
rename from stubs/Constraint.stub
rename to stubs/Symfony/Component/Validator/Constraint.stub
diff --git a/stubs/ConstraintViolationInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
similarity index 100%
rename from stubs/ConstraintViolationInterface.stub
rename to stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
diff --git a/stubs/ConstraintViolationListInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
similarity index 100%
rename from stubs/ConstraintViolationListInterface.stub
rename to stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
diff --git a/stubs/Node.stub b/stubs/Twig/Node/Node.stub
similarity index 100%
rename from stubs/Node.stub
rename to stubs/Twig/Node/Node.stub

From 69d0cbf86ab953232fdf164ca05eabcbb490da58 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Mon, 19 Apr 2021 16:34:35 +0200
Subject: [PATCH 073/297] Add stubs for Acl

---
 extension.neon                                |  6 ++++++
 .../Security/Acl/Model/AclInterface.stub      |  8 ++++++++
 .../Acl/Model/AclProviderInterface.stub       | 19 +++++++++++++++++++
 .../Acl/Model/MutableAclInterface.stub        |  8 ++++++++
 .../Model/MutableAclProviderInterface.stub    | 19 +++++++++++++++++++
 .../Acl/Model/ObjectIdentityInterface.stub    |  8 ++++++++
 .../Acl/Model/SecurityIdentityInterface.stub  |  8 ++++++++
 7 files changed, 76 insertions(+)
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub

diff --git a/extension.neon b/extension.neon
index dfb804af..51ef297f 100644
--- a/extension.neon
+++ b/extension.neon
@@ -22,6 +22,12 @@ parameters:
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
new file mode 100644
index 00000000..b2ce3e58
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
@@ -0,0 +1,8 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface AclInterface
+{
+
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
new file mode 100644
index 00000000..97cb6880
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
@@ -0,0 +1,19 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface AclProviderInterface
+{
+    /**
+     * @phpstan-param array<SecurityIdentityInterface> $sids
+     * @phpstan-return AclInterface
+     */
+    public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
+
+    /**
+     * @phpstan-param array<ObjectIdentityInterface> $oids
+     * @phpstan-param array<SecurityIdentityInterface> $sids
+     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, AclInterface>
+     */
+    public function findAcls(array $oids, array $sids = []);
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
new file mode 100644
index 00000000..75df80b3
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
@@ -0,0 +1,8 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface MutableAclInterface extends AclInterface
+{
+
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
new file mode 100644
index 00000000..4f25b6c5
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
@@ -0,0 +1,19 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface MutableAclProviderInterface extends AclProviderInterface
+{
+    /**
+     * @phpstan-param array<SecurityIdentityInterface> $sids
+     * @phpstan-return MutableAclInterface
+     */
+    public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
+
+    /**
+     * @phpstan-param array<ObjectIdentityInterface> $oids
+     * @phpstan-param array<SecurityIdentityInterface> $sids
+     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, MutableAclInterface>
+     */
+    public function findAcls(array $oids, array $sids = []);
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
new file mode 100644
index 00000000..8c03dabc
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
@@ -0,0 +1,8 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface ObjectIdentityInterface
+{
+
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub
new file mode 100644
index 00000000..d60731dc
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub
@@ -0,0 +1,8 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface SecurityIdentityInterface
+{
+
+}

From 09aaed10601e5a02ff5d22a2bb88701628b7713f Mon Sep 17 00:00:00 2001
From: Thomas Landauer <thomas@landauer.at>
Date: Fri, 23 Apr 2021 22:30:15 +0200
Subject: [PATCH 074/297] README: console-application.php for Symfony 5

---
 README.md | 41 +++++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index 0081f2ad..4cb627e4 100644
--- a/README.md
+++ b/README.md
@@ -83,30 +83,51 @@ parameters:
 
 Be aware that it may hide genuine errors in your application.
 
-## Console command analysis
+## Analysis of Symfony Console Commands
 
-You can opt in for more advanced analysis by providing the console application from your own application. This will allow the correct argument and option types to be inferred when accessing `$input->getArgument()` or `$input->getOption()`.
+You can opt in for more advanced analysis of [Symfony Console Commands](https://symfony.com/doc/current/console.html)
+by providing the console application from your own application. This will allow the correct argument and option types to be inferred when accessing `$input->getArgument()` or `$input->getOption()`.
 
-```
+```neon
 parameters:
 	symfony:
 		console_application_loader: tests/console-application.php
 ```
 
-For example, in a Symfony project, `console-application.php` would look something like this:
+Symfony 4:
 
 ```php
-require __DIR__.'/../config/bootstrap.php';
-$kernel = new \App\Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
-return new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
+// tests/console-application.php
+
+use App\Kernel;
+use Symfony\Bundle\FrameworkBundle\Console\Application;
+
+require __DIR__ . '/../config/bootstrap.php';
+$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
+return new Application($kernel);
 ```
 
-You may then encounter an error with PhpParser:
+Symfony 5:
+
+```php
+// tests/console-application.php
 
-```bash
-Compile Error: Cannot Declare interface PhpParser\NodeVisitor, because the name is already in use
+use App\Kernel;
+use Symfony\Bundle\FrameworkBundle\Console\Application;
+use Symfony\Component\Dotenv\Dotenv;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+(new Dotenv())->bootEnv(__DIR__ . '/../.env');
+
+$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
+return new Application($kernel);
 ```
 
+You may then encounter an error with PhpParser:
+
+> Compile Error: Cannot Declare interface PhpParser\NodeVisitor, because the name is already in use
+
 If this is the case, you should create a new environment for your application that will disable inlining. In `config/packages/phpstan_env/parameters.yaml`:
 
 ```yaml

From a3b3976441534082e971228f0ad4d5ab51ba20b1 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 25 Apr 2021 18:29:13 +0200
Subject: [PATCH 075/297] Register forgotten KernelInterface dynamic return
 type extension

---
 extension.neon | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/extension.neon b/extension.neon
index 51ef297f..002b98a4 100644
--- a/extension.neon
+++ b/extension.neon
@@ -210,3 +210,8 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\Config\TreeBuilderGetRootNodeDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# KernelInterface::locateResource() return type
+	-
+		class: PHPStan\Type\Symfony\KernelInterfaceDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]

From 5b38ab0f2f2a0b4e997a97225a8c403642f0362a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 25 Apr 2021 14:32:19 +0200
Subject: [PATCH 076/297] Modernize type inference tests

---
 composer.json                                 |  14 +-
 tests/Type/Symfony/Config/TreeBuilderTest.php | 211 ------------------
 tests/Type/Symfony/Config/tree_builder.php    | 156 ++++++++++++-
 .../EnvelopeReturnTypeExtensionTest.php       |  35 ---
 tests/Type/Symfony/ExampleBaseCommand.php     |  15 +-
 tests/Type/Symfony/ExampleController.php      |  19 +-
 .../ExampleControllerWithoutContainer.php     |  23 ++
 tests/Type/Symfony/ExampleOptionCommand.php   |  23 +-
 tests/Type/Symfony/ExtensionTest.php          |  68 ++++++
 tests/Type/Symfony/ExtensionTestCase.php      |  96 --------
 .../Symfony/ExtensionTestWithoutContainer.php |  39 ++++
 ...eaderBagDynamicReturnTypeExtensionTest.php |  37 ---
 ...InputBagDynamicReturnTypeExtensionTest.php |  40 ----
 ...ArgumentDynamicReturnTypeExtensionTest.php |  37 ---
 ...etOptionDynamicReturnTypeExtensionTest.php |  41 ----
 ...nterfaceDynamicReturnTypeExtensionTest.php |  33 ---
 .../RequestDynamicReturnTypeExtensionTest.php |  34 ---
 .../RequestTypeSpecifyingExtensionTest.php    |  54 -----
 ...rializerDynamicReturnTypeExtensionTest.php |  52 -----
 .../ServiceDynamicReturnTypeExtensionTest.php |  74 ------
 .../Symfony/VariableTypeReportingRule.php     |  42 ----
 tests/Type/Symfony/denormalizer.php           |  12 +-
 tests/Type/Symfony/envelope_all.php           |  10 +-
 tests/Type/Symfony/extension-test.neon        |   4 +
 tests/Type/Symfony/header_bag_get.php         |  20 +-
 tests/Type/Symfony/input_bag.php              |  16 +-
 tests/Type/Symfony/kernel_interface.php       |  10 +-
 tests/Type/Symfony/request_get_content.php    |  12 +-
 tests/Type/Symfony/request_get_session.php    |   8 +-
 .../Type/Symfony/request_get_session_null.php |  14 ++
 tests/Type/Symfony/serializer.php             |  12 +-
 31 files changed, 387 insertions(+), 874 deletions(-)
 delete mode 100644 tests/Type/Symfony/Config/TreeBuilderTest.php
 delete mode 100644 tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
 create mode 100644 tests/Type/Symfony/ExampleControllerWithoutContainer.php
 create mode 100644 tests/Type/Symfony/ExtensionTest.php
 delete mode 100644 tests/Type/Symfony/ExtensionTestCase.php
 create mode 100644 tests/Type/Symfony/ExtensionTestWithoutContainer.php
 delete mode 100644 tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
 delete mode 100644 tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
 delete mode 100644 tests/Type/Symfony/VariableTypeReportingRule.php
 create mode 100644 tests/Type/Symfony/extension-test.neon
 create mode 100644 tests/Type/Symfony/request_get_session_null.php

diff --git a/composer.json b/composer.json
index 46dbf2d5..20f2fce2 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.83"
+    "phpstan/phpstan": "^0.12.85"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
@@ -26,12 +26,12 @@
     "phpstan/phpstan-phpunit": "^0.12.16",
     "phpstan/phpstan-strict-rules": "^0.12.5",
     "phpunit/phpunit": "^7.5.20",
-    "symfony/console": "^4.0",
-    "symfony/config": "^4.2",
-    "symfony/framework-bundle": "^4.0",
-    "symfony/http-foundation": "^4.0",
-    "symfony/messenger": "^4.2",
-    "symfony/serializer": "^4.0"
+    "symfony/console": "^4.0 || ^5.0",
+    "symfony/config": "^4.2 || ^5.0",
+    "symfony/framework-bundle": "^4.0 || ^5.0",
+    "symfony/http-foundation": "^4.0 || ^5.0",
+    "symfony/messenger": "^4.2 || ^5.0",
+    "symfony/serializer": "^4.0 || ^5.0"
   },
   "config": {
     "platform": {
diff --git a/tests/Type/Symfony/Config/TreeBuilderTest.php b/tests/Type/Symfony/Config/TreeBuilderTest.php
deleted file mode 100644
index 67f77917..00000000
--- a/tests/Type/Symfony/Config/TreeBuilderTest.php
+++ /dev/null
@@ -1,211 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony\Config;
-
-use Iterator;
-use PHPStan\Type\Symfony\ExtensionTestCase;
-
-final class TreeBuilderTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider getProvider
-	 */
-	public function testGet(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/tree_builder.php',
-			$expression,
-			$type,
-			[
-				new ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension(),
-				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\ExprBuilder', ['end']),
-				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeBuilder', ['end']),
-				new ReturnParentDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition', ['end']),
-				new PassParentObjectDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeBuilder', ['arrayNode', 'scalarNode', 'booleanNode', 'integerNode', 'floatNode', 'enumNode', 'variableNode']),
-				new PassParentObjectDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition', ['children', 'validate', 'beforeNormalization']),
-				new TreeBuilderGetRootNodeDynamicReturnTypeExtension(),
-			],
-			[new TreeBuilderDynamicReturnTypeExtension()]
-		);
-	}
-
-	/**
-	 * @return \Iterator<array{string, string}>
-	 */
-	public function getProvider(): Iterator
-	{
-		yield ['$treeRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$treeRootNode
-			->children()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$treeRootNode
-			->children()
-				->scalarNode("protocol")
-				->end()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$treeRootNode
-			->children()
-		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-					->children()
-		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-					->children()
-						->scalarNode("protocol")
-		', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-					->children()
-						->scalarNode("protocol")
-						->end()
-		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-					->children()
-						->scalarNode("protocol")
-						->end()
-					->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-					->children()
-						->scalarNode("protocol")
-						->end()
-					->end()
-				->end()
-		', 'Symfony\Component\Config\Definition\Builder\NodeBuilder'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-					->children()
-						->scalarNode("protocol")
-						->end()
-					->end()
-				->end()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$treeRootNode
-			->children()
-				->arrayNode("protocols")
-					->children()
-						->booleanNode("auto_connect")
-							->defaultTrue()
-						->end()
-						->scalarNode("default_connection")
-							->defaultValue("default")
-						->end()
-						->integerNode("positive_value")
-							->min(0)
-						->end()
-						->floatNode("big_value")
-							->max(5E45)
-						->end()
-						->enumNode("delivery")
-							->values(["standard", "expedited", "priority"])
-						->end()
-					->end()
-				->end()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-
-		yield ['$arrayRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['$arrayRootNode->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
-		yield ['
-		$arrayRootNode
-			->children()
-				->arrayNode("methods")
-					->prototype("scalar")
-						->defaultNull()
-					->end()
-				->end()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$arrayRootNode
-			->children()
-				->arrayNode("methods")
-					->scalarPrototype()
-						->defaultNull()
-					->end()
-				->end()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$arrayRootNode
-			->children()
-				->arrayNode("methods")
-					->prototype("scalar")
-						->validate()
-							->ifNotInArray(["one", "two"])
-							->thenInvalid("%s is not a valid method.")
-						->end()
-					->end()
-				->end()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-		yield ['
-		$arrayRootNode
-			->children()
-				->arrayNode("methods")
-					->prototype("array")
-						->beforeNormalization()
-							->ifString()
-							->then(static function ($v) {
-								return [$v];
-							})
-						->end()
-					->end()
-				->end()
-			->end()
-		', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'];
-
-		yield ['$variableRootNode', 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition'];
-		yield ['$variableRootNode->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
-
-		yield ['$scalarRootNode', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
-		yield ['$scalarRootNode->defaultValue("default")', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition'];
-		yield ['$scalarRootNode->defaultValue("default")->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
-
-		yield ['$booleanRootNode', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition'];
-		yield ['$booleanRootNode->defaultTrue()', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition'];
-		yield ['$booleanRootNode->defaultTrue()->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
-
-		yield ['$integerRootNode', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition'];
-		yield ['$integerRootNode->min(0)', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition'];
-		yield ['$integerRootNode->min(0)->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
-
-		yield ['$floatRootNode', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition'];
-		yield ['$floatRootNode->max(5E45)', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition'];
-		yield ['$floatRootNode->max(5E45)->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
-
-		yield ['$enumRootNode', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition'];
-		yield ['$enumRootNode->values(["standard", "expedited", "priority"])', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition'];
-		yield ['$enumRootNode->values(["standard", "expedited", "priority"])->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder'];
-	}
-
-}
diff --git a/tests/Type/Symfony/Config/tree_builder.php b/tests/Type/Symfony/Config/tree_builder.php
index f1126608..8c3c3270 100644
--- a/tests/Type/Symfony/Config/tree_builder.php
+++ b/tests/Type/Symfony/Config/tree_builder.php
@@ -1,29 +1,183 @@
 <?php declare(strict_types = 1);
 
 use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use function PHPStan\Testing\assertType;
 
 $treeBuilder = new TreeBuilder('my_tree');
 $treeRootNode = $treeBuilder->getRootNode();
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $treeRootNode);
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $treeRootNode
+	->children()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $treeRootNode
+	->children()
+	->scalarNode("protocol")
+	->end()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\NodeBuilder', $treeRootNode
+	->children());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $treeRootNode
+	->children()
+	->arrayNode("protocols"));
+
+assertType('Symfony\Component\Config\Definition\Builder\NodeBuilder', $treeRootNode
+	->children()
+	->arrayNode("protocols")
+	->children());
+
+assertType('Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition', $treeRootNode
+	->children()
+	->arrayNode("protocols")
+	->children()
+	->scalarNode("protocol"));
+
+assertType('Symfony\Component\Config\Definition\Builder\NodeBuilder', $treeRootNode
+	->children()
+	->arrayNode("protocols")
+	->children()
+	->scalarNode("protocol")
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $treeRootNode
+	->children()
+	->arrayNode("protocols")
+	->children()
+	->scalarNode("protocol")
+	->end()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\NodeBuilder', $treeRootNode
+	->children()
+	->arrayNode("protocols")
+	->children()
+	->scalarNode("protocol")
+	->end()
+	->end()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $treeRootNode
+	->children()
+	->arrayNode("protocols")
+	->children()
+	->scalarNode("protocol")
+	->end()
+	->end()
+	->end()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $treeRootNode
+	->children()
+	->arrayNode("protocols")
+	->children()
+	->booleanNode("auto_connect")
+	->defaultTrue()
+	->end()
+	->scalarNode("default_connection")
+	->defaultValue("default")
+	->end()
+	->integerNode("positive_value")
+	->min(0)
+	->end()
+	->floatNode("big_value")
+	->max(5E45)
+	->end()
+	->enumNode("delivery")
+	->values(["standard", "expedited", "priority"])
+	->end()
+	->end()
+	->end()
+	->end());
 
 $arrayTreeBuilder = new TreeBuilder('my_tree', 'array');
 $arrayRootNode = $arrayTreeBuilder->getRootNode();
 
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $arrayRootNode);
+assertType('Symfony\Component\Config\Definition\Builder\TreeBuilder', $arrayRootNode->end());
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $arrayRootNode
+	->children()
+	->arrayNode("methods")
+	->prototype("scalar")
+	->defaultNull()
+	->end()
+	->end()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $arrayRootNode
+	->children()
+	->arrayNode("methods")
+	->scalarPrototype()
+	->defaultNull()
+	->end()
+	->end()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $arrayRootNode
+	->children()
+	->arrayNode("methods")
+	->prototype("scalar")
+	->validate()
+	->ifNotInArray(["one", "two"])
+	->thenInvalid("%s is not a valid method.")
+	->end()
+	->end()
+	->end()
+	->end());
+
+assertType('Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', $arrayRootNode
+	->children()
+	->arrayNode("methods")
+	->prototype("array")
+	->beforeNormalization()
+	->ifString()
+	->then(static function ($v) {
+		return [$v];
+	})
+	->end()
+	->end()
+	->end()
+	->end());
+
 $variableTreeBuilder = new TreeBuilder('my_tree', 'variable');
 $variableRootNode = $variableTreeBuilder->getRootNode();
 
+assertType('Symfony\Component\Config\Definition\Builder\VariableNodeDefinition', $variableRootNode);
+assertType('Symfony\Component\Config\Definition\Builder\TreeBuilder', $variableRootNode->end());
+
 $scalarTreeBuilder = new TreeBuilder('my_tree', 'scalar');
 $scalarRootNode = $scalarTreeBuilder->getRootNode();
 
+assertType('Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition', $scalarRootNode);
+assertType('Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition', $scalarRootNode->defaultValue("default"));
+assertType('Symfony\Component\Config\Definition\Builder\TreeBuilder', $scalarRootNode->defaultValue("default")->end());
+
 $booleanTreeBuilder = new TreeBuilder('my_tree', 'boolean');
 $booleanRootNode = $booleanTreeBuilder->getRootNode();
 
+assertType('Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', $booleanRootNode);
+assertType('Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', $booleanRootNode->defaultTrue());
+assertType('Symfony\Component\Config\Definition\Builder\TreeBuilder', $booleanRootNode->defaultTrue()->end());
+
 $integerTreeBuilder = new TreeBuilder('my_tree', 'integer');
 $integerRootNode = $integerTreeBuilder->getRootNode();
 
+assertType('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', $integerRootNode);
+assertType('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', $integerRootNode->min(0));
+assertType('Symfony\Component\Config\Definition\Builder\TreeBuilder', $integerRootNode->min(0)->end());
+
 $floatTreeBuilder = new TreeBuilder('my_tree', 'float');
 $floatRootNode = $floatTreeBuilder->getRootNode();
 
+assertType('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', $floatRootNode);
+assertType('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', $floatRootNode->max(5E45));
+assertType('Symfony\Component\Config\Definition\Builder\TreeBuilder', $floatRootNode->max(5E45)->end());
+
 $enumTreeBuilder = new TreeBuilder('my_tree', 'enum');
 $enumRootNode = $enumTreeBuilder->getRootNode();
 
-die;
+assertType('Symfony\Component\Config\Definition\Builder\EnumNodeDefinition', $enumRootNode);
+assertType('Symfony\Component\Config\Definition\Builder\EnumNodeDefinition', $enumRootNode->values(["standard", "expedited", "priority"]));
+assertType('Symfony\Component\Config\Definition\Builder\TreeBuilder', $enumRootNode->values(["standard", "expedited", "priority"])->end());
diff --git a/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php b/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
deleted file mode 100644
index ef59ccc4..00000000
--- a/tests/Type/Symfony/EnvelopeReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-use Symfony\Component\Messenger\Stamp\ReceivedStamp;
-use Symfony\Component\Messenger\Stamp\StampInterface;
-
-final class EnvelopeReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider getProvider
-	 */
-	public function testAll(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/envelope_all.php',
-			$expression,
-			$type,
-			[new EnvelopeReturnTypeExtension()]
-		);
-	}
-
-	/**
-	 * @return \Iterator<mixed>
-	 */
-	public function getProvider(): Iterator
-	{
-		yield ['$test1', 'array<' . ReceivedStamp::class . '>'];
-		yield ['$test2', 'array<' . StampInterface::class . '>'];
-		yield ['$test3', 'array<array<' . StampInterface::class . '>>'];
-	}
-
-}
diff --git a/tests/Type/Symfony/ExampleBaseCommand.php b/tests/Type/Symfony/ExampleBaseCommand.php
index b058ee2c..a4493b2c 100644
--- a/tests/Type/Symfony/ExampleBaseCommand.php
+++ b/tests/Type/Symfony/ExampleBaseCommand.php
@@ -5,6 +5,7 @@
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
+use function PHPStan\Testing\assertType;
 
 abstract class ExampleBaseCommand extends Command
 {
@@ -18,14 +19,12 @@ protected function configure(): void
 
 	protected function execute(InputInterface $input, OutputInterface $output): int
 	{
-		$base = $input->getArgument('base');
-		$aaa = $input->getArgument('aaa');
-		$bbb = $input->getArgument('bbb');
-		$diff = $input->getArgument('diff');
-		$arr = $input->getArgument('arr');
-		$both = $input->getArgument('both');
-
-		die;
+		assertType('string|null', $input->getArgument('base'));
+		assertType('string', $input->getArgument('aaa'));
+		assertType('string', $input->getArgument('bbb'));
+		assertType('array<int, string>|string', $input->getArgument('diff'));
+		assertType('array<int, string>', $input->getArgument('arr'));
+		assertType('string|null', $input->getArgument('both'));
 	}
 
 }
diff --git a/tests/Type/Symfony/ExampleController.php b/tests/Type/Symfony/ExampleController.php
index 2a75eefb..ce22ac07 100644
--- a/tests/Type/Symfony/ExampleController.php
+++ b/tests/Type/Symfony/ExampleController.php
@@ -3,23 +3,22 @@
 namespace PHPStan\Type\Symfony;
 
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use function PHPStan\Testing\assertType;
 
 final class ExampleController extends Controller
 {
 
 	public function services(): void
 	{
-		$service1 = $this->get('foo');
-		$service2 = $this->get('bar');
-		$service3 = $this->get(doFoo());
-		$service4 = $this->get();
+		assertType('Foo', $this->get('foo'));
+		assertType('object', $this->get('bar'));
+		assertType('object', $this->get(doFoo()));
+		assertType('object', $this->get());
 
-		$has1 = $this->has('foo');
-		$has2 = $this->has('bar');
-		$has3 = $this->has(doFoo());
-		$has4 = $this->has();
-
-		die;
+		assertType('true', $this->has('foo'));
+		assertType('false', $this->has('bar'));
+		assertType('bool', $this->has(doFoo()));
+		assertType('bool', $this->has());
 	}
 
 }
diff --git a/tests/Type/Symfony/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/ExampleControllerWithoutContainer.php
new file mode 100644
index 00000000..f127d976
--- /dev/null
+++ b/tests/Type/Symfony/ExampleControllerWithoutContainer.php
@@ -0,0 +1,23 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+
+final class ExampleControllerWithoutContainer extends Controller
+{
+
+	public function services(): void
+	{
+		assertType('object', $this->get('foo'));
+		assertType('object', $this->get('bar'));
+		assertType('object', $this->get(doFoo()));
+		assertType('object', $this->get());
+
+		assertType('bool', $this->has('foo'));
+		assertType('bool', $this->has('bar'));
+		assertType('bool', $this->has(doFoo()));
+		assertType('bool', $this->has());
+	}
+
+}
diff --git a/tests/Type/Symfony/ExampleOptionCommand.php b/tests/Type/Symfony/ExampleOptionCommand.php
index c18d55e2..b6a07574 100644
--- a/tests/Type/Symfony/ExampleOptionCommand.php
+++ b/tests/Type/Symfony/ExampleOptionCommand.php
@@ -6,6 +6,7 @@
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
+use function PHPStan\Testing\assertType;
 
 final class ExampleOptionCommand extends Command
 {
@@ -29,18 +30,16 @@ protected function configure(): void
 
 	protected function execute(InputInterface $input, OutputInterface $output): int
 	{
-		$a = $input->getOption('a');
-		$b = $input->getOption('b');
-		$c = $input->getOption('c');
-		$d = $input->getOption('d');
-		$e = $input->getOption('e');
-
-		$bb = $input->getOption('bb');
-		$cc = $input->getOption('cc');
-		$dd = $input->getOption('dd');
-		$ee = $input->getOption('ee');
-
-		die;
+		assertType('bool', $input->getOption('a'));
+		assertType('string|null', $input->getOption('b'));
+		assertType('string|null', $input->getOption('c'));
+		assertType('array<int, string|null>', $input->getOption('d'));
+		assertType('array<int, string>', $input->getOption('e'));
+
+		assertType('1|string|null', $input->getOption('bb'));
+		assertType('1|string', $input->getOption('cc'));
+		assertType('array<int, 1|string|null>', $input->getOption('dd'));
+		assertType('array<int, 1|string>', $input->getOption('ee'));
 	}
 
 }
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
new file mode 100644
index 00000000..b85c943f
--- /dev/null
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PHPStan\Testing\TypeInferenceTestCase;
+use ReflectionMethod;
+use Symfony\Component\HttpFoundation\Request;
+
+class ExtensionTest extends TypeInferenceTestCase
+{
+
+	public function dataFileAsserts(): iterable
+	{
+		yield from $this->gatherAssertTypes(__DIR__ . '/envelope_all.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/header_bag_get.php');
+
+		if (class_exists('Symfony\Component\HttpFoundation\InputBag')) {
+			yield from $this->gatherAssertTypes(__DIR__ . '/input_bag.php');
+		}
+
+		yield from $this->gatherAssertTypes(__DIR__ . '/Config/tree_builder.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/ExampleBaseCommand.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/ExampleOptionCommand.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/kernel_interface.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/request_get_content.php');
+
+
+		$ref = new ReflectionMethod(Request::class, 'getSession');
+		$doc = (string) $ref->getDocComment();
+		if (strpos($doc, '@return SessionInterface|null') !== false) {
+			yield from $this->gatherAssertTypes(__DIR__ . '/request_get_session_null.php');
+		} else {
+			yield from $this->gatherAssertTypes(__DIR__ . '/request_get_session.php');
+		}
+
+		yield from $this->gatherAssertTypes(__DIR__ . '/serializer.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/denormalizer.php');
+
+		if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			yield from $this->gatherAssertTypes(__DIR__ . '/ExampleController.php');
+		}
+	}
+
+	/**
+	 * @dataProvider dataFileAsserts
+	 * @param string $assertType
+	 * @param string $file
+	 * @param mixed ...$args
+	 */
+	public function testFileAsserts(
+		string $assertType,
+		string $file,
+		...$args
+	): void
+	{
+		$this->assertFileAsserts($assertType, $file, ...$args);
+	}
+
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/../../../extension.neon',
+			__DIR__ . '/extension-test.neon',
+		];
+	}
+
+
+}
diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php
deleted file mode 100644
index a69c6e59..00000000
--- a/tests/Type/Symfony/ExtensionTestCase.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use PhpParser\Node;
-use PhpParser\PrettyPrinter\Standard;
-use PHPStan\Analyser\NodeScopeResolver;
-use PHPStan\Analyser\Scope;
-use PHPStan\Analyser\ScopeContext;
-use PHPStan\Broker\AnonymousClassNameHelper;
-use PHPStan\Cache\Cache;
-use PHPStan\File\FileHelper;
-use PHPStan\Node\VirtualNode;
-use PHPStan\Php\PhpVersion;
-use PHPStan\PhpDoc\PhpDocInheritanceResolver;
-use PHPStan\PhpDoc\PhpDocNodeResolver;
-use PHPStan\PhpDoc\PhpDocStringResolver;
-use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider;
-use PHPStan\Testing\TestCase;
-use PHPStan\Type\FileTypeMapper;
-use PHPStan\Type\VerbosityLevel;
-
-abstract class ExtensionTestCase extends TestCase
-{
-
-	/**
-	 * @param string $file
-	 * @param string $expression
-	 * @param string $type
-	 * @param \PHPStan\Type\DynamicMethodReturnTypeExtension[] $dynamicMethodReturnTypeExtensions
-	 * @param \PHPStan\Type\DynamicStaticMethodReturnTypeExtension[] $dynamicStaticMethodReturnTypeExtensions
-	 */
-	protected function processFile(
-		string $file,
-		string $expression,
-		string $type,
-		array $dynamicMethodReturnTypeExtensions = [],
-		array $dynamicStaticMethodReturnTypeExtensions = []
-	): void
-	{
-		$broker = $this->createBroker($dynamicMethodReturnTypeExtensions, $dynamicStaticMethodReturnTypeExtensions);
-		$parser = $this->getParser();
-		$currentWorkingDirectory = $this->getCurrentWorkingDirectory();
-		$fileHelper = new FileHelper($currentWorkingDirectory);
-		$typeSpecifier = $this->createTypeSpecifier(new Standard(), $broker);
-		/** @var \PHPStan\PhpDoc\PhpDocStringResolver $phpDocStringResolver */
-		$phpDocStringResolver = self::getContainer()->getByType(PhpDocStringResolver::class);
-		$fileTypeMapper = new FileTypeMapper(
-			new DirectReflectionProviderProvider($broker),
-			$parser,
-			$phpDocStringResolver,
-			self::getContainer()->getByType(PhpDocNodeResolver::class),
-			$this->createMock(Cache::class),
-			$this->createMock(AnonymousClassNameHelper::class)
-		);
-		$resolver = new NodeScopeResolver(
-			$broker,
-			self::getReflectors()[0],
-			$this->getClassReflectionExtensionRegistryProvider(),
-			$parser,
-			$fileTypeMapper,
-			new PhpVersion(PHP_VERSION_ID),
-			new PhpDocInheritanceResolver($fileTypeMapper),
-			$fileHelper,
-			$typeSpecifier,
-			true,
-			true,
-			true,
-			[],
-			[],
-			true,
-			true
-		);
-		$resolver->setAnalysedFiles([$fileHelper->normalizePath($file)]);
-
-		$run = false;
-		$resolver->processNodes(
-			$parser->parseFile($file),
-			$this->createScopeFactory($broker, $typeSpecifier)->create(ScopeContext::create($file)),
-			function (Node $node, Scope $scope) use ($expression, $type, &$run): void {
-				if ($node instanceof VirtualNode) {
-					return;
-				}
-				if ((new Standard())->prettyPrint([$node]) !== 'die') {
-					return;
-				}
-				/** @var \PhpParser\Node\Stmt\Expression $expNode */
-				$expNode = $this->getParser()->parseString(sprintf('<?php %s;', $expression))[0];
-				self::assertSame($type, $scope->getType($expNode->expr)->describe(VerbosityLevel::typeOnly()), sprintf('Expression "%s"', $expression));
-				$run = true;
-			}
-		);
-		self::assertTrue($run);
-	}
-
-}
diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
new file mode 100644
index 00000000..13b1a2dd
--- /dev/null
+++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
@@ -0,0 +1,39 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PHPStan\Testing\TypeInferenceTestCase;
+
+class ExtensionTestWithoutContainer extends TypeInferenceTestCase
+{
+
+	public function dataFileAsserts(): iterable
+	{
+		yield from $this->gatherAssertTypes(__DIR__ . '/ExampleControllerWithoutContainer.php');
+	}
+
+	/**
+	 * @dataProvider dataFileAsserts
+	 * @param string $assertType
+	 * @param string $file
+	 * @param mixed ...$args
+	 */
+	public function testFileAsserts(
+		string $assertType,
+		string $file,
+		...$args
+	): void
+	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			$this->markTestSkipped('Needs class Symfony\Bundle\FrameworkBundle\Controller\Controller');
+		}
+		$this->assertFileAsserts($assertType, $file, ...$args);
+	}
+
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/../../../extension.neon',
+		];
+	}
+}
diff --git a/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index f84f3032..00000000
--- a/tests/Type/Symfony/HeaderBagDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-
-final class HeaderBagDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider getProvider
-	 */
-	public function testGet(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/header_bag_get.php',
-			$expression,
-			$type,
-			[new HeaderBagDynamicReturnTypeExtension()]
-		);
-	}
-
-	/**
-	 * @return \Iterator<array{string, string}>
-	 */
-	public function getProvider(): Iterator
-	{
-		yield ['$test1', 'string|null'];
-		yield ['$test2', 'string|null'];
-		yield ['$test3', 'string'];
-		yield ['$test5', 'string|null'];
-		yield ['$test6', 'string'];
-		yield ['$test8', 'array<int, string>'];
-		yield ['$test9', 'array<int, string>'];
-	}
-
-}
diff --git a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index f1a2a520..00000000
--- a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-
-final class InputBagDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider inputBagProvider
-	 */
-	public function testInputBag(string $expression, string $type): void
-	{
-		if (!class_exists('Symfony\\Component\\HttpFoundation\\InputBag')) {
-			self::markTestSkipped('The test needs Symfony\Component\HttpFoundation\InputBag class.');
-		}
-
-		$this->processFile(
-			__DIR__ . '/input_bag.php',
-			$expression,
-			$type,
-			[new InputBagDynamicReturnTypeExtension()]
-		);
-	}
-
-	/**
-	 * @return \Iterator<array{string, string}>
-	 */
-	public function inputBagProvider(): Iterator
-	{
-		yield ['$test1', 'string|null'];
-		yield ['$test2', 'string|null'];
-		yield ['$test3', 'string'];
-		yield ['$test4', 'string'];
-		yield ['$test5', 'array<string, array<string>|string>'];
-		yield ['$test6', 'array<string>'];
-	}
-
-}
diff --git a/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index 00d85e00..00000000
--- a/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-use PHPStan\Symfony\ConsoleApplicationResolver;
-
-final class InputInterfaceGetArgumentDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider argumentTypesProvider
-	 */
-	public function testArgumentTypes(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/ExampleBaseCommand.php',
-			$expression,
-			$type,
-			[new InputInterfaceGetArgumentDynamicReturnTypeExtension(new ConsoleApplicationResolver(__DiR__ . '/console_application_loader.php'))]
-		);
-	}
-
-	/**
-	 * @return \Iterator<array{string, string}>
-	 */
-	public function argumentTypesProvider(): Iterator
-	{
-		yield ['$base', 'string|null'];
-		yield ['$aaa', 'string'];
-		yield ['$bbb', 'string'];
-		yield ['$diff', 'array<int, string>|string'];
-		yield ['$arr', 'array<int, string>'];
-		yield ['$both', 'string|null'];
-	}
-
-}
diff --git a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index ede5f68d..00000000
--- a/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-use PHPStan\Symfony\ConsoleApplicationResolver;
-
-final class InputInterfaceGetOptionDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider argumentTypesProvider
-	 */
-	public function testArgumentTypes(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/ExampleOptionCommand.php',
-			$expression,
-			$type,
-			[new InputInterfaceGetOptionDynamicReturnTypeExtension(new ConsoleApplicationResolver(__DiR__ . '/console_application_loader.php'))]
-		);
-	}
-
-	/**
-	 * @return \Iterator<array{string, string}>
-	 */
-	public function argumentTypesProvider(): Iterator
-	{
-		yield ['$a', 'bool'];
-		yield ['$b', 'string|null'];
-		yield ['$c', 'string|null'];
-		yield ['$d', 'array<int, string|null>'];
-		yield ['$e', 'array<int, string>'];
-
-		yield ['$bb', 'int|string|null'];
-		yield ['$cc', 'int|string'];
-		yield ['$dd', 'array<int, int|string|null>'];
-		yield ['$ee', 'array<int, int|string>'];
-	}
-
-}
diff --git a/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index 40bef996..00000000
--- a/tests/Type/Symfony/KernelInterfaceDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-
-final class KernelInterfaceDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider getProvider
-	 */
-	public function testGet(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/kernel_interface.php',
-			$expression,
-			$type,
-			[new KernelInterfaceDynamicReturnTypeExtension()]
-		);
-	}
-
-	/**
-	 * @return Iterator<int, array{string, string}>
-	 */
-	public function getProvider(): Iterator
-	{
-		yield ['$foo', 'string'];
-		yield ['$bar', 'string'];
-		yield ['$baz', 'array<int, string>'];
-	}
-
-}
diff --git a/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index f0834674..00000000
--- a/tests/Type/Symfony/RequestDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-
-final class RequestDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider getContentProvider
-	 */
-	public function testGetContent(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/request_get_content.php',
-			$expression,
-			$type,
-			[new RequestDynamicReturnTypeExtension()]
-		);
-	}
-
-	/**
-	 * @return Iterator<int, array{string, string}>
-	 */
-	public function getContentProvider(): Iterator
-	{
-		yield ['$content1', 'string'];
-		yield ['$content2', 'string'];
-		yield ['$content3', 'resource'];
-		yield ['$content4', 'resource|string'];
-	}
-
-}
diff --git a/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php b/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
deleted file mode 100644
index ac6f80c1..00000000
--- a/tests/Type/Symfony/RequestTypeSpecifyingExtensionTest.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use PHPStan\Rules\Rule;
-use PHPStan\Testing\RuleTestCase;
-use PHPStan\Type\MethodTypeSpecifyingExtension;
-use ReflectionMethod;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Session\SessionInterface;
-use function strpos;
-
-/**
- * @extends RuleTestCase<VariableTypeReportingRule>
- */
-final class RequestTypeSpecifyingExtensionTest extends RuleTestCase
-{
-
-	protected function getRule(): Rule
-	{
-		return new VariableTypeReportingRule();
-	}
-
-	/** @return MethodTypeSpecifyingExtension[] */
-	protected function getMethodTypeSpecifyingExtensions(): array
-	{
-		return [
-			new RequestTypeSpecifyingExtension($this->createBroker()),
-		];
-	}
-
-	public function testGetSession(): void
-	{
-		$ref = new ReflectionMethod(Request::class, 'getSession');
-		$doc = (string) $ref->getDocComment();
-
-		$checkedTypeString = SessionInterface::class;
-		if (strpos($doc, '@return SessionInterface|null') !== false) {
-			$checkedTypeString .= '|null';
-		}
-
-		$this->analyse([__DIR__ . '/request_get_session.php'], [
-			[
-				'Variable $session1 is: ' . $checkedTypeString,
-				7,
-			],
-			[
-				'Variable $session2 is: ' . SessionInterface::class,
-				11,
-			],
-		]);
-	}
-
-}
diff --git a/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index 0857127f..00000000
--- a/tests/Type/Symfony/SerializerDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-
-final class SerializerDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider getContentProvider
-	 */
-	public function testSerializerInterface(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/serializer.php',
-			$expression,
-			$type,
-			[new SerializerDynamicReturnTypeExtension(
-				'Symfony\Component\Serializer\SerializerInterface',
-				'deserialize'
-			)]
-		);
-	}
-
-	/**
-	 * @dataProvider getContentProvider
-	 */
-	public function testDenormalizerInterface(string $expression, string $type): void
-	{
-		$this->processFile(
-			__DIR__ . '/denormalizer.php',
-			$expression,
-			$type,
-			[new SerializerDynamicReturnTypeExtension(
-				'Symfony\Component\Serializer\Normalizer\DenormalizerInterface',
-				'denormalize'
-			)]
-		);
-	}
-
-	/**
-	 * @return Iterator<int, array{string, string}>
-	 */
-	public function getContentProvider(): Iterator
-	{
-		yield ['$first', 'Bar'];
-		yield ['$second', 'array<Bar>'];
-		yield ['$third', 'array<array<Bar>>'];
-	}
-
-}
diff --git a/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
deleted file mode 100644
index 8cfaa65d..00000000
--- a/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use Iterator;
-use PHPStan\Symfony\XmlServiceMapFactory;
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-
-final class ServiceDynamicReturnTypeExtensionTest extends ExtensionTestCase
-{
-
-	/**
-	 * @dataProvider servicesProvider
-	 */
-	public function testServices(string $expression, string $type, ?string $container): void
-	{
-		$this->processFile(
-			__DIR__ . '/ExampleController.php',
-			$expression,
-			$type,
-			[new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory($container))->create())]
-		);
-	}
-
-	/**
-	 * @return Iterator<int, array{string, string, string|null}>
-	 */
-	public function servicesProvider(): Iterator
-	{
-		yield ['$service1', 'Foo', __DIR__ . '/container.xml'];
-		yield ['$service2', 'object', __DIR__ . '/container.xml'];
-		yield ['$service3', 'object', __DIR__ . '/container.xml'];
-		yield ['$service4', 'object', __DIR__ . '/container.xml'];
-		yield ['$has1', 'true', __DIR__ . '/container.xml'];
-		yield ['$has2', 'false', __DIR__ . '/container.xml'];
-		yield ['$has3', 'bool', __DIR__ . '/container.xml'];
-		yield ['$has4', 'bool', __DIR__ . '/container.xml'];
-
-		yield ['$service1', 'object', null];
-		yield ['$service2', 'object', null];
-		yield ['$service3', 'object', null];
-		yield ['$service4', 'object', null];
-		yield ['$has1', 'bool', null];
-		yield ['$has2', 'bool', null];
-		yield ['$has3', 'bool', null];
-		yield ['$has4', 'bool', null];
-	}
-
-	/**
-	 * @dataProvider constantHassersOffProvider
-	 */
-	public function testConstantHassersOff(string $expression, string $type, ?string $container): void
-	{
-		$this->processFile(
-			__DIR__ . '/ExampleController.php',
-			$expression,
-			$type,
-			[new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory($container))->create())]
-		);
-	}
-
-	/**
-	 * @return Iterator<int, array{string, string, string|null}>
-	 */
-	public function constantHassersOffProvider(): Iterator
-	{
-		yield ['$has1', 'bool', __DIR__ . '/container.xml'];
-		yield ['$has2', 'bool', __DIR__ . '/container.xml'];
-
-		yield ['$has1', 'bool', null];
-		yield ['$has2', 'bool', null];
-	}
-
-}
diff --git a/tests/Type/Symfony/VariableTypeReportingRule.php b/tests/Type/Symfony/VariableTypeReportingRule.php
deleted file mode 100644
index cbac8c06..00000000
--- a/tests/Type/Symfony/VariableTypeReportingRule.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Type\Symfony;
-
-use PhpParser\Node;
-use PhpParser\Node\Expr\Variable;
-use PHPStan\Analyser\Scope;
-use PHPStan\Rules\Rule;
-use PHPStan\Type\VerbosityLevel;
-
-/**
- * @implements \PHPStan\Rules\Rule<\PhpParser\Node\Expr\Variable>
- */
-final class VariableTypeReportingRule implements Rule
-{
-
-	public function getNodeType(): string
-	{
-		return Variable::class;
-	}
-
-	public function processNode(Node $node, Scope $scope): array
-	{
-		if (!is_string($node->name)) {
-			return [];
-		}
-		if (!$scope->isInFirstLevelStatement()) {
-			return [];
-		};
-		if ($scope->isInExpressionAssign($node)) {
-			return [];
-		}
-		return [
-			sprintf(
-				'Variable $%s is: %s',
-				$node->name,
-				$scope->getType($node)->describe(VerbosityLevel::value())
-			),
-		];
-	}
-
-}
diff --git a/tests/Type/Symfony/denormalizer.php b/tests/Type/Symfony/denormalizer.php
index 208483b4..ccbb8fc2 100644
--- a/tests/Type/Symfony/denormalizer.php
+++ b/tests/Type/Symfony/denormalizer.php
@@ -1,10 +1,10 @@
 <?php declare(strict_types = 1);
 
-$serializer = new \Symfony\Component\Serializer\Serializer();
+use function PHPStan\Testing\assertType;
 
-$first = $serializer->denormalize('bar', 'Bar', 'format');
-$second = $serializer->denormalize('bar', 'Bar[]', 'format');
-$third = $serializer->denormalize('bar', 'Bar[][]', 'format');
-$fourth = $serializer->denormalize('bar');
+$serializer = new \Symfony\Component\Serializer\Serializer();
 
-die;
+assertType('Bar', $serializer->denormalize('bar', 'Bar', 'format'));
+assertType('array<Bar>', $serializer->denormalize('bar', 'Bar[]', 'format'));
+assertType('array<array<Bar>>', $serializer->denormalize('bar', 'Bar[][]', 'format'));
+assertType('mixed', $serializer->denormalize('bar'));
diff --git a/tests/Type/Symfony/envelope_all.php b/tests/Type/Symfony/envelope_all.php
index f2bd3ec4..95521530 100644
--- a/tests/Type/Symfony/envelope_all.php
+++ b/tests/Type/Symfony/envelope_all.php
@@ -1,9 +1,9 @@
 <?php declare(strict_types = 1);
 
-$envelope = new \Symfony\Component\Messenger\Envelope(new stdClass());
+use function PHPStan\Testing\assertType;
 
-$test1 = $envelope->all(\Symfony\Component\Messenger\Stamp\ReceivedStamp::class);
-$test2 = $envelope->all(random_bytes(1));
-$test3 = $envelope->all();
+$envelope = new \Symfony\Component\Messenger\Envelope(new stdClass());
 
-die;
+assertType('array<Symfony\Component\Messenger\Stamp\ReceivedStamp>', $envelope->all(\Symfony\Component\Messenger\Stamp\ReceivedStamp::class));
+assertType('array<Symfony\Component\Messenger\Stamp\StampInterface>', $envelope->all(random_bytes(1)));
+assertType('array<array<Symfony\Component\Messenger\Stamp\StampInterface>>', $envelope->all());
diff --git a/tests/Type/Symfony/extension-test.neon b/tests/Type/Symfony/extension-test.neon
new file mode 100644
index 00000000..f7dc1353
--- /dev/null
+++ b/tests/Type/Symfony/extension-test.neon
@@ -0,0 +1,4 @@
+parameters:
+	symfony:
+		console_application_loader: console_application_loader.php
+		container_xml_path: container.xml
diff --git a/tests/Type/Symfony/header_bag_get.php b/tests/Type/Symfony/header_bag_get.php
index ed2d36aa..e3f4c561 100644
--- a/tests/Type/Symfony/header_bag_get.php
+++ b/tests/Type/Symfony/header_bag_get.php
@@ -1,15 +1,13 @@
 <?php declare(strict_types = 1);
 
-$bag = new \Symfony\Component\HttpFoundation\HeaderBag(['foo' => ['bar']]);
-
-$test1 = $bag->get('foo');
-$test2 = $bag->get('foo', null);
-$test3 = $bag->get('foo', 'baz');
+use function PHPStan\Testing\assertType;
 
-$test5 = $bag->get('foo', null, true);
-$test6 = $bag->get('foo', 'baz', true);
-
-$test8 = $bag->get('foo', null, false);
-$test9 = $bag->get('foo', 'baz', false);
+$bag = new \Symfony\Component\HttpFoundation\HeaderBag(['foo' => ['bar']]);
 
-die;
+assertType('string|null', $bag->get('foo'));
+assertType('string|null', $bag->get('foo', null));
+assertType('string', $bag->get('foo', 'baz'));
+assertType('string|null', $bag->get('foo', null, true));
+assertType('string', $bag->get('foo', 'baz', true));
+assertType('array<int, string>', $bag->get('foo', null, false));
+assertType('array<int, string>', $bag->get('foo', 'baz', false));
diff --git a/tests/Type/Symfony/input_bag.php b/tests/Type/Symfony/input_bag.php
index 7a36d61f..cdfde92b 100644
--- a/tests/Type/Symfony/input_bag.php
+++ b/tests/Type/Symfony/input_bag.php
@@ -1,12 +1,12 @@
 <?php declare(strict_types = 1);
 
-$bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar', 'bar' => ['x']]);
+use function PHPStan\Testing\assertType;
 
-$test1 = $bag->get('foo');
-$test2 = $bag->get('foo', null);
-$test3 = $bag->get('foo', '');
-$test4 = $bag->get('foo', 'baz');
-$test5 = $bag->all();
-$test6 = $bag->all('bar');
+$bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar', 'bar' => ['x']]);
 
-die;
+assertType('string|null', $bag->get('foo'));
+assertType('string|null', $bag->get('foo', null));
+assertType('string', $bag->get('foo', ''));
+assertType('string', $bag->get('foo', 'baz'));
+assertType('array<string, array<string>|string>', $bag->all());
+assertType('array<string>', $bag->all('bar'));
diff --git a/tests/Type/Symfony/kernel_interface.php b/tests/Type/Symfony/kernel_interface.php
index aca451fb..e2239754 100644
--- a/tests/Type/Symfony/kernel_interface.php
+++ b/tests/Type/Symfony/kernel_interface.php
@@ -1,5 +1,7 @@
 <?php declare(strict_types = 1);
 
+use function PHPStan\Testing\assertType;
+
 $kernel = new class ('dev', true) extends \Symfony\Component\HttpKernel\Kernel {
 
 	public function registerBundles(): void
@@ -12,8 +14,6 @@ public function registerContainerConfiguration(\Symfony\Component\Config\Loader\
 
 };
 
-$foo = $kernel->locateResource('');
-$bar = $kernel->locateResource('', null, true);
-$baz = $kernel->locateResource('', null, false);
-
-die;
+assertType('string', $kernel->locateResource(''));
+assertType('string', $kernel->locateResource('', null, true));
+assertType('array<int, string>', $kernel->locateResource('', null, false));
diff --git a/tests/Type/Symfony/request_get_content.php b/tests/Type/Symfony/request_get_content.php
index 3fbacaab..4038e3fa 100644
--- a/tests/Type/Symfony/request_get_content.php
+++ b/tests/Type/Symfony/request_get_content.php
@@ -1,11 +1,11 @@
 <?php declare(strict_types = 1);
 
+use function PHPStan\Testing\assertType;
+
 /** @var \Symfony\Component\HttpFoundation\Request $request */
 $request = doFoo();
 
-$content1 = $request->getContent();
-$content2 = $request->getContent(false);
-$content3 = $request->getContent(true);
-$content4 = $request->getContent(doBar());
-
-die;
+assertType('string', $request->getContent());
+assertType('string', $request->getContent(false));
+assertType('resource', $request->getContent(true));
+assertType('resource|string', $request->getContent(doBar()));
diff --git a/tests/Type/Symfony/request_get_session.php b/tests/Type/Symfony/request_get_session.php
index b488455a..9a0335e5 100644
--- a/tests/Type/Symfony/request_get_session.php
+++ b/tests/Type/Symfony/request_get_session.php
@@ -1,12 +1,14 @@
 <?php declare(strict_types = 1);
 
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use function PHPStan\Testing\assertType;
+
 /** @var \Symfony\Component\HttpFoundation\Request $request */
 $request = doRequest();
 
 $session1 = $request->getSession();
-$session1;
+assertType(SessionInterface::class, $request->getSession());
 
 if ($request->hasSession()) {
-	$session2 = $request->getSession();
-	$session2;
+	assertType(SessionInterface::class, $request->getSession());
 }
diff --git a/tests/Type/Symfony/request_get_session_null.php b/tests/Type/Symfony/request_get_session_null.php
new file mode 100644
index 00000000..9c37979d
--- /dev/null
+++ b/tests/Type/Symfony/request_get_session_null.php
@@ -0,0 +1,14 @@
+<?php declare(strict_types = 1);
+
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use function PHPStan\Testing\assertType;
+
+/** @var \Symfony\Component\HttpFoundation\Request $request */
+$request = doRequest();
+
+$session1 = $request->getSession();
+assertType(SessionInterface::class . '|null', $request->getSession());
+
+if ($request->hasSession()) {
+	assertType(SessionInterface::class, $request->getSession());
+}
diff --git a/tests/Type/Symfony/serializer.php b/tests/Type/Symfony/serializer.php
index fc16573e..8b75f575 100644
--- a/tests/Type/Symfony/serializer.php
+++ b/tests/Type/Symfony/serializer.php
@@ -1,10 +1,10 @@
 <?php declare(strict_types = 1);
 
-$serializer = new \Symfony\Component\Serializer\Serializer();
+use function PHPStan\Testing\assertType;
 
-$first = $serializer->deserialize('bar', 'Bar', 'format');
-$second = $serializer->deserialize('bar', 'Bar[]', 'format');
-$third = $serializer->deserialize('bar', 'Bar[][]', 'format');
-$fourth = $serializer->deserialize('bar');
+$serializer = new \Symfony\Component\Serializer\Serializer();
 
-die;
+assertType('Bar', $serializer->deserialize('bar', 'Bar', 'format'));
+assertType('array<Bar>', $serializer->deserialize('bar', 'Bar[]', 'format'));
+assertType('array<array<Bar>>', $serializer->deserialize('bar', 'Bar[][]', 'format'));
+assertType('mixed', $serializer->deserialize('bar'));

From e75b245b26b153f7b626a39227e15975f50ab7df Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 25 Apr 2021 19:23:02 +0200
Subject: [PATCH 077/297] Run PHPCS on PHP 8

---
 .github/workflows/build.yml | 2 +-
 build-cs/composer.json      | 2 +-
 phpcs.xml                   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 83f79354..fc0ee8f4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -59,7 +59,7 @@ jobs:
         uses: "shivammathur/setup-php@v2"
         with:
           coverage: "none"
-          php-version: "7.4"
+          php-version: "8.0"
 
       - name: "Validate Composer"
         run: "composer validate"
diff --git a/build-cs/composer.json b/build-cs/composer.json
index 9acd0275..ed7744e1 100644
--- a/build-cs/composer.json
+++ b/build-cs/composer.json
@@ -1,6 +1,6 @@
 {
 	"require-dev": {
-		"consistence/coding-standard": "^3.10",
+		"consistence-community/coding-standard": "^3.10",
 		"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
 		"slevomat/coding-standard": "^6.4"
 	}
diff --git a/phpcs.xml b/phpcs.xml
index feb4d827..1a9fb163 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <ruleset name="PHPStan PHPUnit extensions and rules">
-    <rule ref="build-cs/vendor/consistence/coding-standard/Consistence/ruleset.xml">
+    <rule ref="build-cs/vendor/consistence-community/coding-standard/Consistence/ruleset.xml">
         <exclude name="Squiz.Functions.GlobalFunction.Found"/>
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword"/>
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation"/>

From 8ae41d19aa5ee5cbe2829bb6cadce6681c58aea6 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 25 Apr 2021 19:25:18 +0200
Subject: [PATCH 078/297] Fix tests

---
 phpcs.xml                                     |  2 +-
 phpstan.neon                                  |  9 +-----
 ...nerInterfacePrivateServiceRuleFakeTest.php | 14 +++++++++
 ...ntainerInterfacePrivateServiceRuleTest.php | 11 +++++++
 ...nerInterfaceUnknownServiceRuleFakeTest.php | 10 ++++--
 ...ntainerInterfaceUnknownServiceRuleTest.php | 10 ++++--
 tests/Type/Symfony/ExtensionTest.php          | 31 +++++++++----------
 .../Symfony/ExtensionTestWithoutContainer.php |  6 ++--
 .../Symfony/{ => data}/ExampleACommand.php    |  0
 .../Symfony/{ => data}/ExampleBCommand.php    |  0
 .../Symfony/{ => data}/ExampleBaseCommand.php |  0
 .../Symfony/{ => data}/ExampleController.php  |  0
 .../ExampleControllerWithoutContainer.php     |  0
 .../{ => data}/ExampleOptionCommand.php       |  0
 .../Type/Symfony/{ => data}/denormalizer.php  |  0
 .../Type/Symfony/{ => data}/envelope_all.php  |  0
 .../Symfony/{ => data}/header_bag_get.php     |  0
 tests/Type/Symfony/{ => data}/input_bag.php   |  0
 .../Symfony/{ => data}/kernel_interface.php   |  0
 .../{ => data}/request_get_content.php        |  0
 .../{ => data}/request_get_session.php        |  0
 .../{ => data}/request_get_session_null.php   |  0
 tests/Type/Symfony/{ => data}/serializer.php  |  0
 .../Symfony/{Config => data}/tree_builder.php |  0
 24 files changed, 62 insertions(+), 31 deletions(-)
 rename tests/Type/Symfony/{ => data}/ExampleACommand.php (100%)
 rename tests/Type/Symfony/{ => data}/ExampleBCommand.php (100%)
 rename tests/Type/Symfony/{ => data}/ExampleBaseCommand.php (100%)
 rename tests/Type/Symfony/{ => data}/ExampleController.php (100%)
 rename tests/Type/Symfony/{ => data}/ExampleControllerWithoutContainer.php (100%)
 rename tests/Type/Symfony/{ => data}/ExampleOptionCommand.php (100%)
 rename tests/Type/Symfony/{ => data}/denormalizer.php (100%)
 rename tests/Type/Symfony/{ => data}/envelope_all.php (100%)
 rename tests/Type/Symfony/{ => data}/header_bag_get.php (100%)
 rename tests/Type/Symfony/{ => data}/input_bag.php (100%)
 rename tests/Type/Symfony/{ => data}/kernel_interface.php (100%)
 rename tests/Type/Symfony/{ => data}/request_get_content.php (100%)
 rename tests/Type/Symfony/{ => data}/request_get_session.php (100%)
 rename tests/Type/Symfony/{ => data}/request_get_session_null.php (100%)
 rename tests/Type/Symfony/{ => data}/serializer.php (100%)
 rename tests/Type/Symfony/{Config => data}/tree_builder.php (100%)

diff --git a/phpcs.xml b/phpcs.xml
index 1a9fb163..f6ec19e6 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -54,5 +54,5 @@
         </properties>
     </rule>
     <exclude-pattern>tests/tmp</exclude-pattern>
-    <exclude-pattern>tests/Symfony/ExampleContainer.php</exclude-pattern>
+	<exclude-pattern>tests/*/data</exclude-pattern>
 </ruleset>
diff --git a/phpstan.neon b/phpstan.neon
index bc33def1..13242793 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -11,11 +11,4 @@ parameters:
 		- tests/tmp/*
 		- tests/*/Example*.php
 		- tests/*/console_application_loader.php
-		- tests/*/envelope_all.php
-		- tests/*/header_bag_get.php
-		- tests/*/input_bag.php
-		- tests/*/kernel_interface.php
-		- tests/*/request_get_content.php
-		- tests/*/request_get_session.php
-		- tests/*/serializer.php
-		- tests/*/denormalizer.php
+		- tests/*/data/*
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
index 6aa4ae47..81925291 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -19,6 +19,9 @@ protected function getRule(): Rule
 
 	public function testGetPrivateService(): void
 	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleController.php',
@@ -29,6 +32,9 @@ public function testGetPrivateService(): void
 
 	public function testGetPrivateServiceInAbstractController(): void
 	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleAbstractController.php',
@@ -43,6 +49,10 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 			self::markTestSkipped('The test needs Symfony\Component\DependencyInjection\ServiceSubscriberInterface class.');
 		}
 
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
+
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleLegacyServiceSubscriber.php',
@@ -59,6 +69,10 @@ public function testGetPrivateServiceInServiceSubscriber(): void
 			self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.');
 		}
 
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
+
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleServiceSubscriber.php',
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index 3704818e..b3a8fa31 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -19,6 +19,9 @@ protected function getRule(): Rule
 
 	public function testGetPrivateService(): void
 	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleController.php',
@@ -38,6 +41,10 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 			self::markTestSkipped('The test needs Symfony\Component\DependencyInjection\ServiceSubscriberInterface class.');
 		}
 
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
+
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleLegacyServiceSubscriber.php',
@@ -54,6 +61,10 @@ public function testGetPrivateServiceInServiceSubscriber(): void
 			self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.');
 		}
 
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
+
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleServiceSubscriber.php',
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index 87404430..33e3015d 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -7,7 +7,6 @@
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 
 /**
  * @extends RuleTestCase<ContainerInterfaceUnknownServiceRule>
@@ -26,12 +25,15 @@ protected function getRule(): Rule
 	protected function getMethodTypeSpecifyingExtensions(): array
 	{
 		return [
-			new ServiceTypeSpecifyingExtension(Controller::class, new Standard()),
+			new ServiceTypeSpecifyingExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', new Standard()),
 		];
 	}
 
 	public function testGetPrivateService(): void
 	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleController.php',
@@ -42,6 +44,10 @@ public function testGetPrivateService(): void
 
 	public function testGetPrivateServiceInAbstractController(): void
 	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
+
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleAbstractController.php',
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 776812af..306e219a 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -8,7 +8,6 @@
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 
 /**
  * @extends RuleTestCase<ContainerInterfaceUnknownServiceRule>
@@ -27,13 +26,16 @@ protected function getRule(): Rule
 	protected function getMethodTypeSpecifyingExtensions(): array
 	{
 		return [
-			new ServiceTypeSpecifyingExtension(Controller::class, new Standard()),
+			new ServiceTypeSpecifyingExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', new Standard()),
 			new ServiceTypeSpecifyingExtension(AbstractController::class, new Standard()),
 		];
 	}
 
 	public function testGetPrivateService(): void
 	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleController.php',
@@ -49,6 +51,10 @@ public function testGetPrivateService(): void
 
 	public function testGetPrivateServiceInAbstractController(): void
 	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			self::markTestSkipped();
+		}
+
 		$this->analyse(
 			[
 				__DIR__ . '/ExampleAbstractController.php',
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index b85c943f..2c6a0762 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -9,36 +9,36 @@
 class ExtensionTest extends TypeInferenceTestCase
 {
 
+	/** @return mixed[] */
 	public function dataFileAsserts(): iterable
 	{
-		yield from $this->gatherAssertTypes(__DIR__ . '/envelope_all.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/header_bag_get.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php');
 
 		if (class_exists('Symfony\Component\HttpFoundation\InputBag')) {
-			yield from $this->gatherAssertTypes(__DIR__ . '/input_bag.php');
+			yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag.php');
 		}
 
-		yield from $this->gatherAssertTypes(__DIR__ . '/Config/tree_builder.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/ExampleBaseCommand.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/ExampleOptionCommand.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/kernel_interface.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/request_get_content.php');
-
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/tree_builder.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleBaseCommand.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionCommand.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/kernel_interface.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/request_get_content.php');
 
 		$ref = new ReflectionMethod(Request::class, 'getSession');
 		$doc = (string) $ref->getDocComment();
 		if (strpos($doc, '@return SessionInterface|null') !== false) {
-			yield from $this->gatherAssertTypes(__DIR__ . '/request_get_session_null.php');
+			yield from $this->gatherAssertTypes(__DIR__ . '/data/request_get_session_null.php');
 		} else {
-			yield from $this->gatherAssertTypes(__DIR__ . '/request_get_session.php');
+			yield from $this->gatherAssertTypes(__DIR__ . '/data/request_get_session.php');
 		}
 
-		yield from $this->gatherAssertTypes(__DIR__ . '/serializer.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/denormalizer.php');
-
 		if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
-			yield from $this->gatherAssertTypes(__DIR__ . '/ExampleController.php');
+			yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php');
 		}
+
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/serializer.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php');
 	}
 
 	/**
@@ -64,5 +64,4 @@ public static function getAdditionalConfigFiles(): array
 		];
 	}
 
-
 }
diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
index 13b1a2dd..85b52315 100644
--- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php
+++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
@@ -7,9 +7,10 @@
 class ExtensionTestWithoutContainer extends TypeInferenceTestCase
 {
 
+	/** @return mixed[] */
 	public function dataFileAsserts(): iterable
 	{
-		yield from $this->gatherAssertTypes(__DIR__ . '/ExampleControllerWithoutContainer.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleControllerWithoutContainer.php');
 	}
 
 	/**
@@ -25,7 +26,7 @@ public function testFileAsserts(
 	): void
 	{
 		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
-			$this->markTestSkipped('Needs class Symfony\Bundle\FrameworkBundle\Controller\Controller');
+			self::markTestSkipped('Needs class Symfony\Bundle\FrameworkBundle\Controller\Controller');
 		}
 		$this->assertFileAsserts($assertType, $file, ...$args);
 	}
@@ -36,4 +37,5 @@ public static function getAdditionalConfigFiles(): array
 			__DIR__ . '/../../../extension.neon',
 		];
 	}
+
 }
diff --git a/tests/Type/Symfony/ExampleACommand.php b/tests/Type/Symfony/data/ExampleACommand.php
similarity index 100%
rename from tests/Type/Symfony/ExampleACommand.php
rename to tests/Type/Symfony/data/ExampleACommand.php
diff --git a/tests/Type/Symfony/ExampleBCommand.php b/tests/Type/Symfony/data/ExampleBCommand.php
similarity index 100%
rename from tests/Type/Symfony/ExampleBCommand.php
rename to tests/Type/Symfony/data/ExampleBCommand.php
diff --git a/tests/Type/Symfony/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php
similarity index 100%
rename from tests/Type/Symfony/ExampleBaseCommand.php
rename to tests/Type/Symfony/data/ExampleBaseCommand.php
diff --git a/tests/Type/Symfony/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
similarity index 100%
rename from tests/Type/Symfony/ExampleController.php
rename to tests/Type/Symfony/data/ExampleController.php
diff --git a/tests/Type/Symfony/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
similarity index 100%
rename from tests/Type/Symfony/ExampleControllerWithoutContainer.php
rename to tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
diff --git a/tests/Type/Symfony/ExampleOptionCommand.php b/tests/Type/Symfony/data/ExampleOptionCommand.php
similarity index 100%
rename from tests/Type/Symfony/ExampleOptionCommand.php
rename to tests/Type/Symfony/data/ExampleOptionCommand.php
diff --git a/tests/Type/Symfony/denormalizer.php b/tests/Type/Symfony/data/denormalizer.php
similarity index 100%
rename from tests/Type/Symfony/denormalizer.php
rename to tests/Type/Symfony/data/denormalizer.php
diff --git a/tests/Type/Symfony/envelope_all.php b/tests/Type/Symfony/data/envelope_all.php
similarity index 100%
rename from tests/Type/Symfony/envelope_all.php
rename to tests/Type/Symfony/data/envelope_all.php
diff --git a/tests/Type/Symfony/header_bag_get.php b/tests/Type/Symfony/data/header_bag_get.php
similarity index 100%
rename from tests/Type/Symfony/header_bag_get.php
rename to tests/Type/Symfony/data/header_bag_get.php
diff --git a/tests/Type/Symfony/input_bag.php b/tests/Type/Symfony/data/input_bag.php
similarity index 100%
rename from tests/Type/Symfony/input_bag.php
rename to tests/Type/Symfony/data/input_bag.php
diff --git a/tests/Type/Symfony/kernel_interface.php b/tests/Type/Symfony/data/kernel_interface.php
similarity index 100%
rename from tests/Type/Symfony/kernel_interface.php
rename to tests/Type/Symfony/data/kernel_interface.php
diff --git a/tests/Type/Symfony/request_get_content.php b/tests/Type/Symfony/data/request_get_content.php
similarity index 100%
rename from tests/Type/Symfony/request_get_content.php
rename to tests/Type/Symfony/data/request_get_content.php
diff --git a/tests/Type/Symfony/request_get_session.php b/tests/Type/Symfony/data/request_get_session.php
similarity index 100%
rename from tests/Type/Symfony/request_get_session.php
rename to tests/Type/Symfony/data/request_get_session.php
diff --git a/tests/Type/Symfony/request_get_session_null.php b/tests/Type/Symfony/data/request_get_session_null.php
similarity index 100%
rename from tests/Type/Symfony/request_get_session_null.php
rename to tests/Type/Symfony/data/request_get_session_null.php
diff --git a/tests/Type/Symfony/serializer.php b/tests/Type/Symfony/data/serializer.php
similarity index 100%
rename from tests/Type/Symfony/serializer.php
rename to tests/Type/Symfony/data/serializer.php
diff --git a/tests/Type/Symfony/Config/tree_builder.php b/tests/Type/Symfony/data/tree_builder.php
similarity index 100%
rename from tests/Type/Symfony/Config/tree_builder.php
rename to tests/Type/Symfony/data/tree_builder.php

From e4b9ea96b78057a758ac648986d2339259448293 Mon Sep 17 00:00:00 2001
From: Andrey Bondar <andrey-bondar@users.noreply.github.com>
Date: Tue, 27 Apr 2021 12:18:16 +0300
Subject: [PATCH 079/297] Allow fetching private services from ServiceLocator

---
 src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php | 3 ++-
 tests/Rules/Symfony/ExampleController.php                  | 5 +++++
 tests/Rules/Symfony/container.xml                          | 5 +++++
 3 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index f97633c0..c471e6ba 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -54,7 +54,8 @@ public function processNode(Node $node, Scope $scope): array
 		$isTestContainerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Test\TestContainer'))->isSuperTypeOf($argType);
 		$isOldServiceSubscriber = (new ObjectType('Symfony\Component\DependencyInjection\ServiceSubscriberInterface'))->isSuperTypeOf($argType);
 		$isServiceSubscriber = (new ObjectType('Symfony\Contracts\Service\ServiceSubscriberInterface'))->isSuperTypeOf($argType);
-		if ($isTestContainerType->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes()) {
+		$isServiceLocator = (new ObjectType('Symfony\Component\DependencyInjection\ServiceLocator'))->isSuperTypeOf($argType);
+		if ($isTestContainerType->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes() || $isServiceLocator->yes()) {
 			return [];
 		}
 
diff --git a/tests/Rules/Symfony/ExampleController.php b/tests/Rules/Symfony/ExampleController.php
index 65d1359b..edbdaaf5 100644
--- a/tests/Rules/Symfony/ExampleController.php
+++ b/tests/Rules/Symfony/ExampleController.php
@@ -39,4 +39,9 @@ public function unknownGuardedServiceOutsideOfContext(): void
 		$this->get('unknown');
 	}
 
+	public function privateServiceFromServiceLocator(): void
+	{
+		$this->get('service_locator')->get('private');
+	}
+
 }
diff --git a/tests/Rules/Symfony/container.xml b/tests/Rules/Symfony/container.xml
index f21aeae3..f3261e0a 100644
--- a/tests/Rules/Symfony/container.xml
+++ b/tests/Rules/Symfony/container.xml
@@ -3,5 +3,10 @@
     <services>
         <service id="private" class="Foo" public="false"></service>
         <service id="public" class="Foo"></service>
+        <service id="service_locator" class="Symfony\Component\DependencyInjection\ServiceLocator" public="true">
+            <argument type="collection">
+                <argument key="private" type="service" id="private"/>
+            </argument>
+        </service>
     </services>
 </container>

From 2dad4deeb117713a6f6a2d92ce8e439ddc7454d7 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 30 Apr 2021 09:23:12 +0200
Subject: [PATCH 080/297] Lock closed issues

---
 .github/workflows/lock-closed-issues.yml | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 .github/workflows/lock-closed-issues.yml

diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml
new file mode 100644
index 00000000..960c1ba5
--- /dev/null
+++ b/.github/workflows/lock-closed-issues.yml
@@ -0,0 +1,23 @@
+name: 'Lock Issues'
+
+on:
+  schedule:
+    - cron: '0 0 * * *'
+
+jobs:
+  lock:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: dessant/lock-threads@v2
+        with:
+          github-token: ${{ github.token }}
+          issue-lock-inactive-days: '31'
+          issue-exclude-created-before: ''
+          issue-exclude-labels: ''
+          issue-lock-labels: ''
+          issue-lock-comment: >
+            This thread has been automatically locked since there has not been
+            any recent activity after it was closed. Please open a new issue for
+            related bugs.
+          issue-lock-reason: 'resolved'
+          process-only: 'issues'

From 61cabd46bd669e63720e5c127108b4b4cc622438 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 30 Apr 2021 13:14:45 +0200
Subject: [PATCH 081/297] Unchecked exceptions configuration

---
 composer.json  | 2 +-
 extension.neon | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 20f2fce2..81c70f48 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.85"
+    "phpstan/phpstan": "^0.12.86"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index 002b98a4..a8be8071 100644
--- a/extension.neon
+++ b/extension.neon
@@ -1,6 +1,9 @@
 parameters:
 	dynamicConstantNames:
 		- Symfony\Component\HttpKernel\Kernel::VERSION_ID
+	exceptions:
+		uncheckedExceptionClasses:
+			- 'Symfony\Component\Console\Exception\InvalidArgumentException'
 	symfony:
 		container_xml_path: null
 		constant_hassers: true

From 4e4353ef28765f1b5e0ebab1bced58f087f2044b Mon Sep 17 00:00:00 2001
From: Hugo Alliaume <hugo@alliau.me>
Date: Sun, 2 May 2021 11:02:01 +0200
Subject: [PATCH 082/297] Understand types returned by
 Container::getParameter() and similar methods

---
 README.md                                     |   7 +-
 composer.json                                 |   4 +-
 extension.neon                                |  25 +++
 src/Symfony/DefaultParameterMap.php           |  43 ++++++
 src/Symfony/FakeParameterMap.php              |  29 ++++
 src/Symfony/Parameter.php                     |  40 +++++
 src/Symfony/ParameterDefinition.php           |  15 ++
 src/Symfony/ParameterMap.php                  |  20 +++
 src/Symfony/ParameterMapFactory.php           |  10 ++
 src/Symfony/XmlParameterMapFactory.php        | 106 +++++++++++++
 .../ParameterDynamicReturnTypeExtension.php   | 127 +++++++++++++++
 tests/Symfony/DefaultParameterMapTest.php     | 144 ++++++++++++++++++
 tests/Symfony/container.xml                   |  32 ++++
 .../Symfony/containers/bugfix%2Fcontainer.xml |  32 ++++
 tests/Type/Symfony/ExtensionTest.php          |   4 +
 .../Symfony/ExtensionTestWithoutContainer.php |  24 ++-
 tests/Type/Symfony/container.xml              |  32 ++++
 .../data/ExampleAbstractController.php        |  96 ++++++++++++
 ...mpleAbstractControllerWithoutContainer.php |  95 ++++++++++++
 tests/Type/Symfony/data/ExampleController.php |  72 +++++++++
 .../ExampleControllerWithoutContainer.php     |  72 +++++++++
 21 files changed, 1020 insertions(+), 9 deletions(-)
 create mode 100644 src/Symfony/DefaultParameterMap.php
 create mode 100644 src/Symfony/FakeParameterMap.php
 create mode 100644 src/Symfony/Parameter.php
 create mode 100644 src/Symfony/ParameterDefinition.php
 create mode 100644 src/Symfony/ParameterMap.php
 create mode 100644 src/Symfony/ParameterMapFactory.php
 create mode 100644 src/Symfony/XmlParameterMapFactory.php
 create mode 100644 src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
 create mode 100644 tests/Symfony/DefaultParameterMapTest.php
 create mode 100644 tests/Type/Symfony/data/ExampleAbstractController.php
 create mode 100644 tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php

diff --git a/README.md b/README.md
index 4cb627e4..b853b3fa 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,11 @@ This extension provides following features:
 
 * Provides correct return type for `ContainerInterface::get()` and `::has()` methods.
 * Provides correct return type for `Controller::get()` and `::has()` methods.
+* Provides correct return type for `AbstractController::get()` and `::has()` methods.
+* Provides correct return type for `ContainerInterface::getParameter()` and `::hasParameter()` methods.
+* Provides correct return type for `ParameterBagInterface::get()` and `::has()` methods.
+* Provides correct return type for `Controller::getParameter()` method.
+* Provides correct return type for `AbstractController::getParameter()` method.
 * Provides correct return type for `Request::getContent()` method based on the `$asResource` parameter.
 * Provides correct return type for `HeaderBag::get()` method based on the `$first` parameter.
 * Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter.
@@ -57,7 +62,7 @@ You have to provide a path to `srcDevDebugProjectContainer.xml` or similar XML f
 parameters:
     symfony:
         container_xml_path: var/cache/dev/srcDevDebugProjectContainer.xml
-        # or with Symfony 4.2+
+        # or with Symfony 4.2+
         container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml
         # or with Symfony 5+
         container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml
diff --git a/composer.json b/composer.json
index 20f2fce2..23f4f7a9 100644
--- a/composer.json
+++ b/composer.json
@@ -26,9 +26,9 @@
     "phpstan/phpstan-phpunit": "^0.12.16",
     "phpstan/phpstan-strict-rules": "^0.12.5",
     "phpunit/phpunit": "^7.5.20",
-    "symfony/console": "^4.0 || ^5.0",
     "symfony/config": "^4.2 || ^5.0",
-    "symfony/framework-bundle": "^4.0 || ^5.0",
+    "symfony/console": "^4.0 || ^5.0",
+    "symfony/framework-bundle": "^4.4 || ^5.0",
     "symfony/http-foundation": "^4.0 || ^5.0",
     "symfony/messenger": "^4.2 || ^5.0",
     "symfony/serializer": "^4.0 || ^5.0"
diff --git a/extension.neon b/extension.neon
index 002b98a4..1b74bc46 100644
--- a/extension.neon
+++ b/extension.neon
@@ -63,6 +63,13 @@ services:
 	-
 		factory: @symfony.serviceMapFactory::create()
 
+	# parameter map
+	symfony.parameterMapFactory:
+		class: PHPStan\Symfony\ParameterMapFactory
+		factory: PHPStan\Symfony\XmlParameterMapFactory(%symfony.container_xml_path%)
+	-
+		factory: @symfony.parameterMapFactory::create()
+
 	# ControllerTrait::get()/has() return type
 	-
 		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%)
@@ -215,3 +222,21 @@ services:
 	-
 		class: PHPStan\Type\Symfony\KernelInterfaceDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# ParameterBagInterface::get()/has() return type
+	-
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constant_hassers%)
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# ContainerInterface::getParameter()/hasParameter() return type
+	-
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter', %symfony.constant_hassers%)
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# (Abstract)Controller::getParameter() return type
+	-
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constant_hassers%)
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+	-
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constant_hassers%)
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php
new file mode 100644
index 00000000..a58ed9fc
--- /dev/null
+++ b/src/Symfony/DefaultParameterMap.php
@@ -0,0 +1,43 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PhpParser\Node\Expr;
+use PHPStan\Analyser\Scope;
+use PHPStan\Type\TypeUtils;
+use function count;
+
+final class DefaultParameterMap implements ParameterMap
+{
+
+	/** @var \PHPStan\Symfony\ParameterDefinition[] */
+	private $parameters;
+
+	/**
+	 * @param \PHPStan\Symfony\ParameterDefinition[] $parameters
+	 */
+	public function __construct(array $parameters)
+	{
+		$this->parameters = $parameters;
+	}
+
+	/**
+	 * @return \PHPStan\Symfony\ParameterDefinition[]
+	 */
+	public function getParameters(): array
+	{
+		return $this->parameters;
+	}
+
+	public function getParameter(string $key): ?ParameterDefinition
+	{
+		return $this->parameters[$key] ?? null;
+	}
+
+	public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
+	{
+		$strings = TypeUtils::getConstantStrings($scope->getType($node));
+		return count($strings) === 1 ? $strings[0]->getValue() : null;
+	}
+
+}
diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php
new file mode 100644
index 00000000..7e723411
--- /dev/null
+++ b/src/Symfony/FakeParameterMap.php
@@ -0,0 +1,29 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PhpParser\Node\Expr;
+use PHPStan\Analyser\Scope;
+
+final class FakeParameterMap implements ParameterMap
+{
+
+	/**
+	 * @return \PHPStan\Symfony\ParameterDefinition[]
+	 */
+	public function getParameters(): array
+	{
+		return [];
+	}
+
+	public function getParameter(string $key): ?ParameterDefinition
+	{
+		return null;
+	}
+
+	public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
+	{
+		return null;
+	}
+
+}
diff --git a/src/Symfony/Parameter.php b/src/Symfony/Parameter.php
new file mode 100644
index 00000000..1df679bc
--- /dev/null
+++ b/src/Symfony/Parameter.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+final class Parameter implements ParameterDefinition
+{
+
+	/** @var string */
+	private $key;
+
+	/** @var array<mixed>|bool|float|int|string */
+	private $value;
+
+	/**
+	 * @param string $key
+	 * @param array<mixed>|bool|float|int|string $value
+	 */
+	public function __construct(
+		string $key,
+		$value
+	)
+	{
+		$this->key = $key;
+		$this->value = $value;
+	}
+
+	public function getKey(): string
+	{
+		return $this->key;
+	}
+
+	/**
+	 * @return array<mixed>|bool|float|int|string
+	 */
+	public function getValue()
+	{
+		return $this->value;
+	}
+
+}
diff --git a/src/Symfony/ParameterDefinition.php b/src/Symfony/ParameterDefinition.php
new file mode 100644
index 00000000..e1aa2eaa
--- /dev/null
+++ b/src/Symfony/ParameterDefinition.php
@@ -0,0 +1,15 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+interface ParameterDefinition
+{
+
+	public function getKey(): string;
+
+	/**
+	 * @return array<mixed>|bool|float|int|string
+	 */
+	public function getValue();
+
+}
diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php
new file mode 100644
index 00000000..fc14fb2c
--- /dev/null
+++ b/src/Symfony/ParameterMap.php
@@ -0,0 +1,20 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PhpParser\Node\Expr;
+use PHPStan\Analyser\Scope;
+
+interface ParameterMap
+{
+
+	/**
+	 * @return \PHPStan\Symfony\ParameterDefinition[]
+	 */
+	public function getParameters(): array;
+
+	public function getParameter(string $key): ?ParameterDefinition;
+
+	public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string;
+
+}
diff --git a/src/Symfony/ParameterMapFactory.php b/src/Symfony/ParameterMapFactory.php
new file mode 100644
index 00000000..4e318540
--- /dev/null
+++ b/src/Symfony/ParameterMapFactory.php
@@ -0,0 +1,10 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+interface ParameterMapFactory
+{
+
+	public function create(): ParameterMap;
+
+}
diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php
new file mode 100644
index 00000000..6a7575f4
--- /dev/null
+++ b/src/Symfony/XmlParameterMapFactory.php
@@ -0,0 +1,106 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use function sprintf;
+
+final class XmlParameterMapFactory implements ParameterMapFactory
+{
+
+	/** @var string|null */
+	private $containerXml;
+
+	public function __construct(?string $containerXml)
+	{
+		$this->containerXml = $containerXml;
+	}
+
+	public function create(): ParameterMap
+	{
+		if ($this->containerXml === null) {
+			return new FakeParameterMap();
+		}
+
+		$fileContents = file_get_contents($this->containerXml);
+		if ($fileContents === false) {
+			throw new XmlContainerNotExistsException(sprintf('Container %s does not exist', $this->containerXml));
+		}
+
+		$xml = @simplexml_load_string($fileContents);
+		if ($xml === false) {
+			throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml));
+		}
+
+		/** @var \PHPStan\Symfony\Parameter[] $parameters */
+		$parameters = [];
+		foreach ($xml->parameters->parameter as $def) {
+			/** @var \SimpleXMLElement $attrs */
+			$attrs = $def->attributes();
+
+			$parameter = new Parameter(
+				(string) $attrs->key,
+				$this->getNodeValue($def)
+			);
+
+			$parameters[$parameter->getKey()] = $parameter;
+		}
+
+		return new DefaultParameterMap($parameters);
+	}
+
+	/**
+	 * @return array<mixed>|bool|float|int|string
+	 */
+	private function getNodeValue(\SimpleXMLElement $def)
+	{
+		/** @var \SimpleXMLElement $attrs */
+		$attrs = $def->attributes();
+
+		$value = null;
+		switch ((string) $attrs->type) {
+			case 'collection':
+				$value = [];
+				foreach ($def->children() as $child) {
+					/** @var \SimpleXMLElement $childAttrs */
+					$childAttrs = $child->attributes();
+
+					if (isset($childAttrs->key)) {
+						$value[(string) $childAttrs->key] = $this->getNodeValue($child);
+					} else {
+						$value[] = $this->getNodeValue($child);
+					}
+				}
+				break;
+
+			case 'string':
+				$value = (string) $def;
+				break;
+
+			case 'binary':
+				$value = base64_decode((string) $def, true);
+				if ($value === false) {
+					throw new \InvalidArgumentException(sprintf('Parameter "%s" of binary type is not valid base64 encoded string.', (string) $attrs->key));
+				}
+
+				break;
+
+			default:
+				$value = (string) $def;
+
+				if (is_numeric($value)) {
+					if (strpos($value, '.') !== false) {
+						$value = (float) $value;
+					} else {
+						$value = (int) $value;
+					}
+				} elseif ($value === 'true') {
+					$value = true;
+				} elseif ($value === 'false') {
+					$value = false;
+				}
+		}
+
+		return $value;
+	}
+
+}
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..adce58fb
--- /dev/null
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -0,0 +1,127 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\ShouldNotHappenException;
+use PHPStan\Symfony\ParameterMap;
+use PHPStan\Type\ArrayType;
+use PHPStan\Type\BooleanType;
+use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\FloatType;
+use PHPStan\Type\IntegerType;
+use PHPStan\Type\MixedType;
+use PHPStan\Type\NullType;
+use PHPStan\Type\StringType;
+use PHPStan\Type\Type;
+use PHPStan\Type\UnionType;
+use function in_array;
+
+final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var string */
+	private $className;
+
+	/** @var string|null */
+	private $methodGet;
+
+	/** @var string|null */
+	private $methodHas;
+
+	/** @var bool */
+	private $constantHassers;
+
+	/** @var \PHPStan\Symfony\ParameterMap */
+	private $parameterMap;
+
+	public function __construct(string $className, ?string $methodGet, ?string $methodHas, bool $constantHassers, ParameterMap $symfonyParameterMap)
+	{
+		$this->className = $className;
+		$this->methodGet = $methodGet;
+		$this->methodHas = $methodHas;
+		$this->constantHassers = $constantHassers;
+		$this->parameterMap = $symfonyParameterMap;
+	}
+
+	public function getClass(): string
+	{
+		return $this->className;
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		$methods = array_filter([$this->methodGet, $this->methodHas], function (?string $method): bool {
+			return $method !== null;
+		});
+
+		return in_array($methodReflection->getName(), $methods, true);
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	{
+		switch ($methodReflection->getName()) {
+			case $this->methodGet:
+				return $this->getGetTypeFromMethodCall($methodReflection, $methodCall, $scope);
+			case $this->methodHas:
+				return $this->getHasTypeFromMethodCall($methodReflection, $methodCall, $scope);
+		}
+		throw new ShouldNotHappenException();
+	}
+
+	private function getGetTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		// We don't use the method's return type because this won't work properly with lowest and
+		// highest versions of Symfony ("mixed" for lowest, "array|bool|float|integer|string|null" for highest).
+		$returnType = new UnionType([
+			new ArrayType(new MixedType(), new MixedType()),
+			new BooleanType(),
+			new FloatType(),
+			new IntegerType(),
+			new StringType(),
+			new NullType(),
+		]);
+		if (!isset($methodCall->args[0])) {
+			return $returnType;
+		}
+
+		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope);
+		if ($parameterKey !== null) {
+			$parameter = $this->parameterMap->getParameter($parameterKey);
+			if ($parameter !== null) {
+				return $scope->getTypeFromValue($parameter->getValue());
+			}
+		}
+
+		return $returnType;
+	}
+
+	private function getHasTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		if (!isset($methodCall->args[0]) || !$this->constantHassers) {
+			return $returnType;
+		}
+
+		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope);
+		if ($parameterKey !== null) {
+			$parameter = $this->parameterMap->getParameter($parameterKey);
+			return new ConstantBooleanType($parameter !== null);
+		}
+
+		return $returnType;
+	}
+
+}
diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php
new file mode 100644
index 00000000..0b97d078
--- /dev/null
+++ b/tests/Symfony/DefaultParameterMapTest.php
@@ -0,0 +1,144 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use Iterator;
+use PHPUnit\Framework\TestCase;
+
+final class DefaultParameterMapTest extends TestCase
+{
+
+	/**
+	 * @dataProvider getParameterProvider
+	 */
+	public function testGetParameter(string $key, callable $validator): void
+	{
+		$factory = new XmlParameterMapFactory(__DIR__ . '/container.xml');
+		$validator($factory->create()->getParameter($key));
+	}
+
+	public function testGetParameterEscapedPath(): void
+	{
+		$factory = new XmlParameterMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml');
+		$serviceMap = $factory->create();
+
+		self::assertNotNull($serviceMap->getParameter('app.string'));
+	}
+
+	/**
+	 * @return \Iterator<mixed>
+	 */
+	public function getParameterProvider(): Iterator
+	{
+		yield [
+			'unknown',
+			function (?Parameter $parameter): void {
+				self::assertNull($parameter);
+			},
+		];
+		yield [
+			'app.string',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.string', $parameter->getKey());
+				self::assertSame('abcdef', $parameter->getValue());
+			},
+		];
+		yield [
+			'app.int',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.int', $parameter->getKey());
+				self::assertSame(123, $parameter->getValue());
+			},
+		];
+		yield [
+			'app.int_as_string',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.int_as_string', $parameter->getKey());
+				self::assertSame('123', $parameter->getValue());
+			},
+		];
+		yield [
+			'app.float',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.float', $parameter->getKey());
+				self::assertSame(123.45, $parameter->getValue());
+			},
+		];
+		yield [
+			'app.float_as_string',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.float_as_string', $parameter->getKey());
+				self::assertSame('123.45', $parameter->getValue());
+			},
+		];
+		yield [
+			'app.boolean',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.boolean', $parameter->getKey());
+				self::assertTrue($parameter->getValue());
+			},
+		];
+		yield [
+			'app.boolean_as_string',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.boolean_as_string', $parameter->getKey());
+				self::assertSame('true', $parameter->getValue());
+			},
+		];
+		yield [
+			'app.list',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.list', $parameter->getKey());
+				self::assertEquals(['en', 'es', 'fr'], $parameter->getValue());
+			},
+		];
+		yield [
+			'app.list_of_list',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.list_of_list', $parameter->getKey());
+				self::assertEquals([
+					['name' => 'the name', 'value' => 'the value'],
+					['name' => 'another name', 'value' => 'another value'],
+				], $parameter->getValue());
+			},
+		];
+		yield [
+			'app.map',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.map', $parameter->getKey());
+				self::assertEquals([
+					'a' => 'value of a',
+					'b' => 'value of b',
+					'c' => 'value of c',
+				], $parameter->getValue());
+			},
+		];
+		yield [
+			'app.binary',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.binary', $parameter->getKey());
+				self::assertSame('This is a Bell char ', $parameter->getValue());
+			},
+		];
+		yield [
+			'app.constant',
+			function (?Parameter $parameter): void {
+				self::assertNotNull($parameter);
+				self::assertSame('app.constant', $parameter->getKey());
+				self::assertSame('Y-m-d\TH:i:sP', $parameter->getValue());
+			},
+		];
+	}
+
+}
diff --git a/tests/Symfony/container.xml b/tests/Symfony/container.xml
index 8c8fbd55..5bed7715 100644
--- a/tests/Symfony/container.xml
+++ b/tests/Symfony/container.xml
@@ -1,5 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+  <parameters>
+    <parameter key="app.string">abcdef</parameter>
+    <parameter key="app.int">123</parameter>
+    <parameter key="app.int_as_string" type="string">123</parameter>
+    <parameter key="app.float">123.45</parameter>
+    <parameter key="app.float_as_string" type="string">123.45</parameter>
+    <parameter key="app.boolean">true</parameter>
+    <parameter key="app.boolean_as_string" type="string">true</parameter>
+    <parameter key="app.list" type="collection">
+      <parameter>en</parameter>
+      <parameter>es</parameter>
+      <parameter>fr</parameter>
+    </parameter>
+    <parameter key="app.list_of_list" type="collection">
+      <parameter type="collection">
+        <parameter key="name">the name</parameter>
+        <parameter key="value">the value</parameter>
+      </parameter>
+      <parameter type="collection">
+        <parameter key="name">another name</parameter>
+        <parameter key="value">another value</parameter>
+      </parameter>
+    </parameter>
+    <parameter key="app.map" type="collection">
+      <parameter key="a">value of a</parameter>
+      <parameter key="b">value of b</parameter>
+      <parameter key="c">value of c</parameter>
+    </parameter>
+    <parameter key="app.binary" type="binary">VGhpcyBpcyBhIEJlbGwgY2hhciAH</parameter>
+    <parameter key="app.constant">Y-m-d\TH:i:sP</parameter>
+  </parameters>
+
   <services>
     <service></service><!-- without id -->
     <service id="withoutClass"></service>
diff --git a/tests/Symfony/containers/bugfix%2Fcontainer.xml b/tests/Symfony/containers/bugfix%2Fcontainer.xml
index 8c8fbd55..5bed7715 100644
--- a/tests/Symfony/containers/bugfix%2Fcontainer.xml
+++ b/tests/Symfony/containers/bugfix%2Fcontainer.xml
@@ -1,5 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+  <parameters>
+    <parameter key="app.string">abcdef</parameter>
+    <parameter key="app.int">123</parameter>
+    <parameter key="app.int_as_string" type="string">123</parameter>
+    <parameter key="app.float">123.45</parameter>
+    <parameter key="app.float_as_string" type="string">123.45</parameter>
+    <parameter key="app.boolean">true</parameter>
+    <parameter key="app.boolean_as_string" type="string">true</parameter>
+    <parameter key="app.list" type="collection">
+      <parameter>en</parameter>
+      <parameter>es</parameter>
+      <parameter>fr</parameter>
+    </parameter>
+    <parameter key="app.list_of_list" type="collection">
+      <parameter type="collection">
+        <parameter key="name">the name</parameter>
+        <parameter key="value">the value</parameter>
+      </parameter>
+      <parameter type="collection">
+        <parameter key="name">another name</parameter>
+        <parameter key="value">another value</parameter>
+      </parameter>
+    </parameter>
+    <parameter key="app.map" type="collection">
+      <parameter key="a">value of a</parameter>
+      <parameter key="b">value of b</parameter>
+      <parameter key="c">value of c</parameter>
+    </parameter>
+    <parameter key="app.binary" type="binary">VGhpcyBpcyBhIEJlbGwgY2hhciAH</parameter>
+    <parameter key="app.constant">Y-m-d\TH:i:sP</parameter>
+  </parameters>
+
   <services>
     <service></service><!-- without id -->
     <service id="withoutClass"></service>
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 2c6a0762..a2de90ec 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -37,6 +37,10 @@ public function dataFileAsserts(): iterable
 			yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php');
 		}
 
+		if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) {
+			yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleAbstractController.php');
+		}
+
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/serializer.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php');
 	}
diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
index 85b52315..997b7e02 100644
--- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php
+++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
@@ -8,13 +8,28 @@ class ExtensionTestWithoutContainer extends TypeInferenceTestCase
 {
 
 	/** @return mixed[] */
-	public function dataFileAsserts(): iterable
+	public function dataExampleController(): iterable
 	{
-		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleControllerWithoutContainer.php');
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
+			return;
+		}
+
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php');
+	}
+
+	/** @return mixed[] */
+	public function dataAbstractController(): iterable
+	{
+		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) {
+			return;
+		}
+
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleAbstractController.php');
 	}
 
 	/**
-	 * @dataProvider dataFileAsserts
+	 * @dataProvider dataExampleController
+	 * @dataProvider dataAbstractController
 	 * @param string $assertType
 	 * @param string $file
 	 * @param mixed ...$args
@@ -25,9 +40,6 @@ public function testFileAsserts(
 		...$args
 	): void
 	{
-		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
-			self::markTestSkipped('Needs class Symfony\Bundle\FrameworkBundle\Controller\Controller');
-		}
 		$this->assertFileAsserts($assertType, $file, ...$args);
 	}
 
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index 978519d4..af82fd19 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -1,5 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+    <parameters>
+        <parameter key="app.string">abcdef</parameter>
+        <parameter key="app.int">123</parameter>
+        <parameter key="app.int_as_string" type="string">123</parameter>
+        <parameter key="app.float">123.45</parameter>
+        <parameter key="app.float_as_string" type="string">123.45</parameter>
+        <parameter key="app.boolean">true</parameter>
+        <parameter key="app.boolean_as_string" type="string">true</parameter>
+        <parameter key="app.list" type="collection">
+            <parameter>en</parameter>
+            <parameter>es</parameter>
+            <parameter>fr</parameter>
+        </parameter>
+        <parameter key="app.list_of_list" type="collection">
+            <parameter type="collection">
+                <parameter key="name">the name</parameter>
+                <parameter key="value">the value</parameter>
+            </parameter>
+            <parameter type="collection">
+                <parameter key="name">another name</parameter>
+                <parameter key="value">another value</parameter>
+            </parameter>
+        </parameter>
+        <parameter key="app.map" type="collection">
+            <parameter key="a">value of a</parameter>
+            <parameter key="b">value of b</parameter>
+            <parameter key="c">value of c</parameter>
+        </parameter>
+        <parameter key="app.binary" type="binary">VGhpcyBpcyBhIEJlbGwgY2hhciAH</parameter>
+        <parameter key="app.constant">Y-m-d\TH:i:sP</parameter>
+    </parameters>
+
     <services>
         <service id="foo" class="Foo"></service>
     </services>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
new file mode 100644
index 00000000..2bff7543
--- /dev/null
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -0,0 +1,96 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
+use function PHPStan\Testing\assertType;
+
+final class ExampleAbstractController extends AbstractController
+{
+
+	public function services(): void
+	{
+		assertType('Foo', $this->get('foo'));
+		assertType('object', $this->get('bar'));
+		assertType('object', $this->get(doFoo()));
+		assertType('object', $this->get());
+
+		assertType('true', $this->has('foo'));
+		assertType('false', $this->has('bar'));
+		assertType('bool', $this->has(doFoo()));
+		assertType('bool', $this->has());
+	}
+
+	public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void
+	{
+		assertType('array|bool|float|int|string|null', $container->getParameter('unknown'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('unknown'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('unknown'));
+		assertType("'abcdef'", $container->getParameter('app.string'));
+		assertType("'abcdef'", $parameterBag->get('app.string'));
+		assertType("'abcdef'", $this->getParameter('app.string'));
+		assertType('123', $container->getParameter('app.int'));
+		assertType('123', $parameterBag->get('app.int'));
+		assertType('123', $this->getParameter('app.int'));
+		assertType("'123'", $container->getParameter('app.int_as_string'));
+		assertType("'123'", $parameterBag->get('app.int_as_string'));
+		assertType("'123'", $this->getParameter('app.int_as_string'));
+		assertType('123.45', $container->getParameter('app.float'));
+		assertType('123.45', $parameterBag->get('app.float'));
+		assertType('123.45', $this->getParameter('app.float'));
+		assertType("'123.45'", $container->getParameter('app.float_as_string'));
+		assertType("'123.45'", $parameterBag->get('app.float_as_string'));
+		assertType("'123.45'", $this->getParameter('app.float_as_string'));
+		assertType('true', $container->getParameter('app.boolean'));
+		assertType('true', $parameterBag->get('app.boolean'));
+		assertType('true', $this->getParameter('app.boolean'));
+		assertType("'true'", $container->getParameter('app.boolean_as_string'));
+		assertType("'true'", $parameterBag->get('app.boolean_as_string'));
+		assertType("'true'", $this->getParameter('app.boolean_as_string'));
+		assertType("array('en', 'es', 'fr')", $container->getParameter('app.list'));
+		assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list'));
+		assertType("array('en', 'es', 'fr')", $this->getParameter('app.list'));
+		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list'));
+		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list'));
+		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list'));
+		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map'));
+		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map'));
+		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map'));
+		assertType("'This is a Bell char '", $container->getParameter('app.binary'));
+		assertType("'This is a Bell char '", $parameterBag->get('app.binary'));
+		assertType("'This is a Bell char '", $this->getParameter('app.binary'));
+		assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant'));
+		assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant'));
+		assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant'));
+
+		assertType('false', $container->hasParameter('unknown'));
+		assertType('false', $parameterBag->has('unknown'));
+		assertType('true', $container->hasParameter('app.string'));
+		assertType('true', $parameterBag->has('app.string'));
+		assertType('true', $container->hasParameter('app.int'));
+		assertType('true', $parameterBag->has('app.int'));
+		assertType('true', $container->hasParameter('app.int_as_string'));
+		assertType('true', $parameterBag->has('app.int_as_string'));
+		assertType('true', $container->hasParameter('app.float'));
+		assertType('true', $parameterBag->has('app.float'));
+		assertType('true', $container->hasParameter('app.float_as_string'));
+		assertType('true', $parameterBag->has('app.float_as_string'));
+		assertType('true', $container->hasParameter('app.boolean'));
+		assertType('true', $parameterBag->has('app.boolean'));
+		assertType('true', $container->hasParameter('app.boolean_as_string'));
+		assertType('true', $parameterBag->has('app.boolean_as_string'));
+		assertType('true', $container->hasParameter('app.list'));
+		assertType('true', $parameterBag->has('app.list'));
+		assertType('true', $container->hasParameter('app.list_of_list'));
+		assertType('true', $parameterBag->has('app.list_of_list'));
+		assertType('true', $container->hasParameter('app.map'));
+		assertType('true', $parameterBag->has('app.map'));
+		assertType('true', $container->hasParameter('app.binary'));
+		assertType('true', $parameterBag->has('app.binary'));
+		assertType('true', $container->hasParameter('app.constant'));
+		assertType('true', $parameterBag->has('app.constant'));
+	}
+
+}
diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
new file mode 100644
index 00000000..28773829
--- /dev/null
+++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
@@ -0,0 +1,95 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
+
+final class ExampleAbstractControllerWithoutContainer extends AbstractController
+{
+
+	public function services(): void
+	{
+		assertType('object', $this->get('foo'));
+		assertType('object', $this->get('bar'));
+		assertType('object', $this->get(doFoo()));
+		assertType('object', $this->get());
+
+		assertType('bool', $this->has('foo'));
+		assertType('bool', $this->has('bar'));
+		assertType('bool', $this->has(doFoo()));
+		assertType('bool', $this->has());
+	}
+
+	public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void
+	{
+		assertType('array|bool|float|int|string|null', $container->getParameter('unknown'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('unknown'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('unknown'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.int'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.int'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.float'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.float'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.list'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.list'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.map'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.map'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.map'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.binary'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.binary'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.binary'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.constant'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.constant'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.constant'));
+
+		assertType('bool', $container->hasParameter('unknown'));
+		assertType('bool', $parameterBag->has('unknown'));
+		assertType('bool', $container->hasParameter('app.string'));
+		assertType('bool', $parameterBag->has('app.string'));
+		assertType('bool', $container->hasParameter('app.int'));
+		assertType('bool', $parameterBag->has('app.int'));
+		assertType('bool', $container->hasParameter('app.int_as_string'));
+		assertType('bool', $parameterBag->has('app.int_as_string'));
+		assertType('bool', $container->hasParameter('app.float'));
+		assertType('bool', $parameterBag->has('app.float'));
+		assertType('bool', $container->hasParameter('app.float_as_string'));
+		assertType('bool', $parameterBag->has('app.float_as_string'));
+		assertType('bool', $container->hasParameter('app.boolean'));
+		assertType('bool', $parameterBag->has('app.boolean'));
+		assertType('bool', $container->hasParameter('app.boolean_as_string'));
+		assertType('bool', $parameterBag->has('app.boolean_as_string'));
+		assertType('bool', $container->hasParameter('app.list'));
+		assertType('bool', $parameterBag->has('app.list'));
+		assertType('bool', $container->hasParameter('app.list_of_list'));
+		assertType('bool', $parameterBag->has('app.list_of_list'));
+		assertType('bool', $container->hasParameter('app.map'));
+		assertType('bool', $parameterBag->has('app.map'));
+		assertType('bool', $container->hasParameter('app.binary'));
+		assertType('bool', $parameterBag->has('app.binary'));
+		assertType('bool', $container->hasParameter('app.constant'));
+		assertType('bool', $parameterBag->has('app.constant'));
+	}
+
+}
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index ce22ac07..db761227 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -3,6 +3,8 @@
 namespace PHPStan\Type\Symfony;
 
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
 use function PHPStan\Testing\assertType;
 
 final class ExampleController extends Controller
@@ -21,4 +23,74 @@ public function services(): void
 		assertType('bool', $this->has());
 	}
 
+	public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void
+	{
+		assertType('array|bool|float|int|string|null', $container->getParameter('unknown'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('unknown'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('unknown'));
+		assertType("'abcdef'", $container->getParameter('app.string'));
+		assertType("'abcdef'", $parameterBag->get('app.string'));
+		assertType("'abcdef'", $this->getParameter('app.string'));
+		assertType('123', $container->getParameter('app.int'));
+		assertType('123', $parameterBag->get('app.int'));
+		assertType('123', $this->getParameter('app.int'));
+		assertType("'123'", $container->getParameter('app.int_as_string'));
+		assertType("'123'", $parameterBag->get('app.int_as_string'));
+		assertType("'123'", $this->getParameter('app.int_as_string'));
+		assertType('123.45', $container->getParameter('app.float'));
+		assertType('123.45', $parameterBag->get('app.float'));
+		assertType('123.45', $this->getParameter('app.float'));
+		assertType("'123.45'", $container->getParameter('app.float_as_string'));
+		assertType("'123.45'", $parameterBag->get('app.float_as_string'));
+		assertType("'123.45'", $this->getParameter('app.float_as_string'));
+		assertType('true', $container->getParameter('app.boolean'));
+		assertType('true', $parameterBag->get('app.boolean'));
+		assertType('true', $this->getParameter('app.boolean'));
+		assertType("'true'", $container->getParameter('app.boolean_as_string'));
+		assertType("'true'", $parameterBag->get('app.boolean_as_string'));
+		assertType("'true'", $this->getParameter('app.boolean_as_string'));
+		assertType("array('en', 'es', 'fr')", $container->getParameter('app.list'));
+		assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list'));
+		assertType("array('en', 'es', 'fr')", $this->getParameter('app.list'));
+		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list'));
+		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list'));
+		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list'));
+		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map'));
+		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map'));
+		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map'));
+		assertType("'This is a Bell char '", $container->getParameter('app.binary'));
+		assertType("'This is a Bell char '", $parameterBag->get('app.binary'));
+		assertType("'This is a Bell char '", $this->getParameter('app.binary'));
+		assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant'));
+		assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant'));
+		assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant'));
+
+		assertType('false', $container->hasParameter('unknown'));
+		assertType('false', $parameterBag->has('unknown'));
+		assertType('true', $container->hasParameter('app.string'));
+		assertType('true', $parameterBag->has('app.string'));
+		assertType('true', $container->hasParameter('app.int'));
+		assertType('true', $parameterBag->has('app.int'));
+		assertType('true', $container->hasParameter('app.int_as_string'));
+		assertType('true', $parameterBag->has('app.int_as_string'));
+		assertType('true', $container->hasParameter('app.float'));
+		assertType('true', $parameterBag->has('app.float'));
+		assertType('true', $container->hasParameter('app.float_as_string'));
+		assertType('true', $parameterBag->has('app.float_as_string'));
+		assertType('true', $container->hasParameter('app.boolean'));
+		assertType('true', $parameterBag->has('app.boolean'));
+		assertType('true', $container->hasParameter('app.boolean_as_string'));
+		assertType('true', $parameterBag->has('app.boolean_as_string'));
+		assertType('true', $container->hasParameter('app.list'));
+		assertType('true', $parameterBag->has('app.list'));
+		assertType('true', $container->hasParameter('app.list_of_list'));
+		assertType('true', $parameterBag->has('app.list_of_list'));
+		assertType('true', $container->hasParameter('app.map'));
+		assertType('true', $parameterBag->has('app.map'));
+		assertType('true', $container->hasParameter('app.binary'));
+		assertType('true', $parameterBag->has('app.binary'));
+		assertType('true', $container->hasParameter('app.constant'));
+		assertType('true', $parameterBag->has('app.constant'));
+	}
+
 }
diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
index f127d976..2adfd7d5 100644
--- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
+++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
@@ -3,6 +3,8 @@
 namespace PHPStan\Type\Symfony;
 
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
 
 final class ExampleControllerWithoutContainer extends Controller
 {
@@ -20,4 +22,74 @@ public function services(): void
 		assertType('bool', $this->has());
 	}
 
+	public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void
+	{
+		assertType('array|bool|float|int|string|null', $container->getParameter('unknown'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('unknown'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('unknown'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.int'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.int'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.float'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.float'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.list'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.list'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.map'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.map'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.map'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.binary'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.binary'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.binary'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.constant'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.constant'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.constant'));
+
+		assertType('bool', $container->hasParameter('unknown'));
+		assertType('bool', $parameterBag->has('unknown'));
+		assertType('bool', $container->hasParameter('app.string'));
+		assertType('bool', $parameterBag->has('app.string'));
+		assertType('bool', $container->hasParameter('app.int'));
+		assertType('bool', $parameterBag->has('app.int'));
+		assertType('bool', $container->hasParameter('app.int_as_string'));
+		assertType('bool', $parameterBag->has('app.int_as_string'));
+		assertType('bool', $container->hasParameter('app.float'));
+		assertType('bool', $parameterBag->has('app.float'));
+		assertType('bool', $container->hasParameter('app.float_as_string'));
+		assertType('bool', $parameterBag->has('app.float_as_string'));
+		assertType('bool', $container->hasParameter('app.boolean'));
+		assertType('bool', $parameterBag->has('app.boolean'));
+		assertType('bool', $container->hasParameter('app.boolean_as_string'));
+		assertType('bool', $parameterBag->has('app.boolean_as_string'));
+		assertType('bool', $container->hasParameter('app.list'));
+		assertType('bool', $parameterBag->has('app.list'));
+		assertType('bool', $container->hasParameter('app.list_of_list'));
+		assertType('bool', $parameterBag->has('app.list_of_list'));
+		assertType('bool', $container->hasParameter('app.map'));
+		assertType('bool', $parameterBag->has('app.map'));
+		assertType('bool', $container->hasParameter('app.binary'));
+		assertType('bool', $parameterBag->has('app.binary'));
+		assertType('bool', $container->hasParameter('app.constant'));
+		assertType('bool', $parameterBag->has('app.constant'));
+	}
+
 }

From af91203fa081c46540296b6df827fa6f79b11043 Mon Sep 17 00:00:00 2001
From: Alexander Schranz <alexander@sulu.io>
Date: Mon, 3 May 2021 11:16:42 +0200
Subject: [PATCH 083/297] Change parameterbag scalar type inference to
 generalized type

---
 .../ParameterDynamicReturnTypeExtension.php   |  3 +-
 .../data/ExampleAbstractController.php        | 72 +++++++++----------
 tests/Type/Symfony/data/ExampleController.php | 72 +++++++++----------
 3 files changed, 74 insertions(+), 73 deletions(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index adce58fb..071020c8 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -18,6 +18,7 @@
 use PHPStan\Type\NullType;
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
+use PHPStan\Type\TypeUtils;
 use PHPStan\Type\UnionType;
 use function in_array;
 
@@ -97,7 +98,7 @@ private function getGetTypeFromMethodCall(
 		if ($parameterKey !== null) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
 			if ($parameter !== null) {
-				return $scope->getTypeFromValue($parameter->getValue());
+				return TypeUtils::generalizeType($scope->getTypeFromValue($parameter->getValue()));
 			}
 		}
 
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index 2bff7543..4b910774 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -28,42 +28,42 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('array|bool|float|int|string|null', $container->getParameter('unknown'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('unknown'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('unknown'));
-		assertType("'abcdef'", $container->getParameter('app.string'));
-		assertType("'abcdef'", $parameterBag->get('app.string'));
-		assertType("'abcdef'", $this->getParameter('app.string'));
-		assertType('123', $container->getParameter('app.int'));
-		assertType('123', $parameterBag->get('app.int'));
-		assertType('123', $this->getParameter('app.int'));
-		assertType("'123'", $container->getParameter('app.int_as_string'));
-		assertType("'123'", $parameterBag->get('app.int_as_string'));
-		assertType("'123'", $this->getParameter('app.int_as_string'));
-		assertType('123.45', $container->getParameter('app.float'));
-		assertType('123.45', $parameterBag->get('app.float'));
-		assertType('123.45', $this->getParameter('app.float'));
-		assertType("'123.45'", $container->getParameter('app.float_as_string'));
-		assertType("'123.45'", $parameterBag->get('app.float_as_string'));
-		assertType("'123.45'", $this->getParameter('app.float_as_string'));
-		assertType('true', $container->getParameter('app.boolean'));
-		assertType('true', $parameterBag->get('app.boolean'));
-		assertType('true', $this->getParameter('app.boolean'));
-		assertType("'true'", $container->getParameter('app.boolean_as_string'));
-		assertType("'true'", $parameterBag->get('app.boolean_as_string'));
-		assertType("'true'", $this->getParameter('app.boolean_as_string'));
-		assertType("array('en', 'es', 'fr')", $container->getParameter('app.list'));
-		assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list'));
-		assertType("array('en', 'es', 'fr')", $this->getParameter('app.list'));
-		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list'));
-		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list'));
-		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list'));
-		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map'));
-		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map'));
-		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map'));
-		assertType("'This is a Bell char '", $container->getParameter('app.binary'));
-		assertType("'This is a Bell char '", $parameterBag->get('app.binary'));
-		assertType("'This is a Bell char '", $this->getParameter('app.binary'));
-		assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant'));
-		assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant'));
-		assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant'));
+		assertType("string", $container->getParameter('app.string'));
+		assertType("string", $parameterBag->get('app.string'));
+		assertType("string", $this->getParameter('app.string'));
+		assertType('int', $container->getParameter('app.int'));
+		assertType('int', $parameterBag->get('app.int'));
+		assertType('int', $this->getParameter('app.int'));
+		assertType("string", $container->getParameter('app.int_as_string'));
+		assertType("string", $parameterBag->get('app.int_as_string'));
+		assertType("string", $this->getParameter('app.int_as_string'));
+		assertType('float', $container->getParameter('app.float'));
+		assertType('float', $parameterBag->get('app.float'));
+		assertType('float', $this->getParameter('app.float'));
+		assertType("string", $container->getParameter('app.float_as_string'));
+		assertType("string", $parameterBag->get('app.float_as_string'));
+		assertType("string", $this->getParameter('app.float_as_string'));
+		assertType('bool', $container->getParameter('app.boolean'));
+		assertType('bool', $parameterBag->get('app.boolean'));
+		assertType('bool', $this->getParameter('app.boolean'));
+		assertType("string", $container->getParameter('app.boolean_as_string'));
+		assertType("string", $parameterBag->get('app.boolean_as_string'));
+		assertType("string", $this->getParameter('app.boolean_as_string'));
+		assertType("array<int, string>&nonEmpty", $container->getParameter('app.list'));
+		assertType("array<int, string>&nonEmpty", $parameterBag->get('app.list'));
+		assertType("array<int, string>&nonEmpty", $this->getParameter('app.list'));
+		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $container->getParameter('app.list_of_list'));
+		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $parameterBag->get('app.list_of_list'));
+		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $this->getParameter('app.list_of_list'));
+		assertType("array<string, string>&nonEmpty", $container->getParameter('app.map'));
+		assertType("array<string, string>&nonEmpty", $parameterBag->get('app.map'));
+		assertType("array<string, string>&nonEmpty", $this->getParameter('app.map'));
+		assertType("string", $container->getParameter('app.binary'));
+		assertType("string", $parameterBag->get('app.binary'));
+		assertType("string", $this->getParameter('app.binary'));
+		assertType("string", $container->getParameter('app.constant'));
+		assertType("string", $parameterBag->get('app.constant'));
+		assertType("string", $this->getParameter('app.constant'));
 
 		assertType('false', $container->hasParameter('unknown'));
 		assertType('false', $parameterBag->has('unknown'));
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index db761227..1d8edf48 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -28,42 +28,42 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('array|bool|float|int|string|null', $container->getParameter('unknown'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('unknown'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('unknown'));
-		assertType("'abcdef'", $container->getParameter('app.string'));
-		assertType("'abcdef'", $parameterBag->get('app.string'));
-		assertType("'abcdef'", $this->getParameter('app.string'));
-		assertType('123', $container->getParameter('app.int'));
-		assertType('123', $parameterBag->get('app.int'));
-		assertType('123', $this->getParameter('app.int'));
-		assertType("'123'", $container->getParameter('app.int_as_string'));
-		assertType("'123'", $parameterBag->get('app.int_as_string'));
-		assertType("'123'", $this->getParameter('app.int_as_string'));
-		assertType('123.45', $container->getParameter('app.float'));
-		assertType('123.45', $parameterBag->get('app.float'));
-		assertType('123.45', $this->getParameter('app.float'));
-		assertType("'123.45'", $container->getParameter('app.float_as_string'));
-		assertType("'123.45'", $parameterBag->get('app.float_as_string'));
-		assertType("'123.45'", $this->getParameter('app.float_as_string'));
-		assertType('true', $container->getParameter('app.boolean'));
-		assertType('true', $parameterBag->get('app.boolean'));
-		assertType('true', $this->getParameter('app.boolean'));
-		assertType("'true'", $container->getParameter('app.boolean_as_string'));
-		assertType("'true'", $parameterBag->get('app.boolean_as_string'));
-		assertType("'true'", $this->getParameter('app.boolean_as_string'));
-		assertType("array('en', 'es', 'fr')", $container->getParameter('app.list'));
-		assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list'));
-		assertType("array('en', 'es', 'fr')", $this->getParameter('app.list'));
-		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list'));
-		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list'));
-		assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list'));
-		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map'));
-		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map'));
-		assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map'));
-		assertType("'This is a Bell char '", $container->getParameter('app.binary'));
-		assertType("'This is a Bell char '", $parameterBag->get('app.binary'));
-		assertType("'This is a Bell char '", $this->getParameter('app.binary'));
-		assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant'));
-		assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant'));
-		assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant'));
+		assertType("string", $container->getParameter('app.string'));
+		assertType("string", $parameterBag->get('app.string'));
+		assertType("string", $this->getParameter('app.string'));
+		assertType('int', $container->getParameter('app.int'));
+		assertType('int', $parameterBag->get('app.int'));
+		assertType('int', $this->getParameter('app.int'));
+		assertType("string", $container->getParameter('app.int_as_string'));
+		assertType("string", $parameterBag->get('app.int_as_string'));
+		assertType("string", $this->getParameter('app.int_as_string'));
+		assertType('float', $container->getParameter('app.float'));
+		assertType('float', $parameterBag->get('app.float'));
+		assertType('float', $this->getParameter('app.float'));
+		assertType("string", $container->getParameter('app.float_as_string'));
+		assertType("string", $parameterBag->get('app.float_as_string'));
+		assertType("string", $this->getParameter('app.float_as_string'));
+		assertType('bool', $container->getParameter('app.boolean'));
+		assertType('bool', $parameterBag->get('app.boolean'));
+		assertType('bool', $this->getParameter('app.boolean'));
+		assertType("string", $container->getParameter('app.boolean_as_string'));
+		assertType("string", $parameterBag->get('app.boolean_as_string'));
+		assertType("string", $this->getParameter('app.boolean_as_string'));
+		assertType("array<int, string>&nonEmpty", $container->getParameter('app.list'));
+		assertType("array<int, string>&nonEmpty", $parameterBag->get('app.list'));
+		assertType("array<int, string>&nonEmpty", $this->getParameter('app.list'));
+		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $container->getParameter('app.list_of_list'));
+		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $parameterBag->get('app.list_of_list'));
+		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $this->getParameter('app.list_of_list'));
+		assertType("array<string, string>&nonEmpty", $container->getParameter('app.map'));
+		assertType("array<string, string>&nonEmpty", $parameterBag->get('app.map'));
+		assertType("array<string, string>&nonEmpty", $this->getParameter('app.map'));
+		assertType("string", $container->getParameter('app.binary'));
+		assertType("string", $parameterBag->get('app.binary'));
+		assertType("string", $this->getParameter('app.binary'));
+		assertType("string", $container->getParameter('app.constant'));
+		assertType("string", $parameterBag->get('app.constant'));
+		assertType("string", $this->getParameter('app.constant'));
 
 		assertType('false', $container->hasParameter('unknown'));
 		assertType('false', $parameterBag->has('unknown'));

From ebf89e81c60e3c7a0417fa5166fd6da50139dde5 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 4 May 2021 17:58:38 +0200
Subject: [PATCH 084/297] Generalize empty array parameters too

---
 .../ParameterDynamicReturnTypeExtension.php   | 19 +++++++++++++++++--
 tests/Type/Symfony/container.xml              |  1 +
 .../data/ExampleAbstractController.php        |  1 +
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 071020c8..8fc6f387 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -10,6 +10,7 @@
 use PHPStan\Symfony\ParameterMap;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\BooleanType;
+use PHPStan\Type\Constant\ConstantArrayType;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FloatType;
@@ -18,7 +19,7 @@
 use PHPStan\Type\NullType;
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
-use PHPStan\Type\TypeUtils;
+use PHPStan\Type\TypeTraverser;
 use PHPStan\Type\UnionType;
 use function in_array;
 
@@ -98,7 +99,21 @@ private function getGetTypeFromMethodCall(
 		if ($parameterKey !== null) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
 			if ($parameter !== null) {
-				return TypeUtils::generalizeType($scope->getTypeFromValue($parameter->getValue()));
+				return TypeTraverser::map($scope->getTypeFromValue($parameter->getValue()), static function (\PHPStan\Type\Type $type, callable $traverse): Type {
+					if ($type instanceof \PHPStan\Type\ConstantType) {
+						$generalized = $type->generalize();
+						if ($generalized instanceof ConstantArrayType) {
+							if (count($generalized->getValueTypes()) !== 0) {
+								throw new \PHPStan\ShouldNotHappenException();
+							}
+
+							return new ArrayType(new MixedType(), new MixedType());
+						}
+
+						return $generalized;
+					}
+					return $traverse($type);
+				});
 			}
 		}
 
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index af82fd19..d794479e 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -30,6 +30,7 @@
         </parameter>
         <parameter key="app.binary" type="binary">VGhpcyBpcyBhIEJlbGwgY2hhciAH</parameter>
         <parameter key="app.constant">Y-m-d\TH:i:sP</parameter>
+		<parameter key="test_collection" type="collection"/>
     </parameters>
 
     <services>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index 4b910774..635e0f33 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -64,6 +64,7 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("string", $container->getParameter('app.constant'));
 		assertType("string", $parameterBag->get('app.constant'));
 		assertType("string", $this->getParameter('app.constant'));
+		assertType("array", $this->getParameter('test_collection'));
 
 		assertType('false', $container->hasParameter('unknown'));
 		assertType('false', $parameterBag->has('unknown'));

From bb80b1be3debd0ea1bd1486676cfc568155dc0b1 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 4 May 2021 19:27:05 +0200
Subject: [PATCH 085/297] Non-empty parameter array might be empty in different
 environment

---
 .../ParameterDynamicReturnTypeExtension.php   | 33 ++++++++++---------
 tests/Type/Symfony/container.xml              |  3 ++
 .../data/ExampleAbstractController.php        | 19 ++++++-----
 3 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 8fc6f387..da7a489a 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -12,6 +12,7 @@
 use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantArrayType;
 use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\ConstantType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FloatType;
 use PHPStan\Type\IntegerType;
@@ -99,27 +100,29 @@ private function getGetTypeFromMethodCall(
 		if ($parameterKey !== null) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
 			if ($parameter !== null) {
-				return TypeTraverser::map($scope->getTypeFromValue($parameter->getValue()), static function (\PHPStan\Type\Type $type, callable $traverse): Type {
-					if ($type instanceof \PHPStan\Type\ConstantType) {
-						$generalized = $type->generalize();
-						if ($generalized instanceof ConstantArrayType) {
-							if (count($generalized->getValueTypes()) !== 0) {
-								throw new \PHPStan\ShouldNotHappenException();
-							}
-
-							return new ArrayType(new MixedType(), new MixedType());
-						}
-
-						return $generalized;
-					}
-					return $traverse($type);
-				});
+				return $this->generalizeType($scope->getTypeFromValue($parameter->getValue()));
 			}
 		}
 
 		return $returnType;
 	}
 
+	private function generalizeType(Type $type): Type
+	{
+		return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
+			if ($type instanceof ConstantArrayType) {
+				if (count($type->getValueTypes()) === 0) {
+					return new ArrayType(new MixedType(), new MixedType());
+				}
+				return new ArrayType($this->generalizeType($type->getKeyType()), $this->generalizeType($type->getItemType()));
+			}
+			if ($type instanceof ConstantType) {
+				return $type->generalize();
+			}
+			return $traverse($type);
+		});
+	}
+
 	private function getHasTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index d794479e..25966148 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -31,6 +31,9 @@
         <parameter key="app.binary" type="binary">VGhpcyBpcyBhIEJlbGwgY2hhciAH</parameter>
         <parameter key="app.constant">Y-m-d\TH:i:sP</parameter>
 		<parameter key="test_collection" type="collection"/>
+		<parameter key="non_empty_collection" type="collection">
+			<parameter>value</parameter>
+		</parameter>
     </parameters>
 
     <services>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index 635e0f33..ffb4e33f 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -49,15 +49,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("string", $container->getParameter('app.boolean_as_string'));
 		assertType("string", $parameterBag->get('app.boolean_as_string'));
 		assertType("string", $this->getParameter('app.boolean_as_string'));
-		assertType("array<int, string>&nonEmpty", $container->getParameter('app.list'));
-		assertType("array<int, string>&nonEmpty", $parameterBag->get('app.list'));
-		assertType("array<int, string>&nonEmpty", $this->getParameter('app.list'));
-		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $container->getParameter('app.list_of_list'));
-		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $parameterBag->get('app.list_of_list'));
-		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $this->getParameter('app.list_of_list'));
-		assertType("array<string, string>&nonEmpty", $container->getParameter('app.map'));
-		assertType("array<string, string>&nonEmpty", $parameterBag->get('app.map'));
-		assertType("array<string, string>&nonEmpty", $this->getParameter('app.map'));
+		assertType("array<int, string>", $container->getParameter('app.list'));
+		assertType("array<int, string>", $parameterBag->get('app.list'));
+		assertType("array<int, string>", $this->getParameter('app.list'));
+		assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
+		assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
+		assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
+		assertType("array<string, string>", $container->getParameter('app.map'));
+		assertType("array<string, string>", $parameterBag->get('app.map'));
+		assertType("array<string, string>", $this->getParameter('app.map'));
 		assertType("string", $container->getParameter('app.binary'));
 		assertType("string", $parameterBag->get('app.binary'));
 		assertType("string", $this->getParameter('app.binary'));
@@ -65,6 +65,7 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("string", $parameterBag->get('app.constant'));
 		assertType("string", $this->getParameter('app.constant'));
 		assertType("array", $this->getParameter('test_collection'));
+		assertType("array<int, string>", $this->getParameter('non_empty_collection'));
 
 		assertType('false', $container->hasParameter('unknown'));
 		assertType('false', $parameterBag->has('unknown'));

From 34f16fa4caf8eee7e2a776b376cc48208cd37fc8 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 4 May 2021 19:52:07 +0200
Subject: [PATCH 086/297] Fix tests

---
 tests/Type/Symfony/data/ExampleController.php | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index 1d8edf48..576f8369 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -49,15 +49,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("string", $container->getParameter('app.boolean_as_string'));
 		assertType("string", $parameterBag->get('app.boolean_as_string'));
 		assertType("string", $this->getParameter('app.boolean_as_string'));
-		assertType("array<int, string>&nonEmpty", $container->getParameter('app.list'));
-		assertType("array<int, string>&nonEmpty", $parameterBag->get('app.list'));
-		assertType("array<int, string>&nonEmpty", $this->getParameter('app.list'));
-		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $container->getParameter('app.list_of_list'));
-		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $parameterBag->get('app.list_of_list'));
-		assertType("array<int, array<string, string>&nonEmpty>&nonEmpty", $this->getParameter('app.list_of_list'));
-		assertType("array<string, string>&nonEmpty", $container->getParameter('app.map'));
-		assertType("array<string, string>&nonEmpty", $parameterBag->get('app.map'));
-		assertType("array<string, string>&nonEmpty", $this->getParameter('app.map'));
+		assertType("array<int, string>", $container->getParameter('app.list'));
+		assertType("array<int, string>", $parameterBag->get('app.list'));
+		assertType("array<int, string>", $this->getParameter('app.list'));
+		assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
+		assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
+		assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
+		assertType("array<string, string>", $container->getParameter('app.map'));
+		assertType("array<string, string>", $parameterBag->get('app.map'));
+		assertType("array<string, string>", $this->getParameter('app.map'));
 		assertType("string", $container->getParameter('app.binary'));
 		assertType("string", $parameterBag->get('app.binary'));
 		assertType("string", $this->getParameter('app.binary'));

From 7f47026c69c9f2ef8d6b1814bd17cc86ea768e9d Mon Sep 17 00:00:00 2001
From: Shyim <6224096+shyim@users.noreply.github.com>
Date: Wed, 5 May 2021 14:52:06 +0000
Subject: [PATCH 087/297] Fix synthetic service return type when class is
 available

---
 src/Type/Symfony/ServiceDynamicReturnTypeExtension.php          | 2 +-
 tests/Type/Symfony/container.xml                                | 1 +
 tests/Type/Symfony/data/ExampleAbstractController.php           | 2 ++
 .../Symfony/data/ExampleAbstractControllerWithoutContainer.php  | 2 ++
 tests/Type/Symfony/data/ExampleController.php                   | 2 ++
 tests/Type/Symfony/data/ExampleControllerWithoutContainer.php   | 2 ++
 6 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index 6b2f6f84..9d028c35 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -68,7 +68,7 @@ private function getGetTypeFromMethodCall(
 		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
-			if ($service !== null && !$service->isSynthetic()) {
+			if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) {
 				return new ObjectType($service->getClass() ?? $serviceId);
 			}
 		}
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index 25966148..5bedede2 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -38,5 +38,6 @@
 
     <services>
         <service id="foo" class="Foo"></service>
+        <service id="synthetic" class="Synthetic" synthetic="true" />
     </services>
 </container>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index ffb4e33f..4dbaffa7 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -13,11 +13,13 @@ final class ExampleAbstractController extends AbstractController
 	public function services(): void
 	{
 		assertType('Foo', $this->get('foo'));
+		assertType('Synthetic', $this->get('synthetic'));
 		assertType('object', $this->get('bar'));
 		assertType('object', $this->get(doFoo()));
 		assertType('object', $this->get());
 
 		assertType('true', $this->has('foo'));
+		assertType('true', $this->has('synthetic'));
 		assertType('false', $this->has('bar'));
 		assertType('bool', $this->has(doFoo()));
 		assertType('bool', $this->has());
diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
index 28773829..7ca06775 100644
--- a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
+++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
@@ -12,11 +12,13 @@ final class ExampleAbstractControllerWithoutContainer extends AbstractController
 	public function services(): void
 	{
 		assertType('object', $this->get('foo'));
+		assertType('object', $this->get('synthetic'));
 		assertType('object', $this->get('bar'));
 		assertType('object', $this->get(doFoo()));
 		assertType('object', $this->get());
 
 		assertType('bool', $this->has('foo'));
+		assertType('bool', $this->has('synthetic'));
 		assertType('bool', $this->has('bar'));
 		assertType('bool', $this->has(doFoo()));
 		assertType('bool', $this->has());
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index 576f8369..1fe8c983 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -13,11 +13,13 @@ final class ExampleController extends Controller
 	public function services(): void
 	{
 		assertType('Foo', $this->get('foo'));
+		assertType('Synthetic', $this->get('synthetic'));
 		assertType('object', $this->get('bar'));
 		assertType('object', $this->get(doFoo()));
 		assertType('object', $this->get());
 
 		assertType('true', $this->has('foo'));
+		assertType('true', $this->has('synthetic'));
 		assertType('false', $this->has('bar'));
 		assertType('bool', $this->has(doFoo()));
 		assertType('bool', $this->has());
diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
index 2adfd7d5..2e9b7ba2 100644
--- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
+++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
@@ -12,11 +12,13 @@ final class ExampleControllerWithoutContainer extends Controller
 	public function services(): void
 	{
 		assertType('object', $this->get('foo'));
+		assertType('object', $this->get('synthetic'));
 		assertType('object', $this->get('bar'));
 		assertType('object', $this->get(doFoo()));
 		assertType('object', $this->get());
 
 		assertType('bool', $this->has('foo'));
+		assertType('bool', $this->has('synthetic'));
 		assertType('bool', $this->has('bar'));
 		assertType('bool', $this->has(doFoo()));
 		assertType('bool', $this->has());

From 46feb532aa4d35ba461a4ccccc378d6d28b91745 Mon Sep 17 00:00:00 2001
From: Hugo Alliaume <hugo@alliau.me>
Date: Fri, 14 May 2021 09:30:21 +0200
Subject: [PATCH 088/297] Drop Phing for Makefile, migrate PHPUnit config,
 update PHPCS config (#160)

---
 .gitattributes              |   4 +-
 .github/workflows/build.yml |  10 ++--
 .gitignore                  |   1 +
 Makefile                    |  23 ++++++++
 build.xml                   | 108 ------------------------------------
 composer.json               |   5 +-
 phpcs.xml                   |   8 +++
 phpunit.xml                 |  36 ++++++++++++
 tests/phpunit.xml           |  27 ---------
 tmp/.gitignore              |   3 +
 tmp/cache/.gitignore        |   2 +
 11 files changed, 83 insertions(+), 144 deletions(-)
 create mode 100644 Makefile
 delete mode 100644 build.xml
 create mode 100644 phpunit.xml
 delete mode 100644 tests/phpunit.xml
 create mode 100644 tmp/.gitignore
 create mode 100644 tmp/cache/.gitignore

diff --git a/.gitattributes b/.gitattributes
index 823b5a81..17405d6e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,10 +1,12 @@
 *.php text eol=lf
 
 tests export-ignore
+tmp export-ignore
 .coveralls.yml export-ignore
 .gitattributes export-ignore
 .gitignore export-ignore
 .travis.yml export-ignore
-build.xml export-ignore
+Makefile export-ignore
 phpcs.xml export-ignore
 phpstan.neon export-ignore
+phpunit.xml export-ignore
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fc0ee8f4..18898d7a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -44,7 +44,7 @@ jobs:
 
 
       - name: "Lint"
-        run: "vendor/bin/phing lint"
+        run: "make lint"
 
   coding-standards:
     name: "Coding Standard"
@@ -68,10 +68,10 @@ jobs:
         run: "composer install --no-interaction --no-progress --no-suggest"
 
       - name: "Lint"
-        run: "vendor/bin/phing lint"
+        run: "make lint"
 
       - name: "Coding Standard"
-        run: "vendor/bin/phing cs"
+        run: "make cs"
 
   tests:
     name: "Tests"
@@ -116,7 +116,7 @@ jobs:
         run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
 
       - name: "Tests"
-        run: "vendor/bin/phing tests"
+        run: "make tests"
 
   static-analysis:
     name: "PHPStan"
@@ -160,4 +160,4 @@ jobs:
         run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
 
       - name: "PHPStan"
-        run: "vendor/bin/phing phpstan"
+        run: "make phpstan"
diff --git a/.gitignore b/.gitignore
index ca398d28..d6a83e59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 /tests/tmp
 /vendor
 composer.lock
+.phpunit.result.cache
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..fe917d3b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+.PHONY: check
+check: lint cs tests phpstan
+
+.PHONY: tests
+tests:
+	php vendor/bin/phpunit
+
+.PHONY: lint
+lint:
+	php vendor/bin/parallel-lint --colors \
+		src tests
+
+.PHONY: cs
+cs:
+	composer install --working-dir build-cs && php build-cs/vendor/bin/phpcs
+
+.PHONY: cs-fix
+cs-fix:
+	php build-cs/vendor/bin/phpcbf
+
+.PHONY: phpstan
+phpstan:
+	php vendor/bin/phpstan analyse -l 8 -c phpstan.neon src tests
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 75b98df1..00000000
--- a/build.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<project name="PHPStan Symfony Framework extensions and rules" default="check">
-
-    <target name="check" depends="
-        composer,
-        lint,
-        cs,
-        tests,
-        phpstan
-    "/>
-
-    <target name="composer">
-        <exec
-                executable="composer"
-                logoutput="true"
-                passthru="true"
-                checkreturn="true"
-        >
-            <arg value="install"/>
-        </exec>
-    </target>
-
-    <target name="lint">
-        <exec
-                executable="vendor/bin/parallel-lint"
-                logoutput="true"
-                passthru="true"
-                checkreturn="true"
-        >
-            <arg path="src"/>
-            <arg path="tests"/>
-        </exec>
-    </target>
-
-    <target name="cs">
-		<exec
-				executable="composer"
-				logoutput="true"
-				passthru="true"
-				checkreturn="true"
-		>
-			<arg value="install"/>
-			<arg value="--working-dir"/>
-			<arg path="build-cs"/>
-			<arg value="--ansi"/>
-		</exec>
-        <exec
-                executable="build-cs/vendor/bin/phpcs"
-                logoutput="true"
-                passthru="true"
-                checkreturn="true"
-        >
-            <arg value="--extensions=php"/>
-            <arg value="--encoding=utf-8"/>
-            <arg value="--tab-width=4"/>
-            <arg value="-sp"/>
-            <arg path="src"/>
-            <arg path="tests"/>
-        </exec>
-    </target>
-
-    <target name="cs-fix">
-        <exec
-                executable="build-cs/vendor/bin/phpcbf"
-                logoutput="true"
-                passthru="true"
-                checkreturn="true"
-        >
-            <arg value="--extensions=php"/>
-            <arg value="--encoding=utf-8"/>
-            <arg value="--tab-width=4"/>
-            <arg value="-sp"/>
-            <arg path="src"/>
-            <arg path="tests"/>
-        </exec>
-    </target>
-
-    <target name="tests">
-        <exec
-                executable="vendor/bin/phpunit"
-                logoutput="true"
-                passthru="true"
-                checkreturn="true"
-        >
-            <arg value="-c"/>
-            <arg value="tests/phpunit.xml"/>
-            <arg path="tests"/>
-        </exec>
-    </target>
-
-    <target name="phpstan">
-        <exec
-                executable="vendor/bin/phpstan"
-                logoutput="true"
-                passthru="true"
-                checkreturn="true"
-        >
-            <arg value="analyse"/>
-            <arg value="-l"/>
-            <arg value="8"/>
-            <arg value="-c"/>
-            <arg path="phpstan.neon"/>
-            <arg path="src"/>
-            <arg path="tests"/>
-        </exec>
-    </target>
-
-</project>
diff --git a/composer.json b/composer.json
index 4e01cb6b..6372b6cc 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,6 @@
     "symfony/framework-bundle": "<3.0"
   },
   "require-dev": {
-    "phing/phing": "^2.16.3",
     "php-parallel-lint/php-parallel-lint": "^1.2",
     "phpstan/phpstan-phpunit": "^0.12.16",
     "phpstan/phpstan-strict-rules": "^0.12.5",
@@ -35,8 +34,8 @@
   },
   "config": {
     "platform": {
-	  "php": "7.4.6"
-	},
+      "php": "7.4.6"
+    },
     "sort-packages": true
   },
   "extra": {
diff --git a/phpcs.xml b/phpcs.xml
index f6ec19e6..2807efec 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,5 +1,13 @@
 <?xml version="1.0"?>
 <ruleset name="PHPStan PHPUnit extensions and rules">
+    <arg name="colors"/>
+    <arg name="extensions" value="php"/>
+    <arg name="encoding" value="utf-8"/>
+    <arg name="tab-width" value="4"/>
+    <arg name="cache" value="tmp/cache/phpcs"/>
+    <arg value="sp"/>
+    <file>src</file>
+    <file>tests</file>
     <rule ref="build-cs/vendor/consistence-community/coding-standard/Consistence/ruleset.xml">
         <exclude name="Squiz.Functions.GlobalFunction.Found"/>
         <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword"/>
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 00000000..f9f3afe2
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         bootstrap="tests/bootstrap.php"
+         colors="true"
+         backupGlobals="false"
+         backupStaticAttributes="false"
+         beStrictAboutChangesToGlobalState="true"
+         beStrictAboutOutputDuringTests="true"
+         beStrictAboutTestsThatDoNotTestAnything="true"
+         beStrictAboutTodoAnnotatedTests="true"
+         failOnRisky="true"
+         failOnWarning="true"
+         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
+>
+    <coverage>
+        <include>
+            <directory suffix=".php">./src</directory>
+        </include>
+        <report>
+            <clover outputFile="tests/tmp/clover.xml"/>
+            <text
+                outputFile="php://stdout"
+                showUncoveredFiles="true"
+                showOnlySummary="true"
+            />
+        </report>
+    </coverage>
+
+    <testsuites>
+        <testsuite name="PHPStan for Symfony">
+            <directory suffix="Test.php">tests</directory>
+        </testsuite>
+    </testsuites>
+
+    <logging/>
+</phpunit>
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
deleted file mode 100644
index 738b4ac6..00000000
--- a/tests/phpunit.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<phpunit
-        bootstrap="bootstrap.php"
-        colors="true"
-        backupGlobals="false"
-        backupStaticAttributes="false"
-        beStrictAboutChangesToGlobalState="true"
-        beStrictAboutOutputDuringTests="true"
-        beStrictAboutTestsThatDoNotTestAnything="true"
-        beStrictAboutTodoAnnotatedTests="true"
-        failOnRisky="true"
-        failOnWarning="true"
->
-    <filter>
-        <whitelist>
-            <directory suffix=".php">../src</directory>
-        </whitelist>
-    </filter>
-    <logging>
-        <log
-                type="coverage-text"
-                target="php://stdout"
-                showUncoveredFiles="true"
-                showOnlySummary="true"
-        />
-        <log type="coverage-clover" target="tmp/clover.xml"/>
-    </logging>
-</phpunit>
diff --git a/tmp/.gitignore b/tmp/.gitignore
new file mode 100644
index 00000000..37890cae
--- /dev/null
+++ b/tmp/.gitignore
@@ -0,0 +1,3 @@
+*
+!cache
+!.*
diff --git a/tmp/cache/.gitignore b/tmp/cache/.gitignore
new file mode 100644
index 00000000..125e3429
--- /dev/null
+++ b/tmp/cache/.gitignore
@@ -0,0 +1,2 @@
+*
+!.*

From 6a367d0ed7e14fe538e7dd796909746d6779a49b Mon Sep 17 00:00:00 2001
From: Hugo Alliaume <hugo@alliau.me>
Date: Sat, 15 May 2021 09:25:55 +0200
Subject: [PATCH 089/297] chore: Use the latest PHPUnit wherever possible

Related to https://github.com/phpstan/phpstan-mockery/pull/30#issuecomment-841182105
---
 .github/workflows/build.yml | 19 +++++++++----------
 composer.json               |  2 +-
 2 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 18898d7a..05fb9cb6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -38,10 +38,9 @@ jobs:
       - name: "Install dependencies"
         run: "composer install --no-interaction --no-progress --no-suggest"
 
-      - name: "Update PHPUnit"
-        if: matrix.php-version == '7.4' || matrix.php-version == '8.0'
-        run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
-
+      - name: "Downgrade PHPUnit"
+        if: matrix.php-version == '7.1' || 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"
@@ -111,9 +110,9 @@ jobs:
         if: ${{ matrix.dependencies == 'highest' }}
         run: "composer update --no-interaction --no-progress --no-suggest"
 
-      - name: "Update PHPUnit"
-        if: matrix.php-version == '7.4' || matrix.php-version == '8.0'
-        run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
+      - name: "Downgrade PHPUnit"
+        if: matrix.php-version == '7.1' || 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"
@@ -155,9 +154,9 @@ jobs:
         if: ${{ matrix.dependencies == 'highest' }}
         run: "composer update --no-interaction --no-progress --no-suggest"
 
-      - name: "Update PHPUnit"
-        if: matrix.php-version == '7.4' || matrix.php-version == '8.0'
-        run: "composer require --dev phpunit/phpunit:'^9.5' --update-with-dependencies"
+      - name: "Downgrade PHPUnit"
+        if: matrix.php-version == '7.1' || 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"
diff --git a/composer.json b/composer.json
index 6372b6cc..e9d076d1 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,7 @@
     "php-parallel-lint/php-parallel-lint": "^1.2",
     "phpstan/phpstan-phpunit": "^0.12.16",
     "phpstan/phpstan-strict-rules": "^0.12.5",
-    "phpunit/phpunit": "^7.5.20",
+    "phpunit/phpunit": "^9.5",
     "symfony/config": "^4.2 || ^5.0",
     "symfony/console": "^4.0 || ^5.0",
     "symfony/framework-bundle": "^4.4 || ^5.0",

From 716a70de8524928036d5bc0d880d1117fa77de82 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 31 May 2021 17:29:33 +0200
Subject: [PATCH 090/297] InputBag - compatibility with Symfony 5.3

---
 .../InputBagDynamicReturnTypeExtension.php    | 21 +++++++++++++------
 tests/Type/Symfony/data/input_bag.php         | 12 +++++------
 2 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
index 51e5ff19..a403af7f 100644
--- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
@@ -8,11 +8,15 @@
 use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Type\ArrayType;
+use PHPStan\Type\BooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\FloatType;
+use PHPStan\Type\IntegerType;
 use PHPStan\Type\MixedType;
 use PHPStan\Type\NullType;
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
 use PHPStan\Type\UnionType;
 
 final class InputBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -54,10 +58,8 @@ private function getGetTypeFromMethodCall(
 		if (isset($methodCall->args[1])) {
 			$argType = $scope->getType($methodCall->args[1]->value);
 			$isNull = (new NullType())->isSuperTypeOf($argType);
-			$isString = (new StringType())->isSuperTypeOf($argType);
-			$compare = $isNull->compareTo($isString);
-			if ($compare === $isString) {
-				return new StringType();
+			if ($isNull->no()) {
+				return TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType());
 			}
 		}
 
@@ -68,11 +70,18 @@ private function getAllTypeFromMethodCall(
 		MethodCall $methodCall
 	): Type
 	{
+		$types = [
+			new BooleanType(),
+			new FloatType(),
+			new IntegerType(),
+			new StringType(),
+		];
+		$oneParameterType = new UnionType($types);
 		if (isset($methodCall->args[0])) {
-			return new ArrayType(new MixedType(), new StringType());
+			return new ArrayType(new MixedType(), $oneParameterType);
 		}
 
-		return new ArrayType(new StringType(), new UnionType([new StringType(), new ArrayType(new MixedType(), new StringType())]));
+		return new ArrayType(new StringType(), new UnionType([new ArrayType(new MixedType(), $oneParameterType), new BooleanType(), new FloatType(), new IntegerType(), new StringType()]));
 	}
 
 }
diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php
index cdfde92b..c99fe6b6 100644
--- a/tests/Type/Symfony/data/input_bag.php
+++ b/tests/Type/Symfony/data/input_bag.php
@@ -4,9 +4,9 @@
 
 $bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar', 'bar' => ['x']]);
 
-assertType('string|null', $bag->get('foo'));
-assertType('string|null', $bag->get('foo', null));
-assertType('string', $bag->get('foo', ''));
-assertType('string', $bag->get('foo', 'baz'));
-assertType('array<string, array<string>|string>', $bag->all());
-assertType('array<string>', $bag->all('bar'));
+assertType('bool|float|int|string|null', $bag->get('foo'));
+assertType('bool|float|int|string|null', $bag->get('foo', null));
+assertType('bool|float|int|string', $bag->get('foo', ''));
+assertType('bool|float|int|string', $bag->get('foo', 'baz'));
+assertType('array<string, array<bool|float|int|string>|bool|float|int|string>', $bag->all());
+assertType('array<bool|float|int|string>', $bag->all('bar'));

From a4d47812f5db2cc4cc78000e8b4733d75532439e Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Mon, 31 May 2021 21:38:55 +0200
Subject: [PATCH 091/297] Fix FormView::getIterator return type

---
 stubs/Symfony/Component/Form/FormView.stub | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/stubs/Symfony/Component/Form/FormView.stub b/stubs/Symfony/Component/Form/FormView.stub
index e7646acf..8f920ed6 100644
--- a/stubs/Symfony/Component/Form/FormView.stub
+++ b/stubs/Symfony/Component/Form/FormView.stub
@@ -11,4 +11,12 @@ use IteratorAggregate;
  */
 class FormView implements ArrayAccess, IteratorAggregate
 {
+
+    /**
+     * Returns an iterator to iterate over children (implements \IteratorAggregate).
+     *
+     * @return \ArrayIterator<string, FormView> The iterator
+     */
+    public function getIterator();
+
 }

From 78c38a0a58cf2f1119e2231da3bf6865ab25e88d Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Tue, 1 Jun 2021 00:13:08 +0200
Subject: [PATCH 092/297] Precise array key and values returns by AclInterface
 methods

---
 extension.neon                                |  1 +
 .../Security/Acl/Model/AclInterface.stub      | 32 +++++++++++++++++++
 .../Security/Acl/Model/EntryInterface.stub    |  7 ++++
 3 files changed, 40 insertions(+)
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub

diff --git a/extension.neon b/extension.neon
index 7873e2b5..544a4d89 100644
--- a/extension.neon
+++ b/extension.neon
@@ -27,6 +27,7 @@ parameters:
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
index b2ce3e58..2f509501 100644
--- a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
+++ b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
@@ -5,4 +5,36 @@ namespace Symfony\Component\Security\Acl\Model;
 interface AclInterface
 {
 
+    /**
+     * Returns all class-based ACEs associated with this ACL.
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getClassAces();
+
+    /**
+     * Returns all class-field-based ACEs associated with this ACL.
+     *
+     * @param string $field
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getClassFieldAces($field);
+
+    /**
+     * Returns all object-based ACEs associated with this ACL.
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getObjectAces();
+
+    /**
+     * Returns all object-field-based ACEs associated with this ACL.
+     *
+     * @param string $field
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getObjectFieldAces($field);
+
 }
diff --git a/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
new file mode 100644
index 00000000..335e581d
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface EntryInterface
+{
+}

From 0cd5b0d7666294c2a983aa276bb2d7fa125d0ae4 Mon Sep 17 00:00:00 2001
From: Jeroen Noten <jeroennoten@me.com>
Date: Wed, 2 Jun 2021 13:44:45 +0200
Subject: [PATCH 093/297] Fix DenormalizableInterface::denormalize() return
 type declaration

---
 .../Serializer/Normalizer/DenormalizableInterface.stub          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub
index 17d6b118..7e8cb7e9 100644
--- a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub
@@ -10,7 +10,7 @@ interface DenormalizableInterface
      * @param string|null $format
      * @param array<mixed> $context
      *
-     * @return object|object[]
+     * @return void
      */
     public function denormalize($denormalizer, $data, $format = null, array $context = []);
 }

From c3ac708fc1860146e35760b16b8eb2a08dd25d62 Mon Sep 17 00:00:00 2001
From: Filip Ayazi <codeTom@users.noreply.github.com>
Date: Thu, 10 Jun 2021 13:35:06 +0100
Subject: [PATCH 094/297] Allow boolean in Command->addOption default value

---
 src/Rules/Symfony/InvalidOptionDefaultValueRule.php | 8 ++------
 tests/Rules/Symfony/ExampleCommand.php              | 1 +
 tests/Rules/Symfony/UndefinedArgumentRuleTest.php   | 2 +-
 tests/Rules/Symfony/UndefinedOptionRuleTest.php     | 2 +-
 4 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index 42d40ee0..678feb35 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -8,14 +8,13 @@
 use PHPStan\Rules\Rule;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Type\ArrayType;
-use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantIntegerType;
 use PHPStan\Type\IntegerType;
 use PHPStan\Type\MixedType;
 use PHPStan\Type\NullType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\StringType;
-use PHPStan\Type\TypeCombinator;
 use PHPStan\Type\TypeUtils;
 use PHPStan\Type\UnionType;
 use PHPStan\Type\VerbosityLevel;
@@ -70,10 +69,7 @@ public function processNode(Node $node, Scope $scope): array
 
 		// not an array
 		if (($mode & 8) !== 8) {
-			$checkType = new UnionType([new StringType(), new IntegerType(), new NullType()]);
-			if (($mode & 4) === 4) { // https://symfony.com/doc/current/console/input.html#options-with-optional-arguments
-				$checkType = TypeCombinator::union($checkType, new ConstantBooleanType(false));
-			}
+			$checkType = new UnionType([new StringType(), new IntegerType(), new NullType(), new BooleanType()]);
 			if (!$checkType->isSuperTypeOf($defaultType)->yes()) {
 				return [sprintf('Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.', $checkType->describe(VerbosityLevel::typeOnly()), $defaultType->describe(VerbosityLevel::typeOnly()))];
 			}
diff --git a/tests/Rules/Symfony/ExampleCommand.php b/tests/Rules/Symfony/ExampleCommand.php
index c376d875..6dec4cbd 100644
--- a/tests/Rules/Symfony/ExampleCommand.php
+++ b/tests/Rules/Symfony/ExampleCommand.php
@@ -29,6 +29,7 @@ protected function configure(): void
 		$this->addOption('b', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '', [1]);
 		$this->addOption('c', null, InputOption::VALUE_OPTIONAL, '', 1);
 		$this->addOption('d', null, InputOption::VALUE_OPTIONAL, '', false);
+		$this->addOption('f', null, InputOption::VALUE_REQUIRED, '', true);
 
 		/** @var string[] $defaults */
 		$defaults = [];
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
index 22fee153..ab4e1b05 100644
--- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -38,7 +38,7 @@ public function testGetArgument(): void
 			[
 				[
 					'Command "example-rule" does not define argument "undefined".',
-					41,
+					42,
 				],
 			]
 		);
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
index 1ca949de..5e81e401 100644
--- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -38,7 +38,7 @@ public function testGetArgument(): void
 			[
 				[
 					'Command "example-rule" does not define option "bbb".',
-					48,
+					49,
 				],
 			]
 		);

From 45e2ae29f6f53f5039b3d5c401dfc26724d8f5bb Mon Sep 17 00:00:00 2001
From: Michael Telgmann <mt@shopware.com>
Date: Wed, 16 Jun 2021 11:59:23 +0200
Subject: [PATCH 095/297] Introduce stub for EventDispatcherInterface

---
 extension.neon                                     |  1 +
 .../EventDispatcher/EventDispatcherInterface.stub  | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub

diff --git a/extension.neon b/extension.neon
index 544a4d89..c02cbbe7 100644
--- a/extension.neon
+++ b/extension.neon
@@ -12,6 +12,7 @@ parameters:
 		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
 		- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
 		- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
+		- stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/GenericEvent.stub
 		- stubs/Symfony/Component/Form/FormBuilderInterface.stub
diff --git a/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
new file mode 100644
index 00000000..e4ad8fc4
--- /dev/null
+++ b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Contracts\EventDispatcher;
+
+interface EventDispatcherInterface
+{
+    /**
+     * @template TEvent of object
+     * @param TEvent $event
+     *
+     * @return TEvent
+     */
+    public function dispatch(object $event, string $eventName = null): object;
+}

From 61f1d204a35476439dd8c545e0a40bf86b963a93 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Wed, 30 Jun 2021 15:12:49 +0200
Subject: [PATCH 096/297] Fix hasSession specifier

---
 .../RequestTypeSpecifyingExtension.php        |  7 +++-
 .../ImpossibleCheckTypeMethodCallRuleTest.php | 32 +++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php

diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
index 295d37e4..66372a7b 100644
--- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
@@ -46,10 +46,15 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 	{
 		$classReflection = $this->broker->getClass(self::REQUEST_CLASS);
 		$methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants();
+		$returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType();
+
+		if (!TypeCombinator::containsNull($returnType)) {
+			return new SpecifiedTypes();
+		}
 
 		return $this->typeSpecifier->create(
 			new MethodCall($node->var, self::GET_METHOD_NAME),
-			TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType()),
+			TypeCombinator::removeNull($returnType),
 			$context
 		);
 	}
diff --git a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
new file mode 100644
index 00000000..dba9cbfa
--- /dev/null
+++ b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PHPStan\Rules\Comparison\ImpossibleCheckTypeMethodCallRule;
+use PHPStan\Rules\Rule;
+
+/**
+ * @extends \PHPStan\Testing\RuleTestCase<ImpossibleCheckTypeMethodCallRule>
+ */
+class ImpossibleCheckTypeMethodCallRuleTest extends \PHPStan\Testing\RuleTestCase
+{
+
+	protected function getRule(): Rule
+	{
+		return self::getContainer()->getByType(ImpossibleCheckTypeMethodCallRule::class);
+	}
+
+	public function testExtension(): void
+	{
+		$this->analyse([__DIR__ . '/data/request_get_session.php'], []);
+	}
+
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/../../../extension.neon',
+			__DIR__ . '/../../../vendor/phpstan/phpstan-strict-rules/rules.neon',
+		];
+	}
+
+}

From 58b5959a11e26ce495b424ee634e721aaa9674d1 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 1 Jul 2021 09:33:28 +0200
Subject: [PATCH 097/297] Fixed createMarkerNode for long constant strings

---
 src/Type/Symfony/Helper.php                     |  2 +-
 .../ImpossibleCheckTypeMethodCallRuleTest.php   |  5 +++++
 tests/Type/Symfony/data/bug-178.php             | 17 +++++++++++++++++
 3 files changed, 23 insertions(+), 1 deletion(-)
 create mode 100644 tests/Type/Symfony/data/bug-178.php

diff --git a/src/Type/Symfony/Helper.php b/src/Type/Symfony/Helper.php
index a39af310..099b64c2 100644
--- a/src/Type/Symfony/Helper.php
+++ b/src/Type/Symfony/Helper.php
@@ -17,7 +17,7 @@ public static function createMarkerNode(Expr $expr, Type $type, PrettyPrinterAbs
 		return new Expr\Variable(md5(sprintf(
 			'%s::%s',
 			$printer->prettyPrintExpr($expr),
-			$type->describe(VerbosityLevel::value())
+			$type->describe(VerbosityLevel::precise())
 		)));
 	}
 
diff --git a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
index dba9cbfa..ab247b9f 100644
--- a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
+++ b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
@@ -21,6 +21,11 @@ public function testExtension(): void
 		$this->analyse([__DIR__ . '/data/request_get_session.php'], []);
 	}
 
+	public function testBug178(): void
+	{
+		$this->analyse([__DIR__ . '/data/bug-178.php'], []);
+	}
+
 	public static function getAdditionalConfigFiles(): array
 	{
 		return [
diff --git a/tests/Type/Symfony/data/bug-178.php b/tests/Type/Symfony/data/bug-178.php
new file mode 100644
index 00000000..9dbd5137
--- /dev/null
+++ b/tests/Type/Symfony/data/bug-178.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Bug178;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+
+class Foo extends AbstractController
+{
+
+	public function doFoo(): void
+	{
+		if ($this->has('sonata.media.manager.category') && $this->has('sonata.media.manager.context')) {
+			// do stuff that requires both managers.
+		}
+	}
+
+}

From 41fff98845bb8c4c0dc6c56f82b75358e57ccef9 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 8 Jul 2021 14:43:33 +0200
Subject: [PATCH 098/297] Fix AclProviderInterface and relevant classes because
 of the overriding method error

---
 extension.neon                                     |  2 ++
 .../Component/Security/Acl/Dbal/AclProvider.stub   | 14 ++++++++++++++
 .../Security/Acl/Dbal/MutableAclProvider.stub      | 14 ++++++++++++++
 .../Security/Acl/Model/AclProviderInterface.stub   |  7 +++++--
 .../Acl/Model/MutableAclProviderInterface.stub     |  8 ++++++--
 5 files changed, 41 insertions(+), 4 deletions(-)
 create mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
 create mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub

diff --git a/extension.neon b/extension.neon
index c02cbbe7..72cefce6 100644
--- a/extension.neon
+++ b/extension.neon
@@ -26,6 +26,8 @@ parameters:
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
+		- stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
new file mode 100644
index 00000000..cc0a37c3
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Dbal;
+
+use Symfony\Component\Security\Acl\Model\AclInterface;
+use Symfony\Component\Security\Acl\Model\AclProviderInterface;
+
+/**
+ * @implements AclProviderInterface<AclInterface>
+ */
+class AclProvider implements AclProviderInterface
+{
+
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub
new file mode 100644
index 00000000..e8568734
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Dbal;
+
+use Symfony\Component\Security\Acl\Model\MutableAclInterface;
+use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface;
+
+/**
+ * @implements MutableAclProviderInterface<MutableAclInterface>
+ */
+class MutableAclProvider implements MutableAclProviderInterface
+{
+
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
index 97cb6880..5c0ad94c 100644
--- a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
+++ b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
@@ -2,18 +2,21 @@
 
 namespace Symfony\Component\Security\Acl\Model;
 
+/**
+ * @template TAcl of AclInterface
+ */
 interface AclProviderInterface
 {
     /**
      * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return AclInterface
+     * @phpstan-return TAcl
      */
     public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
 
     /**
      * @phpstan-param array<ObjectIdentityInterface> $oids
      * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, AclInterface>
+     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, TAcl>
      */
     public function findAcls(array $oids, array $sids = []);
 }
diff --git a/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
index 4f25b6c5..0a8bd147 100644
--- a/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
+++ b/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
@@ -2,18 +2,22 @@
 
 namespace Symfony\Component\Security\Acl\Model;
 
+/**
+ * @template TAcl of MutableAclInterface
+ * @extends AclProviderInterface<TAcl>
+ */
 interface MutableAclProviderInterface extends AclProviderInterface
 {
     /**
      * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return MutableAclInterface
+     * @phpstan-return TAcl
      */
     public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
 
     /**
      * @phpstan-param array<ObjectIdentityInterface> $oids
      * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, MutableAclInterface>
+     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, TAcl>
      */
     public function findAcls(array $oids, array $sids = []);
 }

From 21e6ec58dcfdb684d599776cbf706774b9106c38 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 15 Jul 2021 13:28:37 +0200
Subject: [PATCH 099/297] Removed Security stubs, they're no longer needed

---
 extension.neon                                |  9 -----
 .../Security/Acl/Dbal/AclProvider.stub        | 14 -------
 .../Security/Acl/Dbal/MutableAclProvider.stub | 14 -------
 .../Security/Acl/Model/AclInterface.stub      | 40 -------------------
 .../Acl/Model/AclProviderInterface.stub       | 22 ----------
 .../Security/Acl/Model/EntryInterface.stub    |  7 ----
 .../Acl/Model/MutableAclInterface.stub        |  8 ----
 .../Model/MutableAclProviderInterface.stub    | 23 -----------
 .../Acl/Model/ObjectIdentityInterface.stub    |  8 ----
 .../Acl/Model/SecurityIdentityInterface.stub  |  8 ----
 10 files changed, 153 deletions(-)
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
 delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub

diff --git a/extension.neon b/extension.neon
index 72cefce6..f890042a 100644
--- a/extension.neon
+++ b/extension.neon
@@ -26,15 +26,6 @@ parameters:
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
-		- stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
-		- stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub
-		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
-		- stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
-		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
-		- stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
-		- stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
-		- stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
-		- stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
deleted file mode 100644
index cc0a37c3..00000000
--- a/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Dbal;
-
-use Symfony\Component\Security\Acl\Model\AclInterface;
-use Symfony\Component\Security\Acl\Model\AclProviderInterface;
-
-/**
- * @implements AclProviderInterface<AclInterface>
- */
-class AclProvider implements AclProviderInterface
-{
-
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub
deleted file mode 100644
index e8568734..00000000
--- a/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Dbal;
-
-use Symfony\Component\Security\Acl\Model\MutableAclInterface;
-use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface;
-
-/**
- * @implements MutableAclProviderInterface<MutableAclInterface>
- */
-class MutableAclProvider implements MutableAclProviderInterface
-{
-
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
deleted file mode 100644
index 2f509501..00000000
--- a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Model;
-
-interface AclInterface
-{
-
-    /**
-     * Returns all class-based ACEs associated with this ACL.
-     *
-     * @return array<int, EntryInterface>
-     */
-    public function getClassAces();
-
-    /**
-     * Returns all class-field-based ACEs associated with this ACL.
-     *
-     * @param string $field
-     *
-     * @return array<int, EntryInterface>
-     */
-    public function getClassFieldAces($field);
-
-    /**
-     * Returns all object-based ACEs associated with this ACL.
-     *
-     * @return array<int, EntryInterface>
-     */
-    public function getObjectAces();
-
-    /**
-     * Returns all object-field-based ACEs associated with this ACL.
-     *
-     * @param string $field
-     *
-     * @return array<int, EntryInterface>
-     */
-    public function getObjectFieldAces($field);
-
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
deleted file mode 100644
index 5c0ad94c..00000000
--- a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Model;
-
-/**
- * @template TAcl of AclInterface
- */
-interface AclProviderInterface
-{
-    /**
-     * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return TAcl
-     */
-    public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
-
-    /**
-     * @phpstan-param array<ObjectIdentityInterface> $oids
-     * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, TAcl>
-     */
-    public function findAcls(array $oids, array $sids = []);
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
deleted file mode 100644
index 335e581d..00000000
--- a/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Model;
-
-interface EntryInterface
-{
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
deleted file mode 100644
index 75df80b3..00000000
--- a/stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Model;
-
-interface MutableAclInterface extends AclInterface
-{
-
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
deleted file mode 100644
index 0a8bd147..00000000
--- a/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Model;
-
-/**
- * @template TAcl of MutableAclInterface
- * @extends AclProviderInterface<TAcl>
- */
-interface MutableAclProviderInterface extends AclProviderInterface
-{
-    /**
-     * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return TAcl
-     */
-    public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
-
-    /**
-     * @phpstan-param array<ObjectIdentityInterface> $oids
-     * @phpstan-param array<SecurityIdentityInterface> $sids
-     * @phpstan-return \SplObjectStorage<ObjectIdentityInterface, TAcl>
-     */
-    public function findAcls(array $oids, array $sids = []);
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
deleted file mode 100644
index 8c03dabc..00000000
--- a/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Model;
-
-interface ObjectIdentityInterface
-{
-
-}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub
deleted file mode 100644
index d60731dc..00000000
--- a/stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Acl\Model;
-
-interface SecurityIdentityInterface
-{
-
-}

From de268d22ab48f1f5677d2b96ddb7ea9a79498656 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Fri, 16 Jul 2021 14:24:20 +0200
Subject: [PATCH 100/297] Add back AclInterface stub

---
 extension.neon                                |  2 +
 .../Security/Acl/Model/AclInterface.stub      | 40 +++++++++++++++++++
 .../Security/Acl/Model/EntryInterface.stub    |  7 ++++
 3 files changed, 49 insertions(+)
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub

diff --git a/extension.neon b/extension.neon
index f890042a..a3e1b1dd 100644
--- a/extension.neon
+++ b/extension.neon
@@ -26,6 +26,8 @@ parameters:
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
+		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
new file mode 100644
index 00000000..2f509501
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
@@ -0,0 +1,40 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface AclInterface
+{
+
+    /**
+     * Returns all class-based ACEs associated with this ACL.
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getClassAces();
+
+    /**
+     * Returns all class-field-based ACEs associated with this ACL.
+     *
+     * @param string $field
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getClassFieldAces($field);
+
+    /**
+     * Returns all object-based ACEs associated with this ACL.
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getObjectAces();
+
+    /**
+     * Returns all object-field-based ACEs associated with this ACL.
+     *
+     * @param string $field
+     *
+     * @return array<int, EntryInterface>
+     */
+    public function getObjectFieldAces($field);
+
+}
diff --git a/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
new file mode 100644
index 00000000..335e581d
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Security\Acl\Model;
+
+interface EntryInterface
+{
+}

From 4ae5cbe13a30ac1a377465a5d9dddfb1f1f5a0b3 Mon Sep 17 00:00:00 2001
From: Jordi Sala Morales <jordism91@gmail.com>
Date: Sun, 18 Jul 2021 09:21:50 +0200
Subject: [PATCH 101/297] Add Lazy Command Support

---
 src/Symfony/ConsoleApplicationResolver.php    | 10 +++-
 tests/Type/Symfony/ExtensionTest.php          |  1 +
 .../Symfony/console_application_loader.php    | 11 +++++
 .../Symfony/data/ExampleOptionLazyCommand.php | 47 +++++++++++++++++++
 4 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 tests/Type/Symfony/data/ExampleOptionLazyCommand.php

diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 88ee908f..cc4ece4e 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -54,9 +54,17 @@ public function findCommands(ClassReflection $classReflection): array
 
 		$commands = [];
 		foreach ($this->consoleApplication->all() as $name => $command) {
-			if (!$classType->isSuperTypeOf(new ObjectType(get_class($command)))->yes()) {
+			$commandClass = new ObjectType(get_class($command));
+			$isLazyCommand = (new ObjectType('Symfony\Component\Console\Command\LazyCommand'))->isSuperTypeOf($commandClass)->yes();
+
+			if ($isLazyCommand && method_exists($command, 'getCommand') && !$classType->isSuperTypeOf(new ObjectType(get_class($command->getCommand())))->yes()) {
 				continue;
 			}
+
+			if (!$isLazyCommand && !$classType->isSuperTypeOf($commandClass)->yes()) {
+				continue;
+			}
+
 			$commands[$name] = $command;
 		}
 
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index a2de90ec..8ee9aced 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -22,6 +22,7 @@ public function dataFileAsserts(): iterable
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/tree_builder.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleBaseCommand.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionCommand.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionLazyCommand.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/kernel_interface.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/request_get_content.php');
 
diff --git a/tests/Type/Symfony/console_application_loader.php b/tests/Type/Symfony/console_application_loader.php
index 524bc159..0ef2e0ae 100644
--- a/tests/Type/Symfony/console_application_loader.php
+++ b/tests/Type/Symfony/console_application_loader.php
@@ -3,7 +3,9 @@
 use PHPStan\Type\Symfony\ExampleACommand;
 use PHPStan\Type\Symfony\ExampleBCommand;
 use PHPStan\Type\Symfony\ExampleOptionCommand;
+use PHPStan\Type\Symfony\ExampleOptionLazyCommand;
 use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Command\LazyCommand;
 
 require_once __DIR__ . '/../../../vendor/autoload.php';
 
@@ -11,4 +13,13 @@
 $application->add(new ExampleACommand());
 $application->add(new ExampleBCommand());
 $application->add(new ExampleOptionCommand());
+
+if (class_exists(LazyCommand::class)) {
+	$application->add(new LazyCommand('lazy-example-option', [], '', false, function () {
+		return new ExampleOptionLazyCommand();
+	}));
+} else {
+	$application->add(new ExampleOptionLazyCommand());
+}
+
 return $application;
diff --git a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
new file mode 100644
index 00000000..7933cd41
--- /dev/null
+++ b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
@@ -0,0 +1,47 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use function PHPStan\Testing\assertType;
+
+final class ExampleOptionLazyCommand extends Command
+{
+
+	protected static $defaultName = 'lazy-example-option';
+	protected static $defaultDescription = 'lazy example description';
+
+	protected function configure(): void
+	{
+		parent::configure();
+
+		$this->addOption('a', null, InputOption::VALUE_NONE);
+		$this->addOption('b', null, InputOption::VALUE_OPTIONAL);
+		$this->addOption('c', null, InputOption::VALUE_REQUIRED);
+		$this->addOption('d', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL);
+		$this->addOption('e', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED);
+
+		$this->addOption('bb', null, InputOption::VALUE_OPTIONAL, '', 1);
+		$this->addOption('cc', null, InputOption::VALUE_REQUIRED, '', 1);
+		$this->addOption('dd', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '', [1]);
+		$this->addOption('ee', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, '', [1]);
+	}
+
+	protected function execute(InputInterface $input, OutputInterface $output): int
+	{
+		assertType('bool', $input->getOption('a'));
+		assertType('string|null', $input->getOption('b'));
+		assertType('string|null', $input->getOption('c'));
+		assertType('array<int, string|null>', $input->getOption('d'));
+		assertType('array<int, string>', $input->getOption('e'));
+
+		assertType('1|string|null', $input->getOption('bb'));
+		assertType('1|string', $input->getOption('cc'));
+		assertType('array<int, 1|string|null>', $input->getOption('dd'));
+		assertType('array<int, 1|string>', $input->getOption('ee'));
+	}
+
+}

From fe5f5d8db30e1d25a7e874497902bd900d6229ab Mon Sep 17 00:00:00 2001
From: Thomas Landauer <thomas@landauer.at>
Date: Thu, 12 Aug 2021 15:25:38 +0200
Subject: [PATCH 102/297] Adding `scanDirectories` to README for Symfony 5.3+

---
 README.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/README.md b/README.md
index b853b3fa..ef670e6b 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,9 @@ parameters:
         container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml
         # or with Symfony 5+
         container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml
+    # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`:
+    scanDirectories:
+        - var/cache/dev/Symfony/Config
 ```
 
 ## Constant hassers

From 2c240808116be56c7129d9d48b8dd40d1344e0eb Mon Sep 17 00:00:00 2001
From: Alexey Sakhnov <alexsakhnov@gmail.com>
Date: Tue, 17 Aug 2021 20:54:08 +0300
Subject: [PATCH 103/297] Ignore ContainerBagInterface in
 ContainerInterfaceUnknownServiceRule

---
 .../ContainerInterfaceUnknownServiceRule.php       |  5 +++++
 .../ContainerInterfaceUnknownServiceRuleTest.php   | 14 ++++++++++++++
 tests/Rules/Symfony/ExampleServiceSubscriber.php   |  7 +++++++
 3 files changed, 26 insertions(+)

diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index 2719bb4e..eacc2071 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -55,6 +55,11 @@ public function processNode(Node $node, Scope $scope): array
 		}
 
 		$argType = $scope->getType($node->var);
+		$isContainerBagType = (new ObjectType('Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface'))->isSuperTypeOf($argType);
+		if ($isContainerBagType->yes()) {
+			return [];
+		}
+
 		$isControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller'))->isSuperTypeOf($argType);
 		$isAbstractControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\AbstractController'))->isSuperTypeOf($argType);
 		$isContainerType = (new ObjectType('Symfony\Component\DependencyInjection\ContainerInterface'))->isSuperTypeOf($argType);
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 306e219a..4f4fc092 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -68,4 +68,18 @@ public function testGetPrivateServiceInAbstractController(): void
 		);
 	}
 
+	public function testGetPrivateServiceInLegacyServiceSubscriber(): void
+	{
+		if (!interface_exists('Symfony\Contracts\Service\ServiceSubscriberInterface')) {
+			self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.');
+		}
+
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleServiceSubscriber.php',
+			],
+			[]
+		);
+	}
+
 }
diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php
index c9a009d2..9f5968df 100644
--- a/tests/Rules/Symfony/ExampleServiceSubscriber.php
+++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php
@@ -12,6 +12,13 @@ public function privateService(): void
 		$this->get('private');
 	}
 
+	public function containerParameter(): void
+	{
+		/** @var \Symfony\Component\DependencyInjection\ParameterBag\ContainerBag $containerBag */
+		$containerBag = doFoo();
+		$containerBag->get('parameter_name');
+	}
+
 	/**
 	 * @return string[]
 	 */

From 95d9ae78605e5023e0edcf905c9b42da2a2a8359 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 22 Aug 2021 12:58:31 +0200
Subject: [PATCH 104/297] Make InputBag generic

---
 composer.json                                 |  2 +-
 extension.neon                                |  5 ++++
 .../Component/HttpFoundation/InputBag.stub    | 20 +++++++++++++
 .../Component/HttpFoundation/Request.stub     | 29 +++++++++++++++++++
 tests/Type/Symfony/ExtensionTest.php          |  1 +
 .../Symfony/data/input_bag_from_request.php   | 22 ++++++++++++++
 6 files changed, 78 insertions(+), 1 deletion(-)
 create mode 100644 stubs/Symfony/Component/HttpFoundation/InputBag.stub
 create mode 100644 stubs/Symfony/Component/HttpFoundation/Request.stub
 create mode 100644 tests/Type/Symfony/data/input_bag_from_request.php

diff --git a/composer.json b/composer.json
index e9d076d1..f895d38e 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.86"
+    "phpstan/phpstan": "^0.12.97"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index a3e1b1dd..2dee6a69 100644
--- a/extension.neon
+++ b/extension.neon
@@ -4,6 +4,9 @@ parameters:
 	exceptions:
 		uncheckedExceptionClasses:
 			- 'Symfony\Component\Console\Exception\InvalidArgumentException'
+	featureToggles:
+		skipCheckGenericClasses:
+			- Symfony\Component\HttpFoundation\InputBag
 	symfony:
 		container_xml_path: null
 		constant_hassers: true
@@ -22,7 +25,9 @@ parameters:
 		- stubs/Symfony/Component/Form/FormView.stub
 		- stubs/Symfony/Component/HttpFoundation/Cookie.stub
 		- stubs/Symfony/Component/HttpFoundation/HeaderBag.stub
+		- stubs/Symfony/Component/HttpFoundation/InputBag.stub
 		- stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
+		- stubs/Symfony/Component/HttpFoundation/Request.stub
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
diff --git a/stubs/Symfony/Component/HttpFoundation/InputBag.stub b/stubs/Symfony/Component/HttpFoundation/InputBag.stub
new file mode 100644
index 00000000..98223ce6
--- /dev/null
+++ b/stubs/Symfony/Component/HttpFoundation/InputBag.stub
@@ -0,0 +1,20 @@
+<?php
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * @template TValue of string|int|float|bool
+ */
+final class InputBag extends ParameterBag
+{
+
+	/**
+	 * @param TValue|null $default
+	 * @return TValue|null
+	 */
+	public function get(string $key, $default = null)
+	{
+
+	}
+
+}
diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub
new file mode 100644
index 00000000..00d03b09
--- /dev/null
+++ b/stubs/Symfony/Component/HttpFoundation/Request.stub
@@ -0,0 +1,29 @@
+<?php
+
+namespace Symfony\Component\HttpFoundation;
+
+class Request
+{
+
+	/**
+	 * Request body parameters ($_POST).
+	 *
+	 * @var InputBag<string|int|float|bool>
+	 */
+	public $request;
+
+	/**
+	 * Query string parameters ($_GET).
+	 *
+	 * @var InputBag<string>
+	 */
+	public $query;
+
+	/**
+	 * Cookies ($_COOKIE).
+	 *
+	 * @var InputBag<string>
+	 */
+	public $cookies;
+
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 8ee9aced..dac1c232 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -44,6 +44,7 @@ public function dataFileAsserts(): iterable
 
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/serializer.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag_from_request.php');
 	}
 
 	/**
diff --git a/tests/Type/Symfony/data/input_bag_from_request.php b/tests/Type/Symfony/data/input_bag_from_request.php
new file mode 100644
index 00000000..7aa6057c
--- /dev/null
+++ b/tests/Type/Symfony/data/input_bag_from_request.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace InputBag;
+
+use Symfony\Component\HttpFoundation\Request;
+use function PHPStan\Testing\assertType;
+
+class Foo
+{
+
+	public function doFoo(Request $request): void
+	{
+		assertType('bool|float|int|string|null', $request->request->get('foo'));
+		assertType('string|null', $request->query->get('foo'));
+		assertType('string|null', $request->cookies->get('foo'));
+
+		assertType('bool|float|int|string', $request->request->get('foo', 'foo'));
+		assertType('string', $request->query->get('foo', 'foo'));
+		assertType('string', $request->cookies->get('foo', 'foo'));
+	}
+
+}

From b339195f7c09a44d6346a22c04a521845ddfb3a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= <enumag@gmail.com>
Date: Mon, 23 Aug 2021 14:54:12 +0200
Subject: [PATCH 105/297] Add Constraint::$errorNames

---
 stubs/Symfony/Component/Validator/Constraint.stub | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/stubs/Symfony/Component/Validator/Constraint.stub b/stubs/Symfony/Component/Validator/Constraint.stub
index 7d875f2a..e7a4b501 100644
--- a/stubs/Symfony/Component/Validator/Constraint.stub
+++ b/stubs/Symfony/Component/Validator/Constraint.stub
@@ -4,6 +4,11 @@ namespace Symfony\Component\Validator;
 
 class Constraint
 {
+    /**
+     * @var array<string, string>
+     */
+    protected static $errorNames = [];
+
     /**
      * @return array<mixed>
      */

From 390cfa13ffabd7968f733843828c4bb3f7a7c3c0 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 2 Sep 2021 13:49:47 +0200
Subject: [PATCH 106/297] Fix tests

---
 tests/Type/Symfony/ExtensionTest.php | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index dac1c232..5a49366b 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -43,8 +43,12 @@ public function dataFileAsserts(): iterable
 		}
 
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/serializer.php');
+
+		if (class_exists('Symfony\Component\HttpFoundation\InputBag')) {
+			yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag_from_request.php');
+		}
+
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag_from_request.php');
 	}
 
 	/**

From c1627fce5b505b3f53d9d4fbd4d7963603305f98 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 2 Sep 2021 13:53:08 +0200
Subject: [PATCH 107/297] Fix nonexistent InputBag on Symfony 4.x

---
 composer.json                                |  2 +-
 extension.neon                               |  6 ++--
 src/Symfony/InputBagStubFilesExtension.php   | 22 ++++++++++++++
 tests/Rules/NonexistentInputBagClassTest.php | 32 ++++++++++++++++++++
 tests/Rules/data/input_bag.php               | 23 ++++++++++++++
 5 files changed, 82 insertions(+), 3 deletions(-)
 create mode 100644 src/Symfony/InputBagStubFilesExtension.php
 create mode 100644 tests/Rules/NonexistentInputBagClassTest.php
 create mode 100644 tests/Rules/data/input_bag.php

diff --git a/composer.json b/composer.json
index f895d38e..2320f059 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.97"
+    "phpstan/phpstan": "^0.12.98"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index 2dee6a69..8907cc54 100644
--- a/extension.neon
+++ b/extension.neon
@@ -25,9 +25,7 @@ parameters:
 		- stubs/Symfony/Component/Form/FormView.stub
 		- stubs/Symfony/Component/HttpFoundation/Cookie.stub
 		- stubs/Symfony/Component/HttpFoundation/HeaderBag.stub
-		- stubs/Symfony/Component/HttpFoundation/InputBag.stub
 		- stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
-		- stubs/Symfony/Component/HttpFoundation/Request.stub
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
@@ -245,3 +243,7 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constant_hassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+	-
+		class: PHPStan\Symfony\InputBagStubFilesExtension
+		tags:
+			- phpstan.stubFilesExtension
diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php
new file mode 100644
index 00000000..94fd2104
--- /dev/null
+++ b/src/Symfony/InputBagStubFilesExtension.php
@@ -0,0 +1,22 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\PhpDoc\StubFilesExtension;
+
+class InputBagStubFilesExtension implements StubFilesExtension
+{
+
+	public function getFiles(): array
+	{
+		if (!class_exists('Symfony\Component\HttpFoundation\InputBag')) {
+			return [];
+		}
+
+		return [
+			__DIR__ . '/../../stubs/Symfony/Component/HttpFoundation/InputBag.stub',
+			__DIR__ . '/../../stubs/Symfony/Component/HttpFoundation/Request.stub',
+		];
+	}
+
+}
diff --git a/tests/Rules/NonexistentInputBagClassTest.php b/tests/Rules/NonexistentInputBagClassTest.php
new file mode 100644
index 00000000..6619a8c7
--- /dev/null
+++ b/tests/Rules/NonexistentInputBagClassTest.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules;
+
+use PHPStan\Rules\Methods\CallMethodsRule;
+use PHPStan\Testing\RuleTestCase;
+
+/**
+ * @extends RuleTestCase<CallMethodsRule>
+ */
+class NonexistentInputBagClassTest extends RuleTestCase
+{
+
+	protected function getRule(): \PHPStan\Rules\Rule
+	{
+		return self::getContainer()->getByType(CallMethodsRule::class);
+	}
+
+	public function testInputBag(): void
+	{
+		$this->analyse([__DIR__ . '/data/input_bag.php'], []);
+	}
+
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/../../extension.neon',
+			__DIR__ . '/../../rules.neon',
+		];
+	}
+
+}
diff --git a/tests/Rules/data/input_bag.php b/tests/Rules/data/input_bag.php
new file mode 100644
index 00000000..03699f6b
--- /dev/null
+++ b/tests/Rules/data/input_bag.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace InputBagTest;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Annotation\Route;
+
+class TestController extends AbstractController
+{
+	/**
+	 * @Route("/test", name="test")
+	 */
+	public function index(Request $request): Response
+	{
+		$foo = $request->query->get('foo');
+
+		return $this->render('test/index.html.twig', [
+			'controller_name' => 'TestController',
+		]);
+	}
+}

From 44251cd5f45cbcc76c8bb5c27f61fceb79ba799f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 12 Sep 2021 22:36:24 +0200
Subject: [PATCH 108/297] Open 1.0-dev

---
 composer.json | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/composer.json b/composer.json
index 2320f059..59676289 100644
--- a/composer.json
+++ b/composer.json
@@ -15,15 +15,15 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^0.12.98"
+    "phpstan/phpstan": "^1.0"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
   },
   "require-dev": {
     "php-parallel-lint/php-parallel-lint": "^1.2",
-    "phpstan/phpstan-phpunit": "^0.12.16",
-    "phpstan/phpstan-strict-rules": "^0.12.5",
+    "phpstan/phpstan-phpunit": "^1.0",
+    "phpstan/phpstan-strict-rules": "^1.0",
     "phpunit/phpunit": "^9.5",
     "symfony/config": "^4.2 || ^5.0",
     "symfony/console": "^4.0 || ^5.0",
@@ -40,7 +40,7 @@
   },
   "extra": {
     "branch-alias": {
-      "dev-master": "0.12-dev"
+      "dev-master": "1.0-dev"
     },
     "phpstan": {
       "includes": [

From 3707cff25f90b16bf792beb70c70e5dcad6f5a7d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 12 Sep 2021 23:52:07 +0200
Subject: [PATCH 109/297] Enforce generic InputBag

---
 extension.neon | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/extension.neon b/extension.neon
index 8907cc54..27e504bd 100644
--- a/extension.neon
+++ b/extension.neon
@@ -4,9 +4,6 @@ parameters:
 	exceptions:
 		uncheckedExceptionClasses:
 			- 'Symfony\Component\Console\Exception\InvalidArgumentException'
-	featureToggles:
-		skipCheckGenericClasses:
-			- Symfony\Component\HttpFoundation\InputBag
 	symfony:
 		container_xml_path: null
 		constant_hassers: true

From fb817e2f0ae9b9a90990de4f0a537dbfaddcad7a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 13 Sep 2021 10:45:19 +0200
Subject: [PATCH 110/297] Improve compatibility with PHPStan 1.0

---
 src/Symfony/ConsoleApplicationResolver.php | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index cc4ece4e..a493cbf0 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -5,6 +5,7 @@
 use PHPStan\Reflection\ClassReflection;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Type\ObjectType;
+use Symfony\Component\Console\Command\Command;
 use function file_exists;
 use function get_class;
 use function is_readable;
@@ -57,8 +58,12 @@ public function findCommands(ClassReflection $classReflection): array
 			$commandClass = new ObjectType(get_class($command));
 			$isLazyCommand = (new ObjectType('Symfony\Component\Console\Command\LazyCommand'))->isSuperTypeOf($commandClass)->yes();
 
-			if ($isLazyCommand && method_exists($command, 'getCommand') && !$classType->isSuperTypeOf(new ObjectType(get_class($command->getCommand())))->yes()) {
-				continue;
+			if ($isLazyCommand && method_exists($command, 'getCommand')) {
+				/** @var Command $wrappedCommand */
+				$wrappedCommand = $command->getCommand();
+				if (!$classType->isSuperTypeOf(new ObjectType(get_class($wrappedCommand)))->yes()) {
+					continue;
+				}
 			}
 
 			if (!$isLazyCommand && !$classType->isSuperTypeOf($commandClass)->yes()) {

From 1108c45d4326f3aaa85547ee0893bcc6ee375156 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 15 Sep 2021 16:16:02 +0200
Subject: [PATCH 111/297] Improve compatibility with PHPStan 1.0

---
 phpstan.neon                                   |  2 +-
 .../Symfony/UndefinedArgumentRuleTest.php      | 18 +++++++-----------
 .../Rules/Symfony/UndefinedOptionRuleTest.php  | 18 +++++++-----------
 tests/Rules/Symfony/argument.neon              |  5 +++++
 tests/Rules/Symfony/option.neon                |  5 +++++
 5 files changed, 25 insertions(+), 23 deletions(-)
 create mode 100644 tests/Rules/Symfony/argument.neon
 create mode 100644 tests/Rules/Symfony/option.neon

diff --git a/phpstan.neon b/phpstan.neon
index 13242793..39998f59 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -7,7 +7,7 @@ includes:
 	- phar://phpstan.phar/conf/bleedingEdge.neon
 
 parameters:
-	excludes_analyse:
+	excludePaths:
 		- tests/tmp/*
 		- tests/*/Example*.php
 		- tests/*/console_application_loader.php
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
index ab4e1b05..b3f9e56e 100644
--- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -6,7 +6,6 @@
 use PHPStan\Rules\Rule;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Testing\RuleTestCase;
-use PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension;
 
 /**
  * @extends RuleTestCase<UndefinedArgumentRule>
@@ -19,16 +18,6 @@ protected function getRule(): Rule
 		return new UndefinedArgumentRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard());
 	}
 
-	/**
-	 * @return \PHPStan\Type\MethodTypeSpecifyingExtension[]
-	 */
-	protected function getMethodTypeSpecifyingExtensions(): array
-	{
-		return [
-			new ArgumentTypeSpecifyingExtension(new Standard()),
-		];
-	}
-
 	public function testGetArgument(): void
 	{
 		$this->analyse(
@@ -44,4 +33,11 @@ public function testGetArgument(): void
 		);
 	}
 
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/argument.neon',
+		];
+	}
+
 }
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
index 5e81e401..32ebe773 100644
--- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -6,7 +6,6 @@
 use PHPStan\Rules\Rule;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Testing\RuleTestCase;
-use PHPStan\Type\Symfony\OptionTypeSpecifyingExtension;
 
 /**
  * @extends RuleTestCase<UndefinedOptionRule>
@@ -19,16 +18,6 @@ protected function getRule(): Rule
 		return new UndefinedOptionRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard());
 	}
 
-	/**
-	 * @return \PHPStan\Type\MethodTypeSpecifyingExtension[]
-	 */
-	protected function getMethodTypeSpecifyingExtensions(): array
-	{
-		return [
-			new OptionTypeSpecifyingExtension(new Standard()),
-		];
-	}
-
 	public function testGetArgument(): void
 	{
 		$this->analyse(
@@ -44,4 +33,11 @@ public function testGetArgument(): void
 		);
 	}
 
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/option.neon',
+		];
+	}
+
 }
diff --git a/tests/Rules/Symfony/argument.neon b/tests/Rules/Symfony/argument.neon
new file mode 100644
index 00000000..86fa3f16
--- /dev/null
+++ b/tests/Rules/Symfony/argument.neon
@@ -0,0 +1,5 @@
+services:
+	-
+		class: PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension
+		tags:
+			- phpstan.typeSpecifier.methodTypeSpecifyingExtension
diff --git a/tests/Rules/Symfony/option.neon b/tests/Rules/Symfony/option.neon
new file mode 100644
index 00000000..30984a7a
--- /dev/null
+++ b/tests/Rules/Symfony/option.neon
@@ -0,0 +1,5 @@
+services:
+	-
+		class: PHPStan\Type\Symfony\OptionTypeSpecifyingExtension
+		tags:
+			- phpstan.typeSpecifier.methodTypeSpecifyingExtension

From 0e272751e6141e6ece0c4b4a6cdb5d10ac39c73e Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 15 Sep 2021 16:50:11 +0200
Subject: [PATCH 112/297] Improved compatibility with PHPStan 1.0

---
 ...ntainerInterfaceUnknownServiceRuleTest.php | 20 +++++++------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 4f4fc092..297158a6 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -6,8 +6,6 @@
 use PHPStan\Rules\Rule;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
-use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
-use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 
 /**
  * @extends RuleTestCase<ContainerInterfaceUnknownServiceRule>
@@ -20,17 +18,6 @@ protected function getRule(): Rule
 		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create(), new Standard());
 	}
 
-	/**
-	 * @return \PHPStan\Type\MethodTypeSpecifyingExtension[]
-	 */
-	protected function getMethodTypeSpecifyingExtensions(): array
-	{
-		return [
-			new ServiceTypeSpecifyingExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', new Standard()),
-			new ServiceTypeSpecifyingExtension(AbstractController::class, new Standard()),
-		];
-	}
-
 	public function testGetPrivateService(): void
 	{
 		if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
@@ -82,4 +69,11 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 		);
 	}
 
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/../../../extension.neon',
+		];
+	}
+
 }

From a0ce7fd33dcc5308c2c7d697b55bd80e20b78e2c Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 20 Sep 2021 18:21:26 +0200
Subject: [PATCH 113/297] Constrain lowest PHPParser version to 4.13.0

---
 composer.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/composer.json b/composer.json
index 59676289..06adbfbf 100644
--- a/composer.json
+++ b/composer.json
@@ -21,6 +21,7 @@
     "symfony/framework-bundle": "<3.0"
   },
   "require-dev": {
+    "nikic/php-parser": "^4.13.0",
     "php-parallel-lint/php-parallel-lint": "^1.2",
     "phpstan/phpstan-phpunit": "^1.0",
     "phpstan/phpstan-strict-rules": "^1.0",

From 84541f032b2b27def3b3139d52d605c43a1afb84 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 20 Sep 2021 18:21:59 +0200
Subject: [PATCH 114/297] Improve compatibility with nikic/php-parser 4.13.0

---
 .../Symfony/ContainerInterfacePrivateServiceRule.php      | 4 ++--
 .../Symfony/ContainerInterfaceUnknownServiceRule.php      | 6 +++---
 src/Rules/Symfony/InvalidArgumentDefaultValueRule.php     | 6 +++---
 src/Rules/Symfony/InvalidOptionDefaultValueRule.php       | 6 +++---
 src/Rules/Symfony/UndefinedArgumentRule.php               | 4 ++--
 src/Rules/Symfony/UndefinedOptionRule.php                 | 4 ++--
 src/Type/Symfony/ArgumentTypeSpecifyingExtension.php      | 4 ++--
 ...yNodeDefinitionPrototypeDynamicReturnTypeExtension.php | 4 ++--
 .../Config/TreeBuilderDynamicReturnTypeExtension.php      | 4 ++--
 src/Type/Symfony/EnvelopeReturnTypeExtension.php          | 4 ++--
 src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php  | 4 ++--
 src/Type/Symfony/InputBagDynamicReturnTypeExtension.php   | 6 +++---
 ...nputInterfaceGetArgumentDynamicReturnTypeExtension.php | 6 +++---
 .../InputInterfaceGetOptionDynamicReturnTypeExtension.php | 6 +++---
 ...nputInterfaceHasArgumentDynamicReturnTypeExtension.php | 4 ++--
 .../InputInterfaceHasOptionDynamicReturnTypeExtension.php | 4 ++--
 .../Symfony/KernelInterfaceDynamicReturnTypeExtension.php | 2 +-
 src/Type/Symfony/OptionTypeSpecifyingExtension.php        | 4 ++--
 src/Type/Symfony/ParameterDynamicReturnTypeExtension.php  | 8 ++++----
 src/Type/Symfony/RequestDynamicReturnTypeExtension.php    | 4 ++--
 src/Type/Symfony/SerializerDynamicReturnTypeExtension.php | 4 ++--
 src/Type/Symfony/ServiceDynamicReturnTypeExtension.php    | 8 ++++----
 src/Type/Symfony/ServiceTypeSpecifyingExtension.php       | 4 ++--
 23 files changed, 55 insertions(+), 55 deletions(-)

diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index c471e6ba..ee4d5b05 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -45,7 +45,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [];
 		}
 
-		if ($node->name->name !== 'get' || !isset($node->args[0])) {
+		if ($node->name->name !== 'get' || !isset($node->getArgs()[0])) {
 			return [];
 		}
 
@@ -72,7 +72,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [];
 		}
 
-		$serviceId = $this->serviceMap::getServiceIdFromNode($node->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($node->getArgs()[0]->value, $scope);
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
 			if ($service !== null && !$service->isPublic()) {
diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index eacc2071..cb0ae4e6 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -50,7 +50,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [];
 		}
 
-		if ($node->name->name !== 'get' || !isset($node->args[0])) {
+		if ($node->name->name !== 'get' || !isset($node->getArgs()[0])) {
 			return [];
 		}
 
@@ -73,10 +73,10 @@ public function processNode(Node $node, Scope $scope): array
 			return [];
 		}
 
-		$serviceId = $this->serviceMap::getServiceIdFromNode($node->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($node->getArgs()[0]->value, $scope);
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
-			$serviceIdType = $scope->getType($node->args[0]->value);
+			$serviceIdType = $scope->getType($node->getArgs()[0]->value);
 			if ($service === null && !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType)) {
 				return [sprintf('Service "%s" is not registered in the container.', $serviceId)];
 			}
diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index b735dbfe..c29f11ed 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -46,11 +46,11 @@ public function processNode(Node $node, Scope $scope): array
 		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'addArgument') {
 			return [];
 		}
-		if (!isset($node->args[3])) {
+		if (!isset($node->getArgs()[3])) {
 			return [];
 		}
 
-		$modeType = isset($node->args[1]) ? $scope->getType($node->args[1]->value) : new NullType();
+		$modeType = isset($node->getArgs()[1]) ? $scope->getType($node->getArgs()[1]->value) : new NullType();
 		if ($modeType instanceof NullType) {
 			$modeType = new ConstantIntegerType(2); // InputArgument::OPTIONAL
 		}
@@ -63,7 +63,7 @@ public function processNode(Node $node, Scope $scope): array
 		}
 		$mode = $modeTypes[0]->getValue();
 
-		$defaultType = $scope->getType($node->args[3]->value);
+		$defaultType = $scope->getType($node->getArgs()[3]->value);
 
 		// not an array
 		if (($mode & 4) !== 4 && !(new UnionType([new StringType(), new NullType()]))->isSuperTypeOf($defaultType)->yes()) {
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index 678feb35..3eb2df1b 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -48,11 +48,11 @@ public function processNode(Node $node, Scope $scope): array
 		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'addOption') {
 			return [];
 		}
-		if (!isset($node->args[4])) {
+		if (!isset($node->getArgs()[4])) {
 			return [];
 		}
 
-		$modeType = isset($node->args[2]) ? $scope->getType($node->args[2]->value) : new NullType();
+		$modeType = isset($node->getArgs()[2]) ? $scope->getType($node->getArgs()[2]->value) : new NullType();
 		if ($modeType instanceof NullType) {
 			$modeType = new ConstantIntegerType(1); // InputOption::VALUE_NONE
 		}
@@ -65,7 +65,7 @@ public function processNode(Node $node, Scope $scope): array
 		}
 		$mode = $modeTypes[0]->getValue();
 
-		$defaultType = $scope->getType($node->args[4]->value);
+		$defaultType = $scope->getType($node->getArgs()[4]->value);
 
 		// not an array
 		if (($mode & 8) !== 8) {
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index e6de90b7..59f71ed6 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -64,11 +64,11 @@ public function processNode(Node $node, Scope $scope): array
 		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'getArgument') {
 			return [];
 		}
-		if (!isset($node->args[0])) {
+		if (!isset($node->getArgs()[0])) {
 			return [];
 		}
 
-		$argType = $scope->getType($node->args[0]->value);
+		$argType = $scope->getType($node->getArgs()[0]->value);
 		$argStrings = TypeUtils::getConstantStrings($argType);
 		if (count($argStrings) !== 1) {
 			return [];
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index ee041929..29e08819 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -64,11 +64,11 @@ public function processNode(Node $node, Scope $scope): array
 		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'getOption') {
 			return [];
 		}
-		if (!isset($node->args[0])) {
+		if (!isset($node->getArgs()[0])) {
 			return [];
 		}
 
-		$optType = $scope->getType($node->args[0]->value);
+		$optType = $scope->getType($node->getArgs()[0]->value);
 		$optStrings = TypeUtils::getConstantStrings($optType);
 		if (count($optStrings) !== 1) {
 			return [];
diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
index edf5574c..74b6981f 100644
--- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
@@ -38,10 +38,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall
 
 	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
 	{
-		if (!isset($node->args[0])) {
+		if (!isset($node->getArgs()[0])) {
 			return new SpecifiedTypes();
 		}
-		$argType = $scope->getType($node->args[0]->value);
+		$argType = $scope->getType($node->getArgs()[0]->value);
 		return $this->typeSpecifier->create(
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
index 71e4fb17..cd1d4e51 100644
--- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
@@ -56,11 +56,11 @@ public function getTypeFromMethodCall(
 		$defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
 
 		if ($methodReflection->getName() === 'prototype') {
-			if (!isset($methodCall->args[0])) {
+			if (!isset($methodCall->getArgs()[0])) {
 				return $defaultType;
 			}
 
-			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 			if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) {
 				$type = $argStrings[0]->getValue();
 
diff --git a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
index 25cd12dc..a5f4f724 100644
--- a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
@@ -44,8 +44,8 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
 
 		$type = 'array';
 
-		if (isset($methodCall->args[1])) {
-			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[1]->value));
+		if (isset($methodCall->getArgs()[1])) {
+			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[1]->value));
 			if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) {
 				$type = $argStrings[0]->getValue();
 			}
diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
index bf0712fc..e4e6a34c 100644
--- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php
+++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
@@ -31,11 +31,11 @@ public function getTypeFromMethodCall(
 		Scope $scope
 	): Type
 	{
-		if (count($methodCall->args) === 0) {
+		if (count($methodCall->getArgs()) === 0) {
 			return new ArrayType(new MixedType(), new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')));
 		}
 
-		$argType = $scope->getType($methodCall->args[0]->value);
+		$argType = $scope->getType($methodCall->getArgs()[0]->value);
 		if (!$argType instanceof ConstantStringType) {
 			return new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'));
 		}
diff --git a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
index 42c6878a..5b05a711 100644
--- a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
@@ -34,13 +34,13 @@ public function getTypeFromMethodCall(
 		Scope $scope
 	): Type
 	{
-		$firstArgType = isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new ConstantBooleanType(true);
+		$firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true);
 		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
 		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType);
 		$compareTypes = $isTrueType->compareTo($isFalseType);
 
 		if ($compareTypes === $isTrueType) {
-			$defaultArgType = isset($methodCall->args[1]) ? $scope->getType($methodCall->args[1]->value) : new NullType();
+			$defaultArgType = isset($methodCall->getArgs()[1]) ? $scope->getType($methodCall->getArgs()[1]->value) : new NullType();
 
 			return TypeCombinator::union($defaultArgType, new StringType());
 		}
diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
index a403af7f..f328ea86 100644
--- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
@@ -55,8 +55,8 @@ private function getGetTypeFromMethodCall(
 		Scope $scope
 	): Type
 	{
-		if (isset($methodCall->args[1])) {
-			$argType = $scope->getType($methodCall->args[1]->value);
+		if (isset($methodCall->getArgs()[1])) {
+			$argType = $scope->getType($methodCall->getArgs()[1]->value);
 			$isNull = (new NullType())->isSuperTypeOf($argType);
 			if ($isNull->no()) {
 				return TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType());
@@ -77,7 +77,7 @@ private function getAllTypeFromMethodCall(
 			new StringType(),
 		];
 		$oneParameterType = new UnionType($types);
-		if (isset($methodCall->args[0])) {
+		if (isset($methodCall->getArgs()[0])) {
 			return new ArrayType(new MixedType(), $oneParameterType);
 		}
 
diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index 24cccc40..15e75d0e 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -40,9 +40,9 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 
 	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
 	{
-		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->args, $methodReflection->getVariants())->getReturnType();
+		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
 
-		if (!isset($methodCall->args[0])) {
+		if (!isset($methodCall->getArgs()[0])) {
 			return $defaultReturnType;
 		}
 
@@ -51,7 +51,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return $defaultReturnType;
 		}
 
-		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($argStrings) !== 1) {
 			return $defaultReturnType;
 		}
diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
index 40a3993f..fb397311 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -42,9 +42,9 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 
 	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
 	{
-		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->args, $methodReflection->getVariants())->getReturnType();
+		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
 
-		if (!isset($methodCall->args[0])) {
+		if (!isset($methodCall->getArgs()[0])) {
 			return $defaultReturnType;
 		}
 
@@ -53,7 +53,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return $defaultReturnType;
 		}
 
-		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($optStrings) !== 1) {
 			return $defaultReturnType;
 		}
diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
index f69908fe..112a2bcf 100644
--- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
@@ -40,7 +40,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 	{
 		$defaultReturnType = new BooleanType();
 
-		if (!isset($methodCall->args[0])) {
+		if (!isset($methodCall->getArgs()[0])) {
 			return $defaultReturnType;
 		}
 
@@ -49,7 +49,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return $defaultReturnType;
 		}
 
-		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($argStrings) !== 1) {
 			return $defaultReturnType;
 		}
diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
index c60e2644..eec7201c 100644
--- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
@@ -40,7 +40,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 	{
 		$defaultReturnType = new BooleanType();
 
-		if (!isset($methodCall->args[0])) {
+		if (!isset($methodCall->getArgs()[0])) {
 			return $defaultReturnType;
 		}
 
@@ -49,7 +49,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return $defaultReturnType;
 		}
 
-		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($optStrings) !== 1) {
 			return $defaultReturnType;
 		}
diff --git a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
index f74f114e..5a2adae5 100644
--- a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
@@ -32,7 +32,7 @@ public function getTypeFromMethodCall(
 		Scope $scope
 	): Type
 	{
-		$firstArgType = isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new ConstantBooleanType(true);
+		$firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true);
 		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
 		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType);
 		$compareTypes = $isTrueType->compareTo($isFalseType);
diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
index aaff84c0..347cf591 100644
--- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
@@ -38,10 +38,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall
 
 	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
 	{
-		if (!isset($node->args[0])) {
+		if (!isset($node->getArgs()[0])) {
 			return new SpecifiedTypes();
 		}
-		$argType = $scope->getType($node->args[0]->value);
+		$argType = $scope->getType($node->getArgs()[0]->value);
 		return $this->typeSpecifier->create(
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index da7a489a..44477124 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -92,11 +92,11 @@ private function getGetTypeFromMethodCall(
 			new StringType(),
 			new NullType(),
 		]);
-		if (!isset($methodCall->args[0])) {
+		if (!isset($methodCall->getArgs()[0])) {
 			return $returnType;
 		}
 
-		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope);
+		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
 		if ($parameterKey !== null) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
 			if ($parameter !== null) {
@@ -130,11 +130,11 @@ private function getHasTypeFromMethodCall(
 	): Type
 	{
 		$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
-		if (!isset($methodCall->args[0]) || !$this->constantHassers) {
+		if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) {
 			return $returnType;
 		}
 
-		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope);
+		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
 		if ($parameterKey !== null) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
 			return new ConstantBooleanType($parameter !== null);
diff --git a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
index 72991824..284ddf01 100644
--- a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
@@ -31,11 +31,11 @@ public function getTypeFromMethodCall(
 		Scope $scope
 	): Type
 	{
-		if (!isset($methodCall->args[0])) {
+		if (!isset($methodCall->getArgs()[0])) {
 			return new StringType();
 		}
 
-		$argType = $scope->getType($methodCall->args[0]->value);
+		$argType = $scope->getType($methodCall->getArgs()[0]->value);
 		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($argType);
 		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($argType);
 		$compareTypes = $isTrueType->compareTo($isFalseType);
diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
index c6c237ee..fda9b048 100755
--- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
@@ -39,11 +39,11 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 
 	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
 	{
-		if (!isset($methodCall->args[1])) {
+		if (!isset($methodCall->getArgs()[1])) {
 			return new MixedType();
 		}
 
-		$argType = $scope->getType($methodCall->args[1]->value);
+		$argType = $scope->getType($methodCall->getArgs()[1]->value);
 		if (!$argType instanceof ConstantStringType) {
 			return new MixedType();
 		}
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index 9d028c35..650aaa42 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -61,11 +61,11 @@ private function getGetTypeFromMethodCall(
 	): Type
 	{
 		$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
-		if (!isset($methodCall->args[0])) {
+		if (!isset($methodCall->getArgs()[0])) {
 			return $returnType;
 		}
 
-		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope);
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
 			if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) {
@@ -83,11 +83,11 @@ private function getHasTypeFromMethodCall(
 	): Type
 	{
 		$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
-		if (!isset($methodCall->args[0]) || !$this->constantHassers) {
+		if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) {
 			return $returnType;
 		}
 
-		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope);
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
 			return new ConstantBooleanType($service !== null && $service->isPublic());
diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
index e9946387..a5aad54e 100644
--- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
@@ -42,10 +42,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall
 
 	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
 	{
-		if (!isset($node->args[0])) {
+		if (!isset($node->getArgs()[0])) {
 			return new SpecifiedTypes();
 		}
-		$argType = $scope->getType($node->args[0]->value);
+		$argType = $scope->getType($node->getArgs()[0]->value);
 		return $this->typeSpecifier->create(
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,

From 7d8ac24aa6f71ea790dc750bef2ffd62f88ec7d8 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 23 Sep 2021 13:04:39 +0200
Subject: [PATCH 115/297] Improved compatibility with PHPStan 1.0

---
 src/Type/Symfony/ParameterDynamicReturnTypeExtension.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 44477124..8e79cf59 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -15,6 +15,7 @@
 use PHPStan\Type\ConstantType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FloatType;
+use PHPStan\Type\GeneralizePrecision;
 use PHPStan\Type\IntegerType;
 use PHPStan\Type\MixedType;
 use PHPStan\Type\NullType;
@@ -117,7 +118,7 @@ private function generalizeType(Type $type): Type
 				return new ArrayType($this->generalizeType($type->getKeyType()), $this->generalizeType($type->getItemType()));
 			}
 			if ($type instanceof ConstantType) {
-				return $type->generalize();
+				return $type->generalize(GeneralizePrecision::lessSpecific());
 			}
 			return $traverse($type);
 		});

From 1e62ed37347f9d8dd20264d6136bbe7b4c179916 Mon Sep 17 00:00:00 2001
From: greeflas <vldmr.kuprienko@gmail.com>
Date: Mon, 27 Sep 2021 20:00:47 +0300
Subject: [PATCH 116/297] Add stubs for messenger component

---
 extension.neon                                  |  2 ++
 stubs/Symfony/Component/Messenger/Envelope.stub | 17 +++++++++++++++++
 .../Component/Messenger/StampInterface.stub     |  7 +++++++
 3 files changed, 26 insertions(+)
 create mode 100644 stubs/Symfony/Component/Messenger/Envelope.stub
 create mode 100644 stubs/Symfony/Component/Messenger/StampInterface.stub

diff --git a/extension.neon b/extension.neon
index 27e504bd..e389a125 100644
--- a/extension.neon
+++ b/extension.neon
@@ -24,6 +24,8 @@ parameters:
 		- stubs/Symfony/Component/HttpFoundation/HeaderBag.stub
 		- stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
+		- stubs/Symfony/Component/Messenger/StampInterface.stub
+		- stubs/Symfony/Component/Messenger/Envelope.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
diff --git a/stubs/Symfony/Component/Messenger/Envelope.stub b/stubs/Symfony/Component/Messenger/Envelope.stub
new file mode 100644
index 00000000..c40a5ee6
--- /dev/null
+++ b/stubs/Symfony/Component/Messenger/Envelope.stub
@@ -0,0 +1,17 @@
+<?php
+
+namespace Symfony\Component\Messenger;
+
+use Symfony\Component\Messenger\Stamp\StampInterface;
+
+final class Envelope
+{
+    /**
+     * @template T of \Symfony\Component\Messenger\Stamp\StampInterface
+     * @phpstan-param class-string<T> $stampFqcn
+     * @phpstan-return T|null
+     */
+    public function last(string $stampFqcn): ?StampInterface
+    {
+    }
+}
diff --git a/stubs/Symfony/Component/Messenger/StampInterface.stub b/stubs/Symfony/Component/Messenger/StampInterface.stub
new file mode 100644
index 00000000..2951ab45
--- /dev/null
+++ b/stubs/Symfony/Component/Messenger/StampInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Messenger\Stamp;
+
+interface StampInterface
+{
+}

From c21cb2ffcde2422928872b0c74705cdf3598425f Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Mon, 25 Oct 2021 09:40:16 +0200
Subject: [PATCH 117/297] Add DataTransformer stub

---
 extension.neon                                |  1 +
 .../Form/DataTransformerInterface.stub        | 26 +++++++++++++++++++
 2 files changed, 27 insertions(+)
 create mode 100644 stubs/Symfony/Component/Form/DataTransformerInterface.stub

diff --git a/extension.neon b/extension.neon
index e389a125..32ad05f4 100644
--- a/extension.neon
+++ b/extension.neon
@@ -15,6 +15,7 @@ parameters:
 		- stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/GenericEvent.stub
+		- stubs/Symfony/Component/Form/DataTransformerInterface.stub
 		- stubs/Symfony/Component/Form/FormBuilderInterface.stub
 		- stubs/Symfony/Component/Form/FormInterface.stub
 		- stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
diff --git a/stubs/Symfony/Component/Form/DataTransformerInterface.stub b/stubs/Symfony/Component/Form/DataTransformerInterface.stub
new file mode 100644
index 00000000..edd4411e
--- /dev/null
+++ b/stubs/Symfony/Component/Form/DataTransformerInterface.stub
@@ -0,0 +1,26 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * @phpstan-template T
+ * @phpstan-template R
+ */
+interface DataTransformerInterface
+{
+    /**
+     * @phpstan-param T|null $value The value in the original representation
+     *
+     * @phpstan-return R|null The value in the transformed representation
+     */
+    public function transform($value);
+
+    /**
+     * @phpstan-param R|null $value The value in the transformed representation
+     *
+     * @phpstan-return T|null The value in the original representation
+     */
+    public function reverseTransform($value);
+}

From edb55af46fe1aaa62fafae51c36ccd1b577125bb Mon Sep 17 00:00:00 2001
From: Sebastian Blank <sebastian.bl@gmx.de>
Date: Wed, 3 Nov 2021 15:14:43 +0100
Subject: [PATCH 118/297] Fix: Incorrect return type for InputBag::all()

---
 .../Symfony/InputBagDynamicReturnTypeExtension.php    | 11 ++---------
 tests/Type/Symfony/data/input_bag.php                 |  4 ++--
 2 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
index f328ea86..b4875829 100644
--- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
@@ -70,18 +70,11 @@ private function getAllTypeFromMethodCall(
 		MethodCall $methodCall
 	): Type
 	{
-		$types = [
-			new BooleanType(),
-			new FloatType(),
-			new IntegerType(),
-			new StringType(),
-		];
-		$oneParameterType = new UnionType($types);
 		if (isset($methodCall->getArgs()[0])) {
-			return new ArrayType(new MixedType(), $oneParameterType);
+			return new ArrayType(new MixedType(), new MixedType(true));
 		}
 
-		return new ArrayType(new StringType(), new UnionType([new ArrayType(new MixedType(), $oneParameterType), new BooleanType(), new FloatType(), new IntegerType(), new StringType()]));
+		return new ArrayType(new StringType(), new UnionType([new ArrayType(new MixedType(), new MixedType(true)), new BooleanType(), new FloatType(), new IntegerType(), new StringType()]));
 	}
 
 }
diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php
index c99fe6b6..4c836c5f 100644
--- a/tests/Type/Symfony/data/input_bag.php
+++ b/tests/Type/Symfony/data/input_bag.php
@@ -8,5 +8,5 @@
 assertType('bool|float|int|string|null', $bag->get('foo', null));
 assertType('bool|float|int|string', $bag->get('foo', ''));
 assertType('bool|float|int|string', $bag->get('foo', 'baz'));
-assertType('array<string, array<bool|float|int|string>|bool|float|int|string>', $bag->all());
-assertType('array<bool|float|int|string>', $bag->all('bar'));
+assertType('array<string, array|bool|float|int|string>', $bag->all());
+assertType('array', $bag->all('bar'));

From 197c4cf78e1568abf0f95593b561220072f69c65 Mon Sep 17 00:00:00 2001
From: Jeroen Noten <jeroen.noten@isaac.nl>
Date: Mon, 29 Nov 2021 09:55:28 +0100
Subject: [PATCH 119/297] Fix false positive "Service ... is private." when
 using KernelTestCase::$container

---
 extension.neon                                        |  2 ++
 .../Bundle/FrameworkBundle/Test/KernelTestCase.stub   | 11 +++++++++++
 .../Bundle/FrameworkBundle/Test/TestContainer.stub    |  7 +++++++
 3 files changed, 20 insertions(+)
 create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub

diff --git a/extension.neon b/extension.neon
index 32ad05f4..4b36a06c 100644
--- a/extension.neon
+++ b/extension.neon
@@ -9,6 +9,8 @@ parameters:
 		constant_hassers: true
 		console_application_loader: null
 	stubFiles:
+		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
+		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
 		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
 		- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
 		- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
new file mode 100644
index 00000000..4b3d6b35
--- /dev/null
+++ b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Bundle\FrameworkBundle\Test;
+
+abstract class KernelTestCase
+{
+    /**
+     * @var TestContainer
+     */
+    protected static $container;
+}
diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub b/stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
new file mode 100644
index 00000000..37f81545
--- /dev/null
+++ b/stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Bundle\FrameworkBundle\Test;
+
+class TestContainer
+{
+}

From be2425774b6e23a9b9dae5304941f33c113bef18 Mon Sep 17 00:00:00 2001
From: Jeroen Noten <jeroen.noten@isaac.nl>
Date: Mon, 29 Nov 2021 12:00:23 +0100
Subject: [PATCH 120/297] Services are private by default

---
 src/Symfony/XmlServiceMapFactory.php    |  2 +-
 tests/Symfony/DefaultServiceMapTest.php | 22 +++++++++++-----------
 tests/Symfony/container.xml             |  4 ++--
 tests/Type/Symfony/container.xml        |  4 ++--
 4 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 47aa1bc9..409ef847 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -48,7 +48,7 @@ public function create(): ServiceMap
 			$service = new Service(
 				strpos((string) $attrs->id, '.') === 0 ? substr((string) $attrs->id, 1) : (string) $attrs->id,
 				isset($attrs->class) ? (string) $attrs->class : null,
-				!isset($attrs->public) || (string) $attrs->public !== 'false',
+				isset($attrs->public) && (string) $attrs->public === 'true',
 				isset($attrs->synthetic) && (string) $attrs->synthetic === 'true',
 				isset($attrs->alias) ? (string) $attrs->alias : null
 			);
diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php
index 35d6b35d..8616ffaf 100644
--- a/tests/Symfony/DefaultServiceMapTest.php
+++ b/tests/Symfony/DefaultServiceMapTest.php
@@ -42,7 +42,7 @@ function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('withoutClass', $service->getId());
 				self::assertNull($service->getClass());
-				self::assertTrue($service->isPublic());
+				self::assertFalse($service->isPublic());
 				self::assertFalse($service->isSynthetic());
 				self::assertNull($service->getAlias());
 			},
@@ -53,7 +53,7 @@ function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('withClass', $service->getId());
 				self::assertSame('Foo', $service->getClass());
-				self::assertTrue($service->isPublic());
+				self::assertFalse($service->isPublic());
 				self::assertFalse($service->isSynthetic());
 				self::assertNull($service->getAlias());
 			},
@@ -64,29 +64,29 @@ function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('withoutPublic', $service->getId());
 				self::assertSame('Foo', $service->getClass());
-				self::assertTrue($service->isPublic());
+				self::assertFalse($service->isPublic());
 				self::assertFalse($service->isSynthetic());
 				self::assertNull($service->getAlias());
 			},
 		];
 		yield [
-			'publicNotFalse',
+			'publicNotTrue',
 			function (?Service $service): void {
 				self::assertNotNull($service);
-				self::assertSame('publicNotFalse', $service->getId());
+				self::assertSame('publicNotTrue', $service->getId());
 				self::assertSame('Foo', $service->getClass());
-				self::assertTrue($service->isPublic());
+				self::assertFalse($service->isPublic());
 				self::assertFalse($service->isSynthetic());
 				self::assertNull($service->getAlias());
 			},
 		];
 		yield [
-			'private',
+			'public',
 			function (?Service $service): void {
 				self::assertNotNull($service);
-				self::assertSame('private', $service->getId());
+				self::assertSame('public', $service->getId());
 				self::assertSame('Foo', $service->getClass());
-				self::assertFalse($service->isPublic());
+				self::assertTrue($service->isPublic());
 				self::assertFalse($service->isSynthetic());
 				self::assertNull($service->getAlias());
 			},
@@ -97,7 +97,7 @@ function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('synthetic', $service->getId());
 				self::assertSame('Foo', $service->getClass());
-				self::assertTrue($service->isPublic());
+				self::assertFalse($service->isPublic());
 				self::assertTrue($service->isSynthetic());
 				self::assertNull($service->getAlias());
 			},
@@ -108,7 +108,7 @@ function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('alias', $service->getId());
 				self::assertSame('Foo', $service->getClass());
-				self::assertTrue($service->isPublic());
+				self::assertFalse($service->isPublic());
 				self::assertFalse($service->isSynthetic());
 				self::assertSame('withClass', $service->getAlias());
 			},
diff --git a/tests/Symfony/container.xml b/tests/Symfony/container.xml
index 5bed7715..4be3bd98 100644
--- a/tests/Symfony/container.xml
+++ b/tests/Symfony/container.xml
@@ -37,8 +37,8 @@
     <service id="withoutClass"></service>
     <service id="withClass" class="Foo"></service>
     <service id="withoutPublic" class="Foo"></service>
-    <service id="publicNotFalse" class="Foo" public="true"></service>
-    <service id="private" class="Foo" public="false"></service>
+    <service id="publicNotTrue" class="Foo" public="false"></service>
+    <service id="public" class="Foo" public="true"></service>
     <service id="synthetic" class="Foo" synthetic="true"></service>
     <service id="alias" class="Bar" alias="withClass"></service>
   </services>
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index 5bedede2..f4240c3b 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -37,7 +37,7 @@
     </parameters>
 
     <services>
-        <service id="foo" class="Foo"></service>
-        <service id="synthetic" class="Synthetic" synthetic="true" />
+        <service id="foo" class="Foo" public="true"></service>
+        <service id="synthetic" class="Synthetic" public="true" synthetic="true" />
     </services>
 </container>

From a5aed927c36088284267c6170b41fcb556e32a44 Mon Sep 17 00:00:00 2001
From: Alex Rock Ancelet <pierstoval@gmail.com>
Date: Mon, 29 Nov 2021 16:07:36 +0100
Subject: [PATCH 121/297] Followup of TestContainer issue

---
 .../Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub  | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
index 4b3d6b35..88654f6c 100644
--- a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
+++ b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
@@ -8,4 +8,9 @@ abstract class KernelTestCase
      * @var TestContainer
      */
     protected static $container;
+
+    /**
+     * @return TestContainer
+     */
+    abstract public function getContainer();
 }

From 84df52516771a2d8cf26b7b5b42a6dd4b1d26c2c Mon Sep 17 00:00:00 2001
From: Jeroen Noten <jeroen.noten@isaac.nl>
Date: Wed, 15 Dec 2021 17:16:31 +0100
Subject: [PATCH 122/297] Fix false positive about private service in service
 subscriber

---
 .../ContainerInterfacePrivateServiceRule.php     | 16 +++++++++++++++-
 tests/Rules/Symfony/ExampleServiceSubscriber.php | 10 ++++++++++
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index ee4d5b05..4c9a6af1 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -8,7 +8,9 @@
 use PHPStan\Rules\Rule;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Symfony\ServiceMap;
+use PHPStan\TrinaryLogic;
 use PHPStan\Type\ObjectType;
+use PHPStan\Type\Type;
 use function sprintf;
 
 /**
@@ -53,7 +55,7 @@ public function processNode(Node $node, Scope $scope): array
 
 		$isTestContainerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Test\TestContainer'))->isSuperTypeOf($argType);
 		$isOldServiceSubscriber = (new ObjectType('Symfony\Component\DependencyInjection\ServiceSubscriberInterface'))->isSuperTypeOf($argType);
-		$isServiceSubscriber = (new ObjectType('Symfony\Contracts\Service\ServiceSubscriberInterface'))->isSuperTypeOf($argType);
+		$isServiceSubscriber = $this->isServiceSubscriber($argType, $scope);
 		$isServiceLocator = (new ObjectType('Symfony\Component\DependencyInjection\ServiceLocator'))->isSuperTypeOf($argType);
 		if ($isTestContainerType->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes() || $isServiceLocator->yes()) {
 			return [];
@@ -83,4 +85,16 @@ public function processNode(Node $node, Scope $scope): array
 		return [];
 	}
 
+	private function isServiceSubscriber(Type $containerType, Scope $scope): TrinaryLogic
+	{
+		$serviceSubscriberInterfaceType = new ObjectType('Symfony\Contracts\Service\ServiceSubscriberInterface');
+		$isContainerServiceSubscriber = $serviceSubscriberInterfaceType->isSuperTypeOf($containerType);
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			return $isContainerServiceSubscriber;
+		}
+		$containedClassType = new ObjectType($classReflection->getName());
+		return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType));
+	}
+
 }
diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php
index 9f5968df..3d7f294d 100644
--- a/tests/Rules/Symfony/ExampleServiceSubscriber.php
+++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php
@@ -2,14 +2,24 @@
 
 namespace PHPStan\Rules\Symfony;
 
+use Psr\Container\ContainerInterface;
 use Symfony\Contracts\Service\ServiceSubscriberInterface;
 
 final class ExampleServiceSubscriber implements ServiceSubscriberInterface
 {
 
+	/** @var ContainerInterface */
+	private $locator;
+
+	public function __construct(ContainerInterface $locator)
+	{
+		$this->locator = $locator;
+	}
+
 	public function privateService(): void
 	{
 		$this->get('private');
+		$this->locator->get('private');
 	}
 
 	public function containerParameter(): void

From 8ab1d52efbf416304b0285176a38b8a01a085d09 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Thu, 16 Dec 2021 15:43:01 +0100
Subject: [PATCH 123/297] Test on PHP 8.1

---
 .github/workflows/build.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 05fb9cb6..9c582d40 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,6 +21,7 @@ jobs:
           - "7.3"
           - "7.4"
           - "8.0"
+          - "8.1"
 
     steps:
       - name: "Checkout"

From 749b0037ab9a478c3322208b30fc29c3f0eefbb1 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Thu, 16 Dec 2021 19:36:26 +0100
Subject: [PATCH 124/297] Composer > Remove `--no-suggest`

You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3.
---
 .github/workflows/build.yml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9c582d40..76066c35 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -37,7 +37,7 @@ jobs:
         run: "composer validate"
 
       - name: "Install dependencies"
-        run: "composer install --no-interaction --no-progress --no-suggest"
+        run: "composer install --no-interaction --no-progress"
 
       - name: "Downgrade PHPUnit"
         if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3'
@@ -65,7 +65,7 @@ jobs:
         run: "composer validate"
 
       - name: "Install dependencies"
-        run: "composer install --no-interaction --no-progress --no-suggest"
+        run: "composer install --no-interaction --no-progress"
 
       - name: "Lint"
         run: "make lint"
@@ -105,11 +105,11 @@ jobs:
 
       - name: "Install lowest dependencies"
         if: ${{ matrix.dependencies == 'lowest' }}
-        run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest"
+        run: "composer update --prefer-lowest --no-interaction --no-progress"
 
       - name: "Install highest dependencies"
         if: ${{ matrix.dependencies == 'highest' }}
-        run: "composer update --no-interaction --no-progress --no-suggest"
+        run: "composer update --no-interaction --no-progress"
 
       - name: "Downgrade PHPUnit"
         if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3'
@@ -149,11 +149,11 @@ jobs:
 
       - name: "Install lowest dependencies"
         if: ${{ matrix.dependencies == 'lowest' }}
-        run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest"
+        run: "composer update --prefer-lowest --no-interaction --no-progress"
 
       - name: "Install highest dependencies"
         if: ${{ matrix.dependencies == 'highest' }}
-        run: "composer update --no-interaction --no-progress --no-suggest"
+        run: "composer update --no-interaction --no-progress"
 
       - name: "Downgrade PHPUnit"
         if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3'

From 4140813ebfe7536222b67652fb82f870606ef204 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis <ruudk@users.noreply.github.com>
Date: Thu, 16 Dec 2021 19:43:58 +0100
Subject: [PATCH 125/297] Test all jobs on PHP 8.1

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

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 76066c35..bccd397b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -86,6 +86,7 @@ jobs:
           - "7.3"
           - "7.4"
           - "8.0"
+          - "8.1"
         dependencies:
           - "lowest"
           - "highest"
@@ -131,6 +132,7 @@ jobs:
           - "7.3"
           - "7.4"
           - "8.0"
+          - "8.1"
         dependencies:
           - "lowest"
           - "highest"

From 19b0934a119c8ea60b2017313fae366e584dc739 Mon Sep 17 00:00:00 2001
From: Martin Herndl <martin@herndl.org>
Date: Thu, 6 Jan 2022 13:34:42 +0100
Subject: [PATCH 126/297] Allow Composer plugins

---
 build-cs/composer.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/build-cs/composer.json b/build-cs/composer.json
index ed7744e1..cc6a4983 100644
--- a/build-cs/composer.json
+++ b/build-cs/composer.json
@@ -3,5 +3,10 @@
 		"consistence-community/coding-standard": "^3.10",
 		"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
 		"slevomat/coding-standard": "^6.4"
+	},
+	"config": {
+		"allow-plugins": {
+			"dealerdirect/phpcodesniffer-composer-installer": true
+		}
 	}
 }

From 29ad8a949139ba8a5342cda60c62fea4d7afafce Mon Sep 17 00:00:00 2001
From: Michael Telgmann <mt@shopware.com>
Date: Fri, 7 Jan 2022 22:35:38 +0100
Subject: [PATCH 127/297] Introduce DynamicReturnTypeExtension for
 'Symfony\Component\Form\FormInterface::getErrors'

fixes https://github.com/phpstan/phpstan-symfony/issues/166
---
 composer.json                                 |  1 +
 extension.neon                                |  5 ++
 ...ormInterfaceDynamicReturnTypeExtension.php | 64 +++++++++++++++++++
 tests/Type/Symfony/ExtensionTest.php          |  2 +
 .../Symfony/data/FormInterface_getErrors.php  | 20 ++++++
 5 files changed, 92 insertions(+)
 create mode 100644 src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php
 create mode 100644 tests/Type/Symfony/data/FormInterface_getErrors.php

diff --git a/composer.json b/composer.json
index 06adbfbf..3cddfd1b 100644
--- a/composer.json
+++ b/composer.json
@@ -28,6 +28,7 @@
     "phpunit/phpunit": "^9.5",
     "symfony/config": "^4.2 || ^5.0",
     "symfony/console": "^4.0 || ^5.0",
+    "symfony/form": "^4.0 || ^5.0",
     "symfony/framework-bundle": "^4.4 || ^5.0",
     "symfony/http-foundation": "^4.0 || ^5.0",
     "symfony/messenger": "^4.2 || ^5.0",
diff --git a/extension.neon b/extension.neon
index 4b36a06c..47746a0b 100644
--- a/extension.neon
+++ b/extension.neon
@@ -249,3 +249,8 @@ services:
 		class: PHPStan\Symfony\InputBagStubFilesExtension
 		tags:
 			- phpstan.stubFilesExtension
+
+	# FormInterface::getErrors() return type
+	-
+		factory: PHPStan\Type\Symfony\Form\FormInterfaceDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..bdbc95ee
--- /dev/null
+++ b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php
@@ -0,0 +1,64 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony\Form;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\Generic\GenericObjectType;
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\Type;
+use PHPStan\Type\UnionType;
+use Symfony\Component\Form\FormError;
+use Symfony\Component\Form\FormErrorIterator;
+use Symfony\Component\Form\FormInterface;
+
+final class FormInterfaceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	public function getClass(): string
+	{
+		return FormInterface::class;
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getErrors';
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		if (!isset($methodCall->getArgs()[1])) {
+			return new GenericObjectType(FormErrorIterator::class, [new ObjectType(FormError::class)]);
+		}
+
+		$firstArgType = $scope->getType($methodCall->getArgs()[0]->value);
+		$secondArgType = $scope->getType($methodCall->getArgs()[1]->value);
+
+		$firstIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
+		$firstIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType);
+		$secondIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($secondArgType);
+		$secondIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($secondArgType);
+
+		$firstCompareType = $firstIsTrueType->compareTo($firstIsFalseType);
+		$secondCompareType = $secondIsTrueType->compareTo($secondIsFalseType);
+
+		if ($firstCompareType === $firstIsTrueType && $secondCompareType === $secondIsFalseType) {
+			return new GenericObjectType(FormErrorIterator::class, [
+				new UnionType([
+					new ObjectType(FormError::class),
+					new ObjectType(FormErrorIterator::class),
+				]),
+			]);
+		}
+
+		return new GenericObjectType(FormErrorIterator::class, [new ObjectType(FormError::class)]);
+	}
+
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 5a49366b..61d354da 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -49,6 +49,8 @@ public function dataFileAsserts(): iterable
 		}
 
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php');
+
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php');
 	}
 
 	/**
diff --git a/tests/Type/Symfony/data/FormInterface_getErrors.php b/tests/Type/Symfony/data/FormInterface_getErrors.php
new file mode 100644
index 00000000..a360a21d
--- /dev/null
+++ b/tests/Type/Symfony/data/FormInterface_getErrors.php
@@ -0,0 +1,20 @@
+<?php declare(strict_types=1);
+
+use Symfony\Component\Form\FormError;
+use Symfony\Component\Form\FormErrorIterator;
+use Symfony\Component\Form\FormInterface;
+use function PHPStan\Testing\assertType;
+
+/** @var FormInterface $form */
+$form = new stdClass();
+
+assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors());
+assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(false));
+assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(false, true));
+
+assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(true));
+assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(true, true));
+
+assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(false, false));
+
+assertType(FormErrorIterator::class . '<'. FormError::class .'|'. FormErrorIterator::class . '>', $form->getErrors(true, false));

From 9b8b650619a1febd7261b59e5c8c9287755bcf02 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Thu, 30 Dec 2021 23:07:26 +0100
Subject: [PATCH 128/297] Fix FormView stub

---
 stubs/Symfony/Component/Form/FormView.stub | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/stubs/Symfony/Component/Form/FormView.stub b/stubs/Symfony/Component/Form/FormView.stub
index 8f920ed6..08c64752 100644
--- a/stubs/Symfony/Component/Form/FormView.stub
+++ b/stubs/Symfony/Component/Form/FormView.stub
@@ -6,8 +6,8 @@ use ArrayAccess;
 use IteratorAggregate;
 
 /**
- * @implements IteratorAggregate<string, self>
- * @implements ArrayAccess<string, self>
+ * @implements IteratorAggregate<int|string, self>
+ * @implements ArrayAccess<int|string, self>
  */
 class FormView implements ArrayAccess, IteratorAggregate
 {
@@ -15,7 +15,7 @@ class FormView implements ArrayAccess, IteratorAggregate
     /**
      * Returns an iterator to iterate over children (implements \IteratorAggregate).
      *
-     * @return \ArrayIterator<string, FormView> The iterator
+     * @return \ArrayIterator<int|string, FormView> The iterator
      */
     public function getIterator();
 

From 9e985a975b1ccff52fd65cf54d1bd5c32b736de5 Mon Sep 17 00:00:00 2001
From: Anatoly Pashin <anatoly.pashin@gmail.com>
Date: Wed, 8 Dec 2021 21:49:08 +1000
Subject: [PATCH 129/297] Add KernelBrowser stub

Fixes #216
---
 extension.neon                                      |  1 +
 .../Bundle/FrameworkBundle/KernelBrowser.stub       | 13 +++++++++++++
 2 files changed, 14 insertions(+)
 create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub

diff --git a/extension.neon b/extension.neon
index 47746a0b..59d9f3f6 100644
--- a/extension.neon
+++ b/extension.neon
@@ -9,6 +9,7 @@ parameters:
 		constant_hassers: true
 		console_application_loader: null
 	stubFiles:
+		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
 		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
diff --git a/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub b/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
new file mode 100644
index 00000000..ec54f1eb
--- /dev/null
+++ b/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
@@ -0,0 +1,13 @@
+<?php
+
+namespace Symfony\Bundle\FrameworkBundle;
+
+use Symfony\Bundle\FrameworkBundle\Test\TestContainer;
+
+abstract class KernelBrowser
+{
+    /**
+     * @return TestContainer
+     */
+    abstract public function getContainer();
+}

From d31922ca4bab5684d53dc749d5742f0b455c0149 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= <looky.msc@gmail.com>
Date: Sat, 8 Jan 2022 19:51:27 +0100
Subject: [PATCH 130/297] Command::getHelper return type

---
 extension.neon                                |  7 ++
 ...andGetHelperDynamicReturnTypeExtension.php | 71 +++++++++++++++++++
 stubs/Symfony/Component/Console/Command.stub  | 11 +++
 .../Console/Helper/HelperInterface.stub       |  7 ++
 .../Type/Symfony/data/ExampleBaseCommand.php  |  1 +
 5 files changed, 97 insertions(+)
 create mode 100644 src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
 create mode 100644 stubs/Symfony/Component/Console/Command.stub
 create mode 100644 stubs/Symfony/Component/Console/Helper/HelperInterface.stub

diff --git a/extension.neon b/extension.neon
index 59d9f3f6..aab376e8 100644
--- a/extension.neon
+++ b/extension.neon
@@ -12,6 +12,8 @@ parameters:
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
+		- stubs/Symfony/Component/Console/Command.stub
+		- stubs/Symfony/Component/Console/Helper/HelperInterface.stub
 		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
 		- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
 		- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
@@ -255,3 +257,8 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\Form\FormInterfaceDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# Command::getHelper() return type
+	-
+		factory: PHPStan\Type\Symfony\CommandGetHelperDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..e9b6ed7e
--- /dev/null
+++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
@@ -0,0 +1,71 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
+use PHPStan\Type\TypeUtils;
+use Throwable;
+use function count;
+use function get_class;
+
+final class CommandGetHelperDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Command\Command';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getHelper';
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	{
+		$defaultReturnType = new ObjectType('Symfony\Component\Console\Helper\HelperInterface');
+
+		if (!isset($methodCall->getArgs()[0])) {
+			return $defaultReturnType;
+		}
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			return $defaultReturnType;
+		}
+
+		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
+		if (count($argStrings) !== 1) {
+			return $defaultReturnType;
+		}
+		$argName = $argStrings[0]->getValue();
+
+		$returnTypes = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
+			try {
+				$command->mergeApplicationDefinition();
+				$returnTypes[] = new ObjectType(get_class($command->getHelper($argName)));
+			} catch (Throwable $e) {
+				// no-op
+			}
+		}
+
+		return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : $defaultReturnType;
+	}
+
+}
diff --git a/stubs/Symfony/Component/Console/Command.stub b/stubs/Symfony/Component/Console/Command.stub
new file mode 100644
index 00000000..6bc2ea4b
--- /dev/null
+++ b/stubs/Symfony/Component/Console/Command.stub
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\Console\Command;
+
+class Command
+{
+    /**
+     * @return \Symfony\Component\Console\Helper\HelperInterface
+     */
+    public function getHelper(string $name);
+}
diff --git a/stubs/Symfony/Component/Console/Helper/HelperInterface.stub b/stubs/Symfony/Component/Console/Helper/HelperInterface.stub
new file mode 100644
index 00000000..757a68b0
--- /dev/null
+++ b/stubs/Symfony/Component/Console/Helper/HelperInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Console\Helper;
+
+interface HelperInterface
+{
+}
diff --git a/tests/Type/Symfony/data/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php
index a4493b2c..0ab3a322 100644
--- a/tests/Type/Symfony/data/ExampleBaseCommand.php
+++ b/tests/Type/Symfony/data/ExampleBaseCommand.php
@@ -25,6 +25,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
 		assertType('array<int, string>|string', $input->getArgument('diff'));
 		assertType('array<int, string>', $input->getArgument('arr'));
 		assertType('string|null', $input->getArgument('both'));
+		assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question'));
 	}
 
 }

From 39ac41f418dd2df8da3b8d20aa1eb8bd4deedbcd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= <looky.msc@gmail.com>
Date: Sun, 9 Jan 2022 09:14:41 +0100
Subject: [PATCH 131/297] Use camelCase config parameters (old ones still work)

---
 README.md                                     | 10 +++---
 extension.neon                                | 31 ++++++++++-------
 src/Symfony/Configuration.php                 | 34 +++++++++++++++++++
 src/Symfony/ConsoleApplicationResolver.php    |  3 +-
 src/Symfony/XmlParameterMapFactory.php        |  4 +--
 src/Symfony/XmlServiceMapFactory.php          |  4 +--
 .../ParameterDynamicReturnTypeExtension.php   |  5 +--
 .../ServiceDynamicReturnTypeExtension.php     |  5 +--
 ...nerInterfacePrivateServiceRuleFakeTest.php |  3 +-
 ...ntainerInterfacePrivateServiceRuleTest.php |  3 +-
 ...nerInterfaceUnknownServiceRuleFakeTest.php |  3 +-
 ...ntainerInterfaceUnknownServiceRuleTest.php |  3 +-
 .../Symfony/UndefinedArgumentRuleTest.php     |  3 +-
 .../Rules/Symfony/UndefinedOptionRuleTest.php |  3 +-
 tests/Symfony/DefaultParameterMapTest.php     |  4 +--
 tests/Symfony/DefaultServiceMapTest.php       |  4 +--
 16 files changed, 86 insertions(+), 36 deletions(-)
 create mode 100644 src/Symfony/Configuration.php

diff --git a/README.md b/README.md
index ef670e6b..7fe1a8bb 100644
--- a/README.md
+++ b/README.md
@@ -61,11 +61,11 @@ You have to provide a path to `srcDevDebugProjectContainer.xml` or similar XML f
 ```yaml
 parameters:
     symfony:
-        container_xml_path: var/cache/dev/srcDevDebugProjectContainer.xml
+        containerXmlPath: var/cache/dev/srcDevDebugProjectContainer.xml
         # or with Symfony 4.2+
-        container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml
+        containerXmlPath: var/cache/dev/srcApp_KernelDevDebugContainer.xml
         # or with Symfony 5+
-        container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml
+        containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml
     # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`:
     scanDirectories:
         - var/cache/dev/Symfony/Config
@@ -86,7 +86,7 @@ In that case, you can disable the `::has()` method return type resolving like th
 ```
 parameters:
 	symfony:
-		constant_hassers: false
+		constantHassers: false
 ```
 
 Be aware that it may hide genuine errors in your application.
@@ -99,7 +99,7 @@ by providing the console application from your own application. This will allow
 ```neon
 parameters:
 	symfony:
-		console_application_loader: tests/console-application.php
+		consoleApplicationLoader: tests/console-application.php
 ```
 
 Symfony 4:
diff --git a/extension.neon b/extension.neon
index aab376e8..23008e5d 100644
--- a/extension.neon
+++ b/extension.neon
@@ -6,8 +6,11 @@ parameters:
 			- 'Symfony\Component\Console\Exception\InvalidArgumentException'
 	symfony:
 		container_xml_path: null
+		containerXmlPath: null
 		constant_hassers: true
+		constantHassers: true
 		console_application_loader: null
+		consoleApplicationLoader: null
 	stubFiles:
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
@@ -53,43 +56,47 @@ parameters:
 parametersSchema:
 	symfony: structure([
 		container_xml_path: schema(string(), nullable())
+		containerXmlPath: schema(string(), nullable())
 		constant_hassers: bool()
+		constantHassers: bool()
 		console_application_loader: schema(string(), nullable())
+		consoleApplicationLoader: schema(string(), nullable())
 	])
 
 services:
+	-
+		factory: PHPStan\Symfony\Configuration(%symfony%)
+
 	# console resolver
 	-
 		factory: PHPStan\Symfony\ConsoleApplicationResolver
-		arguments:
-		    consoleApplicationLoader: %symfony.console_application_loader%
 
 	# service map
 	symfony.serviceMapFactory:
 		class: PHPStan\Symfony\ServiceMapFactory
-		factory: PHPStan\Symfony\XmlServiceMapFactory(%symfony.container_xml_path%)
+		factory: PHPStan\Symfony\XmlServiceMapFactory
 	-
 		factory: @symfony.serviceMapFactory::create()
 
 	# parameter map
 	symfony.parameterMapFactory:
 		class: PHPStan\Symfony\ParameterMapFactory
-		factory: PHPStan\Symfony\XmlParameterMapFactory(%symfony.container_xml_path%)
+		factory: PHPStan\Symfony\XmlParameterMapFactory
 	-
 		factory: @symfony.parameterMapFactory::create()
 
 	# ControllerTrait::get()/has() return type
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface, %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
 	# ControllerTrait::has() type specification
@@ -233,20 +240,20 @@ services:
 
 	# ParameterBagInterface::get()/has() return type
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has')
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
 	# ContainerInterface::getParameter()/hasParameter() return type
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter', %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter')
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
 	# (Abstract)Controller::getParameter() return type
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constant_hassers%)
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
 		class: PHPStan\Symfony\InputBagStubFilesExtension
diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php
new file mode 100644
index 00000000..4c1f1a31
--- /dev/null
+++ b/src/Symfony/Configuration.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+final class Configuration
+{
+
+	/** @var array<string, mixed> */
+	private $parameters;
+
+	/**
+	 * @param array<string, mixed> $parameters
+	 */
+	public function __construct(array $parameters)
+	{
+		$this->parameters = $parameters;
+	}
+
+	public function getContainerXmlPath(): ?string
+	{
+		return $this->parameters['containerXmlPath'] ?? $this->parameters['container_xml_path'] ?? null;
+	}
+
+	public function hasConstantHassers(): bool
+	{
+		return $this->parameters['constantHassers'] ?? $this->parameters['constant_hassers'] ?? true;
+	}
+
+	public function getConsoleApplicationLoader(): ?string
+	{
+		return $this->parameters['consoleApplicationLoader'] ?? $this->parameters['console_application_loader'] ?? null;
+	}
+
+}
diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index a493cbf0..3efc0d3f 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -16,8 +16,9 @@ final class ConsoleApplicationResolver
 	/** @var \Symfony\Component\Console\Application|null */
 	private $consoleApplication;
 
-	public function __construct(?string $consoleApplicationLoader)
+	public function __construct(Configuration $configuration)
 	{
+		$consoleApplicationLoader = $configuration->getConsoleApplicationLoader();
 		if ($consoleApplicationLoader === null) {
 			return;
 		}
diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php
index 6a7575f4..9b34d14d 100644
--- a/src/Symfony/XmlParameterMapFactory.php
+++ b/src/Symfony/XmlParameterMapFactory.php
@@ -10,9 +10,9 @@ final class XmlParameterMapFactory implements ParameterMapFactory
 	/** @var string|null */
 	private $containerXml;
 
-	public function __construct(?string $containerXml)
+	public function __construct(Configuration $configuration)
 	{
-		$this->containerXml = $containerXml;
+		$this->containerXml = $configuration->getContainerXmlPath();
 	}
 
 	public function create(): ParameterMap
diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 409ef847..60af1326 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -13,9 +13,9 @@ final class XmlServiceMapFactory implements ServiceMapFactory
 	/** @var string|null */
 	private $containerXml;
 
-	public function __construct(?string $containerXml)
+	public function __construct(Configuration $configuration)
 	{
-		$this->containerXml = $containerXml;
+		$this->containerXml = $configuration->getContainerXmlPath();
 	}
 
 	public function create(): ServiceMap
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 8e79cf59..29b1a883 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -7,6 +7,7 @@
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\ShouldNotHappenException;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ParameterMap;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\BooleanType;
@@ -43,12 +44,12 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy
 	/** @var \PHPStan\Symfony\ParameterMap */
 	private $parameterMap;
 
-	public function __construct(string $className, ?string $methodGet, ?string $methodHas, bool $constantHassers, ParameterMap $symfonyParameterMap)
+	public function __construct(string $className, ?string $methodGet, ?string $methodHas, Configuration $configuration, ParameterMap $symfonyParameterMap)
 	{
 		$this->className = $className;
 		$this->methodGet = $methodGet;
 		$this->methodHas = $methodHas;
-		$this->constantHassers = $constantHassers;
+		$this->constantHassers = $configuration->hasConstantHassers();
 		$this->parameterMap = $symfonyParameterMap;
 	}
 
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index 650aaa42..41c3a394 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -7,6 +7,7 @@
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\ShouldNotHappenException;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ServiceMap;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
@@ -26,10 +27,10 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
 	/** @var \PHPStan\Symfony\ServiceMap */
 	private $serviceMap;
 
-	public function __construct(string $className, bool $constantHassers, ServiceMap $symfonyServiceMap)
+	public function __construct(string $className, Configuration $configuration, ServiceMap $symfonyServiceMap)
 	{
 		$this->className = $className;
-		$this->constantHassers = $constantHassers;
+		$this->constantHassers = $configuration->hasConstantHassers();
 		$this->serviceMap = $symfonyServiceMap;
 	}
 
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
index 81925291..23bdd4e6 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Rules\Symfony;
 
 use PHPStan\Rules\Rule;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 
@@ -14,7 +15,7 @@ final class ContainerInterfacePrivateServiceRuleFakeTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(null))->create());
+		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration([])))->create());
 	}
 
 	public function testGetPrivateService(): void
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index b3a8fa31..c349b695 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Rules\Symfony;
 
 use PHPStan\Rules\Rule;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 
@@ -14,7 +15,7 @@ final class ContainerInterfacePrivateServiceRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create());
+		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create());
 	}
 
 	public function testGetPrivateService(): void
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index 33e3015d..46ea3b63 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -4,6 +4,7 @@
 
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Rules\Rule;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
@@ -16,7 +17,7 @@ final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(null))->create(), new Standard());
+		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), new Standard());
 	}
 
 	/**
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 297158a6..e433a233 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -4,6 +4,7 @@
 
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Rules\Rule;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 
@@ -15,7 +16,7 @@ final class ContainerInterfaceUnknownServiceRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create(), new Standard());
+		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), new Standard());
 	}
 
 	public function testGetPrivateService(): void
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
index b3f9e56e..95f4b733 100644
--- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -4,6 +4,7 @@
 
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Rules\Rule;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Testing\RuleTestCase;
 
@@ -15,7 +16,7 @@ final class UndefinedArgumentRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new UndefinedArgumentRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard());
+		return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard());
 	}
 
 	public function testGetArgument(): void
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
index 32ebe773..b3cbe0ef 100644
--- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -4,6 +4,7 @@
 
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Rules\Rule;
+use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Testing\RuleTestCase;
 
@@ -15,7 +16,7 @@ final class UndefinedOptionRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new UndefinedOptionRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard());
+		return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard());
 	}
 
 	public function testGetArgument(): void
diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php
index 0b97d078..caab6f97 100644
--- a/tests/Symfony/DefaultParameterMapTest.php
+++ b/tests/Symfony/DefaultParameterMapTest.php
@@ -13,13 +13,13 @@ final class DefaultParameterMapTest extends TestCase
 	 */
 	public function testGetParameter(string $key, callable $validator): void
 	{
-		$factory = new XmlParameterMapFactory(__DIR__ . '/container.xml');
+		$factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml']));
 		$validator($factory->create()->getParameter($key));
 	}
 
 	public function testGetParameterEscapedPath(): void
 	{
-		$factory = new XmlParameterMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml');
+		$factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml']));
 		$serviceMap = $factory->create();
 
 		self::assertNotNull($serviceMap->getParameter('app.string'));
diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php
index 8616ffaf..0ffbb457 100644
--- a/tests/Symfony/DefaultServiceMapTest.php
+++ b/tests/Symfony/DefaultServiceMapTest.php
@@ -13,13 +13,13 @@ final class DefaultServiceMapTest extends TestCase
 	 */
 	public function testGetService(string $id, callable $validator): void
 	{
-		$factory = new XmlServiceMapFactory(__DIR__ . '/container.xml');
+		$factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml']));
 		$validator($factory->create()->getService($id));
 	}
 
 	public function testGetContainerEscapedPath(): void
 	{
-		$factory = new XmlServiceMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml');
+		$factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml']));
 		$serviceMap = $factory->create();
 
 		self::assertNotNull($serviceMap->getService('withClass'));

From 5e9fc30ca533ba06886c0e08bf84171e063bf163 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 9 Jan 2022 13:18:25 +0100
Subject: [PATCH 132/297] Require PHPStan 1.4

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 3cddfd1b..001c0f78 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.1 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.0"
+    "phpstan/phpstan": "^1.4"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From d779d1a5a1678827e56010b47dedf790a0a4baad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= <looky.msc@gmail.com>
Date: Mon, 10 Jan 2022 16:59:52 +0100
Subject: [PATCH 133/297] Better error for invalid consoleApplicationLoader
 setting

---
 src/Symfony/ConsoleApplicationResolver.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 3efc0d3f..efee6292 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -9,6 +9,7 @@
 use function file_exists;
 use function get_class;
 use function is_readable;
+use function sprintf;
 
 final class ConsoleApplicationResolver
 {
@@ -34,7 +35,7 @@ private function loadConsoleApplication(string $consoleApplicationLoader)
 		if (!file_exists($consoleApplicationLoader)
 			|| !is_readable($consoleApplicationLoader)
 		) {
-			throw new ShouldNotHappenException();
+			throw new ShouldNotHappenException(sprintf('Cannot load console application. Check the parameters.symfony.consoleApplicationLoader setting in PHPStan\'s config. The offending value is "%s".', $consoleApplicationLoader));
 		}
 
 		return require $consoleApplicationLoader;

From 99a1dc953f818e9939de37f4398f8b3be0baf9dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Sun, 16 Jan 2022 09:43:24 +0100
Subject: [PATCH 134/297] Tweet release action

---
 .github/workflows/release-tweet.yml | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 .github/workflows/release-tweet.yml

diff --git a/.github/workflows/release-tweet.yml b/.github/workflows/release-tweet.yml
new file mode 100644
index 00000000..09b39ded
--- /dev/null
+++ b/.github/workflows/release-tweet.yml
@@ -0,0 +1,24 @@
+name: Tweet release
+
+# More triggers
+# https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#release
+on:
+  release:
+    types: [published]
+
+jobs:
+  tweet:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: Eomm/why-don-t-you-tweet@v1
+        if: ${{ !github.event.repository.private }}
+        with:
+          # GitHub event payload
+          # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
+          tweet-message: "New release: ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} ${{ github.event.release.html_url }} #phpstan"
+        env:
+          # Get your tokens from https://developer.twitter.com/apps
+          TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
+          TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
+          TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
+          TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}

From 012305dab7a08f4e1a7158e01d7b4bccfbc6fa31 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 16 Jan 2022 09:47:42 +0100
Subject: [PATCH 135/297] Request: Return Session instead of SessionInterface

See https://github.com/phpstan/phpstan-symfony/pull/233
---
 stubs/Symfony/Component/HttpFoundation/Request.stub  | 5 +++++
 tests/Type/Symfony/data/request_get_session.php      | 6 +++---
 tests/Type/Symfony/data/request_get_session_null.php | 6 +++---
 3 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub
index 00d03b09..f32365db 100644
--- a/stubs/Symfony/Component/HttpFoundation/Request.stub
+++ b/stubs/Symfony/Component/HttpFoundation/Request.stub
@@ -26,4 +26,9 @@ class Request
 	 */
 	public $cookies;
 
+    /**
+     * @return \Symfony\Component\HttpFoundation\Session\Session
+     */
+    public function getSession(): SessionInterface;
+
 }
diff --git a/tests/Type/Symfony/data/request_get_session.php b/tests/Type/Symfony/data/request_get_session.php
index 9a0335e5..17ef88a6 100644
--- a/tests/Type/Symfony/data/request_get_session.php
+++ b/tests/Type/Symfony/data/request_get_session.php
@@ -1,14 +1,14 @@
 <?php declare(strict_types = 1);
 
-use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use Symfony\Component\HttpFoundation\Session\Session;
 use function PHPStan\Testing\assertType;
 
 /** @var \Symfony\Component\HttpFoundation\Request $request */
 $request = doRequest();
 
 $session1 = $request->getSession();
-assertType(SessionInterface::class, $request->getSession());
+assertType(Session::class, $request->getSession());
 
 if ($request->hasSession()) {
-	assertType(SessionInterface::class, $request->getSession());
+	assertType(Session::class, $request->getSession());
 }
diff --git a/tests/Type/Symfony/data/request_get_session_null.php b/tests/Type/Symfony/data/request_get_session_null.php
index 9c37979d..be0fd0d1 100644
--- a/tests/Type/Symfony/data/request_get_session_null.php
+++ b/tests/Type/Symfony/data/request_get_session_null.php
@@ -1,14 +1,14 @@
 <?php declare(strict_types = 1);
 
-use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use Symfony\Component\HttpFoundation\Session\Session;
 use function PHPStan\Testing\assertType;
 
 /** @var \Symfony\Component\HttpFoundation\Request $request */
 $request = doRequest();
 
 $session1 = $request->getSession();
-assertType(SessionInterface::class . '|null', $request->getSession());
+assertType(Session::class . '|null', $request->getSession());
 
 if ($request->hasSession()) {
-	assertType(SessionInterface::class, $request->getSession());
+	assertType(Session::class, $request->getSession());
 }

From 78565072185b9e507ab2af35115ae24bff3dcf20 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 16 Jan 2022 09:57:02 +0100
Subject: [PATCH 136/297] Fix

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 001c0f78..52ec4121 100644
--- a/composer.json
+++ b/composer.json
@@ -30,7 +30,7 @@
     "symfony/console": "^4.0 || ^5.0",
     "symfony/form": "^4.0 || ^5.0",
     "symfony/framework-bundle": "^4.4 || ^5.0",
-    "symfony/http-foundation": "^4.0 || ^5.0",
+    "symfony/http-foundation": "^5.1",
     "symfony/messenger": "^4.2 || ^5.0",
     "symfony/serializer": "^4.0 || ^5.0"
   },

From 28c137977468bfc750d6796480fc1629f860088a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sun, 16 Jan 2022 10:00:07 +0100
Subject: [PATCH 137/297] PHP 8.0 polyfills for testing purposes

---
 composer.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/composer.json b/composer.json
index 52ec4121..08f5b685 100644
--- a/composer.json
+++ b/composer.json
@@ -32,6 +32,7 @@
     "symfony/framework-bundle": "^4.4 || ^5.0",
     "symfony/http-foundation": "^5.1",
     "symfony/messenger": "^4.2 || ^5.0",
+    "symfony/polyfill-php80": "^1.24",
     "symfony/serializer": "^4.0 || ^5.0"
   },
   "config": {

From 2e25964784fb657dbd1723691ffe6d5bc0e195a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Sun, 16 Jan 2022 10:09:48 +0100
Subject: [PATCH 138/297] Update release.yml

---
 .github/workflows/release.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 225470a6..5ed11761 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,13 +20,13 @@ jobs:
         id: changelog
         uses: metcalfc/changelog-generator@v1.0.0
         with:
-          myToken: ${{ secrets.GITHUB_TOKEN }}
+          myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 
       - name: "Create release"
         id: create-release
         uses: actions/create-release@v1
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{ secrets.PHPSTAN_BOT_TOKEN }}
         with:
           tag_name: ${{ github.ref }}
           release_name: ${{ github.ref }}

From 96ee630098c49c1c25326ae51b027e469bfd2064 Mon Sep 17 00:00:00 2001
From: Mark Olree <55830693+YspMark@users.noreply.github.com>
Date: Tue, 18 Jan 2022 17:08:22 +0100
Subject: [PATCH 139/297] Added CacheInterface stub

---
 extension.neon                                |  4 ++++
 stubs/Psr/Cache/CacheItemInterface.stub       |  7 ++++++
 .../Contracts/Cache/CacheInterface.stub       | 15 +++++++++++++
 .../Contracts/Cache/CallbackInterface.stub    | 16 ++++++++++++++
 .../Contracts/Cache/ItemInterface.stub        |  9 ++++++++
 tests/Type/Symfony/ExtensionTest.php          |  2 ++
 tests/Type/Symfony/data/cache.php             | 22 +++++++++++++++++++
 7 files changed, 75 insertions(+)
 create mode 100644 stubs/Psr/Cache/CacheItemInterface.stub
 create mode 100644 stubs/Symfony/Contracts/Cache/CacheInterface.stub
 create mode 100644 stubs/Symfony/Contracts/Cache/CallbackInterface.stub
 create mode 100644 stubs/Symfony/Contracts/Cache/ItemInterface.stub
 create mode 100644 tests/Type/Symfony/data/cache.php

diff --git a/extension.neon b/extension.neon
index 23008e5d..4870f24c 100644
--- a/extension.neon
+++ b/extension.neon
@@ -12,6 +12,7 @@ parameters:
 		console_application_loader: null
 		consoleApplicationLoader: null
 	stubFiles:
+		- stubs/Psr/Cache/CacheItemInterface.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
@@ -51,6 +52,9 @@ parameters:
 		- stubs/Symfony/Component/Validator/Constraint.stub
 		- stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
 		- stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
+		- stubs/Symfony/Contracts/Cache/CacheInterface.stub
+		- stubs/Symfony/Contracts/Cache/CallbackInterface.stub
+		- stubs/Symfony/Contracts/Cache/ItemInterface.stub
 		- stubs/Twig/Node/Node.stub
 
 parametersSchema:
diff --git a/stubs/Psr/Cache/CacheItemInterface.stub b/stubs/Psr/Cache/CacheItemInterface.stub
new file mode 100644
index 00000000..f595213d
--- /dev/null
+++ b/stubs/Psr/Cache/CacheItemInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Psr\Cache;
+
+interface CacheItemInterface
+{
+}
diff --git a/stubs/Symfony/Contracts/Cache/CacheInterface.stub b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
new file mode 100644
index 00000000..4b98f533
--- /dev/null
+++ b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
@@ -0,0 +1,15 @@
+<?php
+
+namespace Symfony\Contracts\Cache;
+
+interface CacheInterface
+{
+    /**
+     * @template T
+     *
+     * @param \Symfony\Contracts\Cache\CallbackInterface<T>|callable(\Symfony\Contracts\Cache\ItemInterface): T $callback
+     * @param array<mixed> $metadata
+     * @return T
+     */
+    public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);
+}
diff --git a/stubs/Symfony/Contracts/Cache/CallbackInterface.stub b/stubs/Symfony/Contracts/Cache/CallbackInterface.stub
new file mode 100644
index 00000000..9b5a6e1a
--- /dev/null
+++ b/stubs/Symfony/Contracts/Cache/CallbackInterface.stub
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Contracts\Cache;
+
+use Psr\Cache\CacheItemInterface;
+
+/**
+ * @template T
+ */
+interface CallbackInterface
+{
+    /**
+     * @return T
+     */
+    public function __invoke(CacheItemInterface $item, bool &$save);
+}
diff --git a/stubs/Symfony/Contracts/Cache/ItemInterface.stub b/stubs/Symfony/Contracts/Cache/ItemInterface.stub
new file mode 100644
index 00000000..cb3982bb
--- /dev/null
+++ b/stubs/Symfony/Contracts/Cache/ItemInterface.stub
@@ -0,0 +1,9 @@
+<?php
+
+namespace Symfony\Contracts\Cache;
+
+use Psr\Cache\CacheItemInterface;
+
+interface ItemInterface extends CacheItemInterface
+{
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 61d354da..5da566b1 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -20,6 +20,7 @@ public function dataFileAsserts(): iterable
 		}
 
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/tree_builder.php');
+
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleBaseCommand.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionCommand.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionLazyCommand.php');
@@ -51,6 +52,7 @@ public function dataFileAsserts(): iterable
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php');
 
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/cache.php');
 	}
 
 	/**
diff --git a/tests/Type/Symfony/data/cache.php b/tests/Type/Symfony/data/cache.php
new file mode 100644
index 00000000..6b0728d4
--- /dev/null
+++ b/tests/Type/Symfony/data/cache.php
@@ -0,0 +1,22 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony\cache;
+
+use function PHPStan\Testing\assertType;
+
+function testCacheCallable(\Symfony\Contracts\Cache\CacheInterface  $cache): void {
+	$result = $cache->get('foo', function (): string {
+		return '';
+	});
+
+	assertType('string', $result);
+};
+
+/**
+ * @param \Symfony\Contracts\Cache\CallbackInterface<\stdClass> $cb
+ */
+ function testCacheCallbackInterface(\Symfony\Contracts\Cache\CacheInterface  $cache, \Symfony\Contracts\Cache\CallbackInterface $cb): void {
+	$result = $cache->get('foo',$cb);
+
+	assertType('stdClass', $result);
+};

From b336d0aef4fdc4db6c44c455298d1130e1bb3df6 Mon Sep 17 00:00:00 2001
From: Thomas Gnandt <thomas.gnandt@mayflower.de>
Date: Mon, 24 Jan 2022 12:27:55 +0100
Subject: [PATCH 140/297] Add missing save parameter to CacheInterface stub

---
 stubs/Symfony/Contracts/Cache/CacheInterface.stub | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stubs/Symfony/Contracts/Cache/CacheInterface.stub b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
index 4b98f533..f4cc208d 100644
--- a/stubs/Symfony/Contracts/Cache/CacheInterface.stub
+++ b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
@@ -7,7 +7,7 @@ interface CacheInterface
     /**
      * @template T
      *
-     * @param \Symfony\Contracts\Cache\CallbackInterface<T>|callable(\Symfony\Contracts\Cache\ItemInterface): T $callback
+     * @param \Symfony\Contracts\Cache\CallbackInterface<T>|callable(\Symfony\Contracts\Cache\ItemInterface, bool): T $callback
      * @param array<mixed> $metadata
      * @return T
      */

From 784e7a06dcfadff7f79ec402402ee72e090245a8 Mon Sep 17 00:00:00 2001
From: Tomas Votruba <tomas.vot@gmail.com>
Date: Sun, 30 Jan 2022 18:04:31 +0100
Subject: [PATCH 141/297] Update phpunit.xml

---
 phpunit.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/phpunit.xml b/phpunit.xml
index f9f3afe2..2e2f6167 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -10,7 +10,7 @@
          beStrictAboutTodoAnnotatedTests="true"
          failOnRisky="true"
          failOnWarning="true"
-         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
+         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xml"
 >
     <coverage>
         <include>

From 11221afa283a3fa679a1678449fed0f5a81926b9 Mon Sep 17 00:00:00 2001
From: Fran Moreno <franmomu@gmail.com>
Date: Tue, 1 Feb 2022 13:17:17 +0100
Subject: [PATCH 142/297] Add dynamic return type to
 ResponseHeaderBag::getCookies

---
 extension.neon                                |  5 ++
 ...nseHeaderBagDynamicReturnTypeExtension.php | 65 +++++++++++++++++++
 tests/Type/Symfony/ExtensionTest.php          |  1 +
 .../data/response_header_bag_get_cookies.php  | 12 ++++
 4 files changed, 83 insertions(+)
 create mode 100644 src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php
 create mode 100644 tests/Type/Symfony/data/response_header_bag_get_cookies.php

diff --git a/extension.neon b/extension.neon
index 4870f24c..cd18f378 100644
--- a/extension.neon
+++ b/extension.neon
@@ -273,3 +273,8 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\CommandGetHelperDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# ResponseHeaderBag::getCookies() return type
+	-
+		factory: PHPStan\Type\Symfony\ResponseHeaderBagDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..a659c684
--- /dev/null
+++ b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php
@@ -0,0 +1,65 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\ClassConstFetch;
+use PhpParser\Node\Expr\MethodCall;
+use PhpParser\Node\Identifier;
+use PhpParser\Node\Name;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Type\ArrayType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\IntegerType;
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\StringType;
+use PHPStan\Type\Type;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+
+final class ResponseHeaderBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	public function getClass(): string
+	{
+		return ResponseHeaderBag::class;
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getCookies';
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): Type
+	{
+		if (isset($methodCall->getArgs()[0])) {
+			$node = $methodCall->getArgs()[0]->value;
+
+			if (
+				$node instanceof ClassConstFetch &&
+				$node->class instanceof Name &&
+				$node->name instanceof Identifier &&
+				$node->class->toString() === ResponseHeaderBag::class &&
+				$node->name->name === 'COOKIES_ARRAY'
+			) {
+				return new ArrayType(
+					new StringType(),
+					new ArrayType(
+						new StringType(),
+						new ArrayType(
+							new StringType(),
+							new ObjectType(Cookie::class)
+						)
+					)
+				);
+			}
+		}
+
+		return new ArrayType(new IntegerType(), new ObjectType(Cookie::class));
+	}
+
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 5da566b1..c0aa7f94 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -14,6 +14,7 @@ public function dataFileAsserts(): iterable
 	{
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php');
 
 		if (class_exists('Symfony\Component\HttpFoundation\InputBag')) {
 			yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag.php');
diff --git a/tests/Type/Symfony/data/response_header_bag_get_cookies.php b/tests/Type/Symfony/data/response_header_bag_get_cookies.php
new file mode 100644
index 00000000..f07d5e5b
--- /dev/null
+++ b/tests/Type/Symfony/data/response_header_bag_get_cookies.php
@@ -0,0 +1,12 @@
+<?php declare(strict_types = 1);
+
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use function PHPStan\Testing\assertType;
+
+$headerBag = new ResponseHeaderBag();
+$headerBag->setCookie(Cookie::create('cookie_name'));
+
+assertType('array<int, Symfony\Component\HttpFoundation\Cookie>', $headerBag->getCookies());
+assertType('array<int, Symfony\Component\HttpFoundation\Cookie>', $headerBag->getCookies(ResponseHeaderBag::COOKIES_FLAT));
+assertType('array<string, array<string, array<string, Symfony\Component\HttpFoundation\Cookie>>>', $headerBag->getCookies(ResponseHeaderBag::COOKIES_ARRAY));

From 26a44d1e960a1d2a70621e2efe6ae7b7fb5ea7bc Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Tue, 1 Feb 2022 23:53:55 +0000
Subject: [PATCH 143/297] Add renovate.json

---
 renovate.json | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 renovate.json

diff --git a/renovate.json b/renovate.json
new file mode 100644
index 00000000..f45d8f11
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,5 @@
+{
+  "extends": [
+    "config:base"
+  ]
+}

From 308ff0a9d33c98a50f43fa5ee3e099637ea7b755 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Wed, 2 Feb 2022 11:21:08 +0100
Subject: [PATCH 144/297] Update renovate.json

---
 renovate.json | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/renovate.json b/renovate.json
index f45d8f11..597ad920 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,5 +1,23 @@
 {
   "extends": [
     "config:base"
+  ],
+  "rangeStrategy": "update-lockfile",
+  "packageRules": [
+    {
+      "matchPaths": ["+(composer.json)"],
+      "enabled": true,
+      "groupName": "root-composer"
+    },
+    {
+      "matchPaths": ["build-cs/**"],
+      "enabled": true,
+      "groupName": "build-cs"
+    },
+    {
+      "matchPaths": [".github/**"],
+      "enabled": true,
+      "groupName": "github-actions"
+    }
   ]
 }

From 35dcbe9bbb36b5d82540e1da7b1cb12e2b757bbe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Wed, 2 Feb 2022 11:21:38 +0100
Subject: [PATCH 145/297] Rename renovate.json to .github/renovate.json

---
 renovate.json => .github/renovate.json | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename renovate.json => .github/renovate.json (100%)

diff --git a/renovate.json b/.github/renovate.json
similarity index 100%
rename from renovate.json
rename to .github/renovate.json

From b31fa2ea20d5b91fad600fc6ae5c8936f702deb6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Wed, 2 Feb 2022 11:21:47 +0100
Subject: [PATCH 146/297] Delete dependabot.yml

---
 .github/dependabot.yml | 17 -----------------
 1 file changed, 17 deletions(-)
 delete mode 100644 .github/dependabot.yml

diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 15b77335..00000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-version: 2
-updates:
-  - package-ecosystem: composer
-    directory: "/"
-    schedule:
-      interval: monthly
-    open-pull-requests-limit: 10
-  - package-ecosystem: composer
-    directory: "/build-cs"
-    schedule:
-      interval: monthly
-    open-pull-requests-limit: 10
-  - package-ecosystem: github-actions
-    directory: "/"
-    schedule:
-      interval: monthly
-    open-pull-requests-limit: 10

From 1cb815f2cb6b49bec5ec04a8e9077c583aefac4b Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Wed, 2 Feb 2022 10:21:48 +0000
Subject: [PATCH 147/297] Update metcalfc/changelog-generator action to v1.0.1

---
 .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 5ed11761..24a1d301 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@v1.0.0
+        uses: metcalfc/changelog-generator@v1.0.1
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From ae9d31e6544dec1e91e821cef0d76a7a01dac368 Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Wed, 2 Feb 2022 12:44:59 +0000
Subject: [PATCH 148/297] Update github-actions

---
 .github/workflows/lock-closed-issues.yml | 2 +-
 .github/workflows/release.yml            | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml
index 960c1ba5..9ea53272 100644
--- a/.github/workflows/lock-closed-issues.yml
+++ b/.github/workflows/lock-closed-issues.yml
@@ -8,7 +8,7 @@ jobs:
   lock:
     runs-on: ubuntu-latest
     steps:
-      - uses: dessant/lock-threads@v2
+      - uses: dessant/lock-threads@v3
         with:
           github-token: ${{ github.token }}
           issue-lock-inactive-days: '31'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 24a1d301..0ebed840 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@v1.0.1
+        uses: metcalfc/changelog-generator@v3.0.0
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From a3fe4eb01117b23a9b9ea4e987f1fc3e82ebf9cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Wed, 2 Feb 2022 15:43:18 +0100
Subject: [PATCH 149/297] Update lock-closed-issues.yml

---
 .github/workflows/lock-closed-issues.yml | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml
index 9ea53272..a05d4173 100644
--- a/.github/workflows/lock-closed-issues.yml
+++ b/.github/workflows/lock-closed-issues.yml
@@ -11,11 +11,11 @@ jobs:
       - uses: dessant/lock-threads@v3
         with:
           github-token: ${{ github.token }}
-          issue-lock-inactive-days: '31'
-          issue-exclude-created-before: ''
-          issue-exclude-labels: ''
-          issue-lock-labels: ''
-          issue-lock-comment: >
+          issue-inactive-days: '31'
+          exclude-issue-created-before: ''
+          exclude-any-issue-labels: ''
+          add-issue-labels: ''
+          issue-comment: >
             This thread has been automatically locked since there has not been
             any recent activity after it was closed. Please open a new issue for
             related bugs.

From df5ce326a8935bf400eb59f7080e568c20ef6246 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Wed, 2 Feb 2022 16:35:49 +0100
Subject: [PATCH 150/297] Update renovate.json

---
 .github/renovate.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/renovate.json b/.github/renovate.json
index 597ad920..b775cc18 100644
--- a/.github/renovate.json
+++ b/.github/renovate.json
@@ -1,6 +1,7 @@
 {
   "extends": [
-    "config:base"
+    "config:base",
+    "schedule:weekly"
   ],
   "rangeStrategy": "update-lockfile",
   "packageRules": [

From 173610b6eac80bb0b63eee2b181bd6f67bb3aad8 Mon Sep 17 00:00:00 2001
From: Fran Moreno <franmomu@gmail.com>
Date: Wed, 2 Feb 2022 20:26:32 +0100
Subject: [PATCH 151/297] Specify type after calling InputBag::has

---
 extension.neon                                |  5 ++
 .../InputBagTypeSpecifyingExtension.php       | 68 +++++++++++++++++++
 tests/Type/Symfony/data/input_bag.php         |  6 ++
 3 files changed, 79 insertions(+)
 create mode 100644 src/Type/Symfony/InputBagTypeSpecifyingExtension.php

diff --git a/extension.neon b/extension.neon
index cd18f378..1aba591e 100644
--- a/extension.neon
+++ b/extension.neon
@@ -278,3 +278,8 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\ResponseHeaderBagDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# InputBag::get() type specification
+	-
+		factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension
+		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
new file mode 100644
index 00000000..bb3d5b28
--- /dev/null
+++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Analyser\SpecifiedTypes;
+use PHPStan\Analyser\TypeSpecifier;
+use PHPStan\Analyser\TypeSpecifierAwareExtension;
+use PHPStan\Analyser\TypeSpecifierContext;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\Reflection\ReflectionProvider;
+use PHPStan\Type\MethodTypeSpecifyingExtension;
+use PHPStan\Type\TypeCombinator;
+use Symfony\Component\HttpFoundation\InputBag;
+
+final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
+{
+
+	private const INPUT_BAG_CLASS = InputBag::class;
+	private const HAS_METHOD_NAME = 'has';
+	private const GET_METHOD_NAME = 'get';
+
+	/** @var ReflectionProvider */
+	private $reflectionProvider;
+
+	/** @var TypeSpecifier */
+	private $typeSpecifier;
+
+	public function __construct(ReflectionProvider $reflectionProvider)
+	{
+		$this->reflectionProvider = $reflectionProvider;
+	}
+
+	public function getClass(): string
+	{
+		return self::INPUT_BAG_CLASS;
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
+	{
+		return $methodReflection->getName() === self::HAS_METHOD_NAME && !$context->null();
+	}
+
+	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
+	{
+		$classReflection = $this->reflectionProvider->getClass(self::INPUT_BAG_CLASS);
+		$methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants();
+		$returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType();
+
+		if (!TypeCombinator::containsNull($returnType)) {
+			return new SpecifiedTypes();
+		}
+
+		return $this->typeSpecifier->create(
+			new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()),
+			TypeCombinator::removeNull($returnType),
+			$context
+		);
+	}
+
+	public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
+	{
+		$this->typeSpecifier = $typeSpecifier;
+	}
+
+}
diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php
index 4c836c5f..5420e789 100644
--- a/tests/Type/Symfony/data/input_bag.php
+++ b/tests/Type/Symfony/data/input_bag.php
@@ -5,6 +5,12 @@
 $bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar', 'bar' => ['x']]);
 
 assertType('bool|float|int|string|null', $bag->get('foo'));
+
+if ($bag->has('foo')) {
+	assertType('bool|float|int|string', $bag->get('foo'));
+	assertType('bool|float|int|string|null', $bag->get('bar'));
+}
+
 assertType('bool|float|int|string|null', $bag->get('foo', null));
 assertType('bool|float|int|string', $bag->get('foo', ''));
 assertType('bool|float|int|string', $bag->get('foo', 'baz'));

From 51488c9c3000a07db3e543bc6e8f2de9c0a9037d Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Thu, 3 Feb 2022 12:35:02 +0100
Subject: [PATCH 152/297] Add support for env processor

---
 .../ParameterDynamicReturnTypeExtension.php   | 51 ++++++++++++++++++-
 tests/Type/Symfony/container.xml              | 13 +++++
 .../data/ExampleAbstractController.php        | 11 ++++
 ...mpleAbstractControllerWithoutContainer.php | 15 ++++++
 tests/Type/Symfony/data/ExampleController.php | 15 ++++++
 .../ExampleControllerWithoutContainer.php     | 15 ++++++
 6 files changed, 118 insertions(+), 2 deletions(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 29b1a883..0de91de1 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -4,6 +4,7 @@
 
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
+use PHPStan\PhpDoc\TypeStringResolver;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\ShouldNotHappenException;
@@ -22,8 +23,10 @@
 use PHPStan\Type\NullType;
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
 use PHPStan\Type\TypeTraverser;
 use PHPStan\Type\UnionType;
+use Symfony\Component\DependencyInjection\EnvVarProcessor;
 use function in_array;
 
 final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -44,13 +47,24 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy
 	/** @var \PHPStan\Symfony\ParameterMap */
 	private $parameterMap;
 
-	public function __construct(string $className, ?string $methodGet, ?string $methodHas, Configuration $configuration, ParameterMap $symfonyParameterMap)
+	/** @var \PHPStan\PhpDoc\TypeStringResolver */
+	private $typeStringResolver;
+
+	public function __construct(
+		string $className,
+		?string $methodGet,
+		?string $methodHas,
+		Configuration $configuration,
+		ParameterMap $symfonyParameterMap,
+		TypeStringResolver $typeStringResolver
+	)
 	{
 		$this->className = $className;
 		$this->methodGet = $methodGet;
 		$this->methodHas = $methodHas;
 		$this->constantHassers = $configuration->hasConstantHassers();
 		$this->parameterMap = $symfonyParameterMap;
+		$this->typeStringResolver = $typeStringResolver;
 	}
 
 	public function getClass(): string
@@ -102,13 +116,46 @@ private function getGetTypeFromMethodCall(
 		if ($parameterKey !== null) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
 			if ($parameter !== null) {
-				return $this->generalizeType($scope->getTypeFromValue($parameter->getValue()));
+				return $this->generalizeTypeFromValue($scope, $parameter->getValue());
 			}
 		}
 
 		return $returnType;
 	}
 
+	/**
+	 * @param Scope								 $scope
+	 * @param array<mixed>|bool|float|int|string $value
+	 */
+	private function generalizeTypeFromValue(Scope $scope, $value): Type
+	{
+		if (is_array($value) && $value !== []) {
+			return $this->generalizeType(
+				new ArrayType(
+					TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
+						return $this->generalizeTypeFromValue($scope, $item);
+					}, array_keys($value))),
+					TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
+						return $this->generalizeTypeFromValue($scope, $item);
+					}, array_values($value)))
+				)
+			);
+		}
+
+		if (
+			class_exists(EnvVarProcessor::class)
+			&& is_string($value)
+			&& preg_match('/%env\((.*)\:.*\)%/U', $value, $matches) === 1
+			&& strlen($matches[0]) === strlen($value)
+		) {
+			$providedTypes = EnvVarProcessor::getProvidedTypes();
+
+			return $this->typeStringResolver->resolve($providedTypes[$matches[1]] ?? 'bool|int|float|string|array');
+		}
+
+		return $this->generalizeType($scope->getTypeFromValue($value));
+	}
+
 	private function generalizeType(Type $type): Type
 	{
 		return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index f4240c3b..e6f8f03b 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -4,15 +4,28 @@
         <parameter key="app.string">abcdef</parameter>
         <parameter key="app.int">123</parameter>
         <parameter key="app.int_as_string" type="string">123</parameter>
+        <parameter key="app.int_as_processor">%env(int:APP_INT)%</parameter>
         <parameter key="app.float">123.45</parameter>
         <parameter key="app.float_as_string" type="string">123.45</parameter>
+        <parameter key="app.float_as_processor">%env(float:APP_FLOAT)%</parameter>
         <parameter key="app.boolean">true</parameter>
         <parameter key="app.boolean_as_string" type="string">true</parameter>
+        <parameter key="app.boolean_as_processor">%env(bool:APP_BOOL)%</parameter>
         <parameter key="app.list" type="collection">
             <parameter>en</parameter>
             <parameter>es</parameter>
             <parameter>fr</parameter>
         </parameter>
+        <parameter key="app.list_of_int" type="collection">
+            <parameter>123</parameter>
+            <parameter>456</parameter>
+            <parameter>789</parameter>
+        </parameter>
+        <parameter key="app.list_of_int_as_processor" type="collection">
+            <parameter>%env(int:APP_INT)%</parameter>
+            <parameter>%env(int:APP_INT)%</parameter>
+            <parameter>%env(int:APP_INT)%</parameter>
+        </parameter>
         <parameter key="app.list_of_list" type="collection">
             <parameter type="collection">
                 <parameter key="name">the name</parameter>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index 4dbaffa7..6dc6ef7e 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -39,6 +39,9 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("string", $container->getParameter('app.int_as_string'));
 		assertType("string", $parameterBag->get('app.int_as_string'));
 		assertType("string", $this->getParameter('app.int_as_string'));
+		assertType('int', $container->getParameter('app.int_as_processor'));
+		assertType('int', $parameterBag->get('app.int_as_processor'));
+		assertType('int', $this->getParameter('app.int_as_processor'));
 		assertType('float', $container->getParameter('app.float'));
 		assertType('float', $parameterBag->get('app.float'));
 		assertType('float', $this->getParameter('app.float'));
@@ -54,6 +57,12 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("array<int, string>", $container->getParameter('app.list'));
 		assertType("array<int, string>", $parameterBag->get('app.list'));
 		assertType("array<int, string>", $this->getParameter('app.list'));
+		assertType("array<int, int>", $container->getParameter('app.list_of_int'));
+		assertType("array<int, int>", $parameterBag->get('app.list_of_int'));
+		assertType("array<int, int>", $this->getParameter('app.list_of_int'));
+		assertType("array<int, int>", $container->getParameter('app.list_of_int_as_processor'));
+		assertType("array<int, int>", $parameterBag->get('app.list_of_int_as_processor'));
+		assertType("array<int, int>", $this->getParameter('app.list_of_int_as_processor'));
 		assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
 		assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
 		assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
@@ -77,6 +86,8 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('true', $parameterBag->has('app.int'));
 		assertType('true', $container->hasParameter('app.int_as_string'));
 		assertType('true', $parameterBag->has('app.int_as_string'));
+		assertType('true', $container->hasParameter('app.int_as_processor'));
+		assertType('true', $parameterBag->has('app.int_as_processor'));
 		assertType('true', $container->hasParameter('app.float'));
 		assertType('true', $parameterBag->has('app.float'));
 		assertType('true', $container->hasParameter('app.float_as_string'));
diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
index 7ca06775..26d0f366 100644
--- a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
+++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
@@ -38,18 +38,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_processor'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_processor'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_processor'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.float'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.float'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_processor'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_processor'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_processor'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_processor'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_processor'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_processor'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.list'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.list'));
@@ -74,14 +83,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('bool', $parameterBag->has('app.int'));
 		assertType('bool', $container->hasParameter('app.int_as_string'));
 		assertType('bool', $parameterBag->has('app.int_as_string'));
+		assertType('bool', $container->hasParameter('app.int_as_processor'));
+		assertType('bool', $parameterBag->has('app.int_as_processor'));
 		assertType('bool', $container->hasParameter('app.float'));
 		assertType('bool', $parameterBag->has('app.float'));
 		assertType('bool', $container->hasParameter('app.float_as_string'));
 		assertType('bool', $parameterBag->has('app.float_as_string'));
+		assertType('bool', $container->hasParameter('app.float_as_processor'));
+		assertType('bool', $parameterBag->has('app.float_as_processor'));
 		assertType('bool', $container->hasParameter('app.boolean'));
 		assertType('bool', $parameterBag->has('app.boolean'));
 		assertType('bool', $container->hasParameter('app.boolean_as_string'));
 		assertType('bool', $parameterBag->has('app.boolean_as_string'));
+		assertType('bool', $container->hasParameter('app.boolean_as_processor'));
+		assertType('bool', $parameterBag->has('app.boolean_as_processor'));
 		assertType('bool', $container->hasParameter('app.list'));
 		assertType('bool', $parameterBag->has('app.list'));
 		assertType('bool', $container->hasParameter('app.list_of_list'));
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index 1fe8c983..8cd0c93a 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -39,18 +39,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("string", $container->getParameter('app.int_as_string'));
 		assertType("string", $parameterBag->get('app.int_as_string'));
 		assertType("string", $this->getParameter('app.int_as_string'));
+		assertType('int', $container->getParameter('app.int_as_processor'));
+		assertType('int', $parameterBag->get('app.int_as_processor'));
+		assertType('int', $this->getParameter('app.int_as_processor'));
 		assertType('float', $container->getParameter('app.float'));
 		assertType('float', $parameterBag->get('app.float'));
 		assertType('float', $this->getParameter('app.float'));
 		assertType("string", $container->getParameter('app.float_as_string'));
 		assertType("string", $parameterBag->get('app.float_as_string'));
 		assertType("string", $this->getParameter('app.float_as_string'));
+		assertType('float', $container->getParameter('app.float_as_processor'));
+		assertType('float', $parameterBag->get('app.float_as_processor'));
+		assertType('float', $this->getParameter('app.float_as_processor'));
 		assertType('bool', $container->getParameter('app.boolean'));
 		assertType('bool', $parameterBag->get('app.boolean'));
 		assertType('bool', $this->getParameter('app.boolean'));
 		assertType("string", $container->getParameter('app.boolean_as_string'));
 		assertType("string", $parameterBag->get('app.boolean_as_string'));
 		assertType("string", $this->getParameter('app.boolean_as_string'));
+		assertType('bool', $container->getParameter('app.boolean_as_processor'));
+		assertType('bool', $parameterBag->get('app.boolean_as_processor'));
+		assertType('bool', $this->getParameter('app.boolean_as_processor'));
 		assertType("array<int, string>", $container->getParameter('app.list'));
 		assertType("array<int, string>", $parameterBag->get('app.list'));
 		assertType("array<int, string>", $this->getParameter('app.list'));
@@ -75,14 +84,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('true', $parameterBag->has('app.int'));
 		assertType('true', $container->hasParameter('app.int_as_string'));
 		assertType('true', $parameterBag->has('app.int_as_string'));
+		assertType('true', $container->hasParameter('app.int_as_processor'));
+		assertType('true', $parameterBag->has('app.int_as_processor'));
 		assertType('true', $container->hasParameter('app.float'));
 		assertType('true', $parameterBag->has('app.float'));
 		assertType('true', $container->hasParameter('app.float_as_string'));
 		assertType('true', $parameterBag->has('app.float_as_string'));
+		assertType('true', $container->hasParameter('app.float_as_processor'));
+		assertType('true', $parameterBag->has('app.float_as_processor'));
 		assertType('true', $container->hasParameter('app.boolean'));
 		assertType('true', $parameterBag->has('app.boolean'));
 		assertType('true', $container->hasParameter('app.boolean_as_string'));
 		assertType('true', $parameterBag->has('app.boolean_as_string'));
+		assertType('true', $container->hasParameter('app.boolean_as_processor'));
+		assertType('true', $parameterBag->has('app.boolean_as_processor'));
 		assertType('true', $container->hasParameter('app.list'));
 		assertType('true', $parameterBag->has('app.list'));
 		assertType('true', $container->hasParameter('app.list_of_list'));
diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
index 2e9b7ba2..fc48c0e5 100644
--- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
+++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
@@ -38,18 +38,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_processor'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_processor'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_processor'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.float'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.float'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_processor'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_processor'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_processor'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_processor'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_processor'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_processor'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.list'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.list'));
@@ -74,14 +83,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('bool', $parameterBag->has('app.int'));
 		assertType('bool', $container->hasParameter('app.int_as_string'));
 		assertType('bool', $parameterBag->has('app.int_as_string'));
+		assertType('bool', $container->hasParameter('app.int_as_processor'));
+		assertType('bool', $parameterBag->has('app.int_as_processor'));
 		assertType('bool', $container->hasParameter('app.float'));
 		assertType('bool', $parameterBag->has('app.float'));
 		assertType('bool', $container->hasParameter('app.float_as_string'));
 		assertType('bool', $parameterBag->has('app.float_as_string'));
+		assertType('bool', $container->hasParameter('app.float_as_processor'));
+		assertType('bool', $parameterBag->has('app.float_as_processor'));
 		assertType('bool', $container->hasParameter('app.boolean'));
 		assertType('bool', $parameterBag->has('app.boolean'));
 		assertType('bool', $container->hasParameter('app.boolean_as_string'));
 		assertType('bool', $parameterBag->has('app.boolean_as_string'));
+		assertType('bool', $container->hasParameter('app.boolean_as_processor'));
+		assertType('bool', $parameterBag->has('app.boolean_as_processor'));
 		assertType('bool', $container->hasParameter('app.list'));
 		assertType('bool', $parameterBag->has('app.list'));
 		assertType('bool', $container->hasParameter('app.list_of_list'));

From 4e20d2e6b65a6eb2d0b8ff285c6c8c049faff03d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 5 Feb 2022 12:34:02 +0100
Subject: [PATCH 153/297] Forward compatibility fix for extensions of generic
 methods

---
 .../InputBagTypeSpecifyingExtension.php       | 24 +++----------------
 .../RequestTypeSpecifyingExtension.php        | 14 ++---------
 tests/Type/Symfony/data/input_bag.php         |  3 +++
 3 files changed, 8 insertions(+), 33 deletions(-)

diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
index bb3d5b28..d3ad578c 100644
--- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
@@ -9,10 +9,8 @@
 use PHPStan\Analyser\TypeSpecifierAwareExtension;
 use PHPStan\Analyser\TypeSpecifierContext;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
-use PHPStan\Reflection\ReflectionProvider;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
-use PHPStan\Type\TypeCombinator;
+use PHPStan\Type\NullType;
 use Symfony\Component\HttpFoundation\InputBag;
 
 final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
@@ -22,17 +20,9 @@ final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExten
 	private const HAS_METHOD_NAME = 'has';
 	private const GET_METHOD_NAME = 'get';
 
-	/** @var ReflectionProvider */
-	private $reflectionProvider;
-
 	/** @var TypeSpecifier */
 	private $typeSpecifier;
 
-	public function __construct(ReflectionProvider $reflectionProvider)
-	{
-		$this->reflectionProvider = $reflectionProvider;
-	}
-
 	public function getClass(): string
 	{
 		return self::INPUT_BAG_CLASS;
@@ -45,18 +35,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall
 
 	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
 	{
-		$classReflection = $this->reflectionProvider->getClass(self::INPUT_BAG_CLASS);
-		$methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants();
-		$returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType();
-
-		if (!TypeCombinator::containsNull($returnType)) {
-			return new SpecifiedTypes();
-		}
-
 		return $this->typeSpecifier->create(
 			new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()),
-			TypeCombinator::removeNull($returnType),
-			$context
+			new NullType(),
+			$context->negate()
 		);
 	}
 
diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
index 66372a7b..ffb2e8f3 100644
--- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
@@ -8,7 +8,6 @@
 use PHPStan\Analyser\TypeSpecifier;
 use PHPStan\Analyser\TypeSpecifierAwareExtension;
 use PHPStan\Analyser\TypeSpecifierContext;
-use PHPStan\Broker\Broker;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
@@ -21,17 +20,9 @@ final class RequestTypeSpecifyingExtension implements MethodTypeSpecifyingExtens
 	private const HAS_METHOD_NAME = 'hasSession';
 	private const GET_METHOD_NAME = 'getSession';
 
-	/** @var Broker */
-	private $broker;
-
 	/** @var TypeSpecifier */
 	private $typeSpecifier;
 
-	public function __construct(Broker $broker)
-	{
-		$this->broker = $broker;
-	}
-
 	public function getClass(): string
 	{
 		return self::REQUEST_CLASS;
@@ -44,9 +35,8 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall
 
 	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
 	{
-		$classReflection = $this->broker->getClass(self::REQUEST_CLASS);
-		$methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants();
-		$returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType();
+		$methodVariants = $methodReflection->getDeclaringClass()->getNativeMethod(self::GET_METHOD_NAME)->getVariants();
+		$returnType = ParametersAcceptorSelector::selectFromArgs($scope, $node->getArgs(), $methodVariants)->getReturnType();
 
 		if (!TypeCombinator::containsNull($returnType)) {
 			return new SpecifiedTypes();
diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php
index 5420e789..efd08461 100644
--- a/tests/Type/Symfony/data/input_bag.php
+++ b/tests/Type/Symfony/data/input_bag.php
@@ -9,6 +9,9 @@
 if ($bag->has('foo')) {
 	assertType('bool|float|int|string', $bag->get('foo'));
 	assertType('bool|float|int|string|null', $bag->get('bar'));
+} else {
+	assertType('null', $bag->get('foo'));
+	assertType('bool|float|int|string|null', $bag->get('bar'));
 }
 
 assertType('bool|float|int|string|null', $bag->get('foo', null));

From 463eac060bff830c6d86e4f6f8b4cd6bc6c02820 Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Wed, 2 Feb 2022 10:21:52 +0000
Subject: [PATCH 154/297] Update dependency slevomat/coding-standard to v7

---
 .gitignore                                    |   2 +-
 build-cs/.gitignore                           |   1 -
 build-cs/composer.json                        |   2 +-
 build-cs/composer.lock                        | 327 ++++++++++++++++++
 phpcs.xml                                     | 172 +++++----
 .../ContainerInterfacePrivateServiceRule.php  |  10 +-
 .../ContainerInterfaceUnknownServiceRule.php  |  13 +-
 .../InvalidArgumentDefaultValueRule.php       |  11 +-
 .../Symfony/InvalidOptionDefaultValueRule.php |  11 +-
 src/Rules/Symfony/UndefinedArgumentRule.php   |  14 +-
 src/Rules/Symfony/UndefinedOptionRule.php     |  14 +-
 src/Symfony/ConsoleApplicationResolver.php    |   8 +-
 src/Symfony/DefaultParameterMap.php           |   6 +-
 src/Symfony/DefaultServiceMap.php             |   6 +-
 src/Symfony/FakeParameterMap.php              |   2 +-
 src/Symfony/FakeServiceMap.php                |   2 +-
 src/Symfony/InputBagStubFilesExtension.php    |   1 +
 src/Symfony/Parameter.php                     |   1 -
 src/Symfony/ParameterMap.php                  |   2 +-
 src/Symfony/ServiceMap.php                    |   2 +-
 .../XmlContainerNotExistsException.php        |   4 +-
 src/Symfony/XmlParameterMapFactory.php        |  19 +-
 src/Symfony/XmlServiceMapFactory.php          |   8 +-
 .../ArgumentTypeSpecifyingExtension.php       |   4 +-
 ...andGetHelperDynamicReturnTypeExtension.php |   2 +-
 ...ionPrototypeDynamicReturnTypeExtension.php |   2 +
 ...ParentObjectDynamicReturnTypeExtension.php |   2 +-
 ...ReturnParentDynamicReturnTypeExtension.php |   2 +-
 .../TreeBuilderDynamicReturnTypeExtension.php |   4 +-
 .../Symfony/EnvelopeReturnTypeExtension.php   |   1 +
 .../InputBagDynamicReturnTypeExtension.php    |   1 +
 ...eGetArgumentDynamicReturnTypeExtension.php |   2 +-
 ...aceGetOptionDynamicReturnTypeExtension.php |   2 +-
 ...eHasArgumentDynamicReturnTypeExtension.php |   2 +-
 ...aceHasOptionDynamicReturnTypeExtension.php |   2 +-
 .../Symfony/OptionTypeSpecifyingExtension.php |   4 +-
 .../ParameterDynamicReturnTypeExtension.php   |  17 +-
 .../SerializerDynamicReturnTypeExtension.php  |   1 +
 .../ServiceDynamicReturnTypeExtension.php     |   2 +-
 .../ServiceTypeSpecifyingExtension.php        |   4 +-
 tests/Rules/NonexistentInputBagClassTest.php  |   2 +-
 ...nerInterfacePrivateServiceRuleFakeTest.php |   2 +
 ...ntainerInterfacePrivateServiceRuleTest.php |   2 +
 ...nerInterfaceUnknownServiceRuleFakeTest.php |   4 +-
 ...ntainerInterfaceUnknownServiceRuleTest.php |   2 +
 .../Symfony/ExampleAbstractController.php     |   3 +-
 tests/Rules/Symfony/ExampleController.php     |   3 +-
 .../Symfony/ExampleServiceSubscriber.php      |   3 +-
 tests/Symfony/DefaultParameterMapTest.php     |  28 +-
 tests/Symfony/DefaultServiceMapTest.php       |  18 +-
 tests/Type/Symfony/ExtensionTest.php          |   4 +-
 .../Symfony/ExtensionTestWithoutContainer.php |   3 +-
 .../ImpossibleCheckTypeMethodCallRuleTest.php |   5 +-
 .../Symfony/console_application_loader.php    |   2 +-
 54 files changed, 576 insertions(+), 197 deletions(-)
 create mode 100644 build-cs/composer.lock

diff --git a/.gitignore b/.gitignore
index d6a83e59..2db21315 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
 /tests/tmp
 /vendor
-composer.lock
+/composer.lock
 .phpunit.result.cache
diff --git a/build-cs/.gitignore b/build-cs/.gitignore
index ff72e2d0..61ead866 100644
--- a/build-cs/.gitignore
+++ b/build-cs/.gitignore
@@ -1,2 +1 @@
-/composer.lock
 /vendor
diff --git a/build-cs/composer.json b/build-cs/composer.json
index cc6a4983..e3079710 100644
--- a/build-cs/composer.json
+++ b/build-cs/composer.json
@@ -2,7 +2,7 @@
 	"require-dev": {
 		"consistence-community/coding-standard": "^3.10",
 		"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
-		"slevomat/coding-standard": "^6.4"
+		"slevomat/coding-standard": "^7.0"
 	},
 	"config": {
 		"allow-plugins": {
diff --git a/build-cs/composer.lock b/build-cs/composer.lock
new file mode 100644
index 00000000..70af78f9
--- /dev/null
+++ b/build-cs/composer.lock
@@ -0,0 +1,327 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "4485bbedba7bcc71ace5f69dbb9b6c47",
+    "packages": [],
+    "packages-dev": [
+        {
+            "name": "consistence-community/coding-standard",
+            "version": "3.11.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consistence-community/coding-standard.git",
+                "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df",
+                "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.4",
+                "slevomat/coding-standard": "~7.0",
+                "squizlabs/php_codesniffer": "~3.6.0"
+            },
+            "replace": {
+                "consistence/coding-standard": "3.10.*"
+            },
+            "require-dev": {
+                "phing/phing": "2.16.4",
+                "php-parallel-lint/php-parallel-lint": "1.3.0",
+                "phpunit/phpunit": "9.5.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Consistence\\": [
+                        "Consistence"
+                    ]
+                },
+                "classmap": [
+                    "Consistence"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Vašek Purchart",
+                    "email": "me@vasekpurchart.cz",
+                    "homepage": "http://vasekpurchart.cz"
+                }
+            ],
+            "description": "Consistence - Coding Standard - PHP Code Sniffer rules",
+            "keywords": [
+                "Coding Standard",
+                "PHPCodeSniffer",
+                "codesniffer",
+                "coding",
+                "cs",
+                "phpcs",
+                "ruleset",
+                "sniffer",
+                "standard"
+            ],
+            "support": {
+                "issues": "https://github.com/consistence-community/coding-standard/issues",
+                "source": "https://github.com/consistence-community/coding-standard/tree/3.11.1"
+            },
+            "time": "2021-05-03T18:13:22+00:00"
+        },
+        {
+            "name": "dealerdirect/phpcodesniffer-composer-installer",
+            "version": "v0.7.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
+                "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
+                "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
+                "shasum": ""
+            },
+            "require": {
+                "composer-plugin-api": "^1.0 || ^2.0",
+                "php": ">=5.3",
+                "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
+            },
+            "require-dev": {
+                "composer/composer": "*",
+                "php-parallel-lint/php-parallel-lint": "^1.3.1",
+                "phpcompatibility/php-compatibility": "^9.0"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Franck Nijhof",
+                    "email": "franck.nijhof@dealerdirect.com",
+                    "homepage": "http://www.frenck.nl",
+                    "role": "Developer / IT Manager"
+                },
+                {
+                    "name": "Contributors",
+                    "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors"
+                }
+            ],
+            "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+            "homepage": "http://www.dealerdirect.com",
+            "keywords": [
+                "PHPCodeSniffer",
+                "PHP_CodeSniffer",
+                "code quality",
+                "codesniffer",
+                "composer",
+                "installer",
+                "phpcbf",
+                "phpcs",
+                "plugin",
+                "qa",
+                "quality",
+                "standard",
+                "standards",
+                "style guide",
+                "stylecheck",
+                "tests"
+            ],
+            "support": {
+                "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
+                "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
+            },
+            "time": "2022-02-04T12:51:07+00:00"
+        },
+        {
+            "name": "phpstan/phpdoc-parser",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpdoc-parser.git",
+                "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
+                "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "php-parallel-lint/php-parallel-lint": "^1.2",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^1.0",
+                "phpstan/phpstan-strict-rules": "^1.0",
+                "phpunit/phpunit": "^9.5",
+                "symfony/process": "^5.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PHPStan\\PhpDocParser\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPDoc parser with support for nullable, intersection and generic types",
+            "support": {
+                "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.2.0"
+            },
+            "time": "2021-09-16T20:46:02+00:00"
+        },
+        {
+            "name": "slevomat/coding-standard",
+            "version": "7.0.18",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/slevomat/coding-standard.git",
+                "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b81ac84f41a4797dc25c8ede1b0718e2a74be0fc",
+                "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc",
+                "shasum": ""
+            },
+            "require": {
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
+                "php": "^7.1 || ^8.0",
+                "phpstan/phpdoc-parser": "^1.0.0",
+                "squizlabs/php_codesniffer": "^3.6.1"
+            },
+            "require-dev": {
+                "phing/phing": "2.17.0",
+                "php-parallel-lint/php-parallel-lint": "1.3.1",
+                "phpstan/phpstan": "1.2.0",
+                "phpstan/phpstan-deprecation-rules": "1.0.0",
+                "phpstan/phpstan-phpunit": "1.0.0",
+                "phpstan/phpstan-strict-rules": "1.1.0",
+                "phpunit/phpunit": "7.5.20|8.5.21|9.5.10"
+            },
+            "type": "phpcodesniffer-standard",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "SlevomatCodingStandard\\": "SlevomatCodingStandard"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+            "support": {
+                "issues": "https://github.com/slevomat/coding-standard/issues",
+                "source": "https://github.com/slevomat/coding-standard/tree/7.0.18"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/kukulich",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-12-07T17:19:06+00:00"
+        },
+        {
+            "name": "squizlabs/php_codesniffer",
+            "version": "3.6.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+                "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a",
+                "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a",
+                "shasum": ""
+            },
+            "require": {
+                "ext-simplexml": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+            },
+            "bin": [
+                "bin/phpcs",
+                "bin/phpcbf"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Sherwood",
+                    "role": "lead"
+                }
+            ],
+            "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+            "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
+            "keywords": [
+                "phpcs",
+                "standards"
+            ],
+            "support": {
+                "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
+                "source": "https://github.com/squizlabs/PHP_CodeSniffer",
+                "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+            },
+            "time": "2021-12-12T21:44:58+00:00"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": [],
+    "plugin-api-version": "2.2.0"
+}
diff --git a/phpcs.xml b/phpcs.xml
index 2807efec..3412c956 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,66 +1,112 @@
 <?xml version="1.0"?>
-<ruleset name="PHPStan PHPUnit extensions and rules">
-    <arg name="colors"/>
-    <arg name="extensions" value="php"/>
-    <arg name="encoding" value="utf-8"/>
-    <arg name="tab-width" value="4"/>
-    <arg name="cache" value="tmp/cache/phpcs"/>
-    <arg value="sp"/>
-    <file>src</file>
-    <file>tests</file>
-    <rule ref="build-cs/vendor/consistence-community/coding-standard/Consistence/ruleset.xml">
-        <exclude name="Squiz.Functions.GlobalFunction.Found"/>
-        <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword"/>
-        <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation"/>
-        <exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions"/>
-        <exclude name="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly"/>
-        <exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
-        <exclude name="Consistence.Exceptions.ExceptionDeclaration"/>
-        <exclude name="Squiz.Commenting.FunctionComment.MissingParamTag"/>
-    </rule>
-    <rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses">
-        <properties>
-            <property name="caseSensitive" value="false"/>
-        </properties>
-    </rule>
-    <rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
-        <properties>
-            <property name="newlinesCountBetweenOpenTagAndDeclare" value="0"/>
-        </properties>
-    </rule>
-    <rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint">
-        <properties>
-            <property name="usefulAnnotations" type="array" value="
-                @dataProvider,
-                @requires
-            "/>
-        </properties>
-    </rule>
-    <rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
-        <properties>
-            <property name="enableNativeTypeHint" value="false"/>
-        </properties>
-        <exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
-    </rule>
-    <rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint">
-        <properties>
-            <property name="enableObjectTypeHint" value="false"/>
-        </properties>
-        <exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
-    </rule>
-    <rule ref="SlevomatCodingStandard.ControlStructures.AssignmentInCondition"/>
-    <rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators"/>
-    <rule ref="SlevomatCodingStandard.ControlStructures.EarlyExit"/>
-    <rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming"/>
-    <rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming"/>
-    <!-- <rule ref="SlevomatCodingStandard.ControlStructures.DisallowShortTernaryOperator"/>-->
-    <!-- <rule ref="SlevomatCodingStandard.Namespaces.RequireOneNamespaceInFile"/> -->
-    <!-- <rule ref="SlevomatCodingStandard.PHP.ShortList"/> -->
-    <rule ref="SlevomatCodingStandard.Files.TypeNameMatchesFileName">
-        <properties>
-            <property name="rootNamespaces" type="array" value="src=>PHPStan,tests=>PHPStan"/>
-        </properties>
-    </rule>
-    <exclude-pattern>tests/tmp</exclude-pattern>
+<ruleset name="PHPStan deprecation rules">
+	<config name="php_version" value="70100"/>
+	<arg name="colors"/>
+	<arg name="extensions" value="php"/>
+	<arg name="encoding" value="utf-8"/>
+	<arg name="tab-width" value="4"/>
+	<arg name="cache" value="tmp/cache/phpcs"/>
+	<arg value="sp"/>
+	<file>src</file>
+	<file>tests</file>
+
+	<rule ref="build-cs/vendor/consistence-community/coding-standard/Consistence/ruleset.xml">
+		<exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat"/>
+		<exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable"/>
+		<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation"/>
+		<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions"/>
+		<exclude name="Consistence.Exceptions.ExceptionDeclaration"/>
+		<exclude name="Squiz.Commenting.FunctionComment"/>
+		<exclude name="Squiz.PHP.Heredoc.NotAllowed"/>
+		<exclude name="Squiz.WhiteSpace.FunctionSpacing.Before"/>
+	</rule>
+	<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses">
+		<properties>
+			<property name="caseSensitive" value="false"/>
+			<property name="psr12Compatible" value="true"/>
+		</properties>
+	</rule>
+	<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
+		<properties>
+			<property name="declareOnFirstLine" value="true"/>
+		</properties>
+	</rule>
+	<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint">
+		<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
+		<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint"/>
+	</rule>
+	<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.UselessAnnotation">
+		<severity>10</severity>
+	</rule>
+	<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
+		<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
+	</rule>
+	<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint.UselessAnnotation">
+		<severity>10</severity>
+	</rule>
+	<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint">
+		<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
+		<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint"/>
+		<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint"/>
+	</rule>
+	<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.UselessAnnotation">
+		<severity>10</severity>
+	</rule>
+	<rule ref="SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure.UnusedInheritedVariable"/>
+	<rule ref="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException"/>
+	<rule ref="SlevomatCodingStandard.Variables.UnusedVariable.UnusedVariable"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.AssignmentInCondition"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.EarlyExit">
+		<exclude name="SlevomatCodingStandard.ControlStructures.EarlyExit.UselessElseIf"/>
+	</rule>
+	<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming"/>
+	<rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming"/>
+	<rule ref="SlevomatCodingStandard.Files.TypeNameMatchesFileName">
+		<properties>
+			<property name="rootNamespaces" type="array">
+				<element key="src" value="PHPStan"/>
+				<element key="tests" value="PHPStan"/>
+			</property>
+
+		</properties>
+	</rule>
+	<rule ref="SlevomatCodingStandard.Classes.ModernClassNameReference"/>
+	<rule ref="SlevomatCodingStandard.Functions.StaticClosure"/>
+	<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators"/>
+	<rule ref="SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator"/>
+	<rule ref="SlevomatCodingStandard.TypeHints.NullTypeHintOnLastPosition"/>
+	<rule ref="SlevomatCodingStandard.Classes.TraitUseDeclaration"/>
+	<rule ref="SlevomatCodingStandard.Classes.TraitUseSpacing"/>
+	<rule ref="SlevomatCodingStandard.Variables.UnusedVariable"/>
+	<rule ref="SlevomatCodingStandard.Variables.UselessVariable"/>
+	<!--<rule ref="SlevomatCodingStandard.Functions.UnusedParameter"/>-->
+	<rule ref="SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure"/>
+	<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly">
+		<properties>
+			<property name="searchAnnotations" value="true"/>
+			<property name="namespacesRequiredToUse" value=""/>
+			<property name="allowPartialUses" value="true"/>
+			<property name="allowFallbackGlobalFunctions" value="false"/>
+			<property name="allowFallbackGlobalConstants" value="false"/>
+			<property name="allowFullyQualifiedExceptions" value="false"/>
+			<property name="allowFullyQualifiedNameForCollidingClasses" value="true"/>
+			<property name="allowFullyQualifiedNameForCollidingFunctions" value="true"/>
+			<property name="allowFullyQualifiedNameForCollidingConstants" value="true"/>
+		</properties>
+	</rule>
+	<rule ref="SlevomatCodingStandard.Namespaces.UselessAlias"/>
+	<rule ref="SlevomatCodingStandard.Namespaces.UseSpacing"/>
+	<rule ref="SlevomatCodingStandard.PHP.UselessSemicolon"/>
+	<rule ref="SlevomatCodingStandard.PHP.UselessParentheses"/>
+	<rule ref="Squiz.WhiteSpace.FunctionSpacing">
+		<properties>
+			<property name="spacing" value="1" />
+			<property name="spacingBeforeFirst" value="1"/>
+			<property name="spacingAfterLast" value="1"/>
+		</properties>
+	</rule>
+	<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps"/>
+	<rule ref="Consistence.NamingConventions.ValidVariableName.NotCamelCaps"/>
 	<exclude-pattern>tests/*/data</exclude-pattern>
+	<exclude-pattern>tests/tmp</exclude-pattern>
 </ruleset>
diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 4c9a6af1..0a3a3d3e 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -6,7 +6,7 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\ShouldNotHappenException;
+use PHPStan\Rules\RuleError;
 use PHPStan\Symfony\ServiceMap;
 use PHPStan\TrinaryLogic;
 use PHPStan\Type\ObjectType;
@@ -33,16 +33,10 @@ public function getNodeType(): string
 	}
 
 	/**
-	 * @param \PhpParser\Node $node
-	 * @param \PHPStan\Analyser\Scope $scope
-	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 * @return (string|RuleError)[] errors
 	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
-		if (!$node instanceof MethodCall) {
-			throw new ShouldNotHappenException();
-		}
-
 		if (!$node->name instanceof Node\Identifier) {
 			return [];
 		}
diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index cb0ae4e6..417f51f5 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -7,10 +7,11 @@
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\ShouldNotHappenException;
+use PHPStan\Rules\RuleError;
 use PHPStan\Symfony\ServiceMap;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
+use function sprintf;
 
 /**
  * @implements Rule<MethodCall>
@@ -21,7 +22,7 @@ final class ContainerInterfaceUnknownServiceRule implements Rule
 	/** @var ServiceMap */
 	private $serviceMap;
 
-	/** @var \PhpParser\PrettyPrinter\Standard */
+	/** @var Standard */
 	private $printer;
 
 	public function __construct(ServiceMap $symfonyServiceMap, Standard $printer)
@@ -36,16 +37,10 @@ public function getNodeType(): string
 	}
 
 	/**
-	 * @param \PhpParser\Node $node
-	 * @param \PHPStan\Analyser\Scope $scope
-	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 * @return (string|RuleError)[] errors
 	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
-		if (!$node instanceof MethodCall) {
-			throw new ShouldNotHappenException();
-		}
-
 		if (!$node->name instanceof Node\Identifier) {
 			return [];
 		}
diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index c29f11ed..51d7c3bc 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -6,7 +6,7 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\ShouldNotHappenException;
+use PHPStan\Rules\RuleError;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\Constant\ConstantIntegerType;
 use PHPStan\Type\IntegerType;
@@ -16,6 +16,7 @@
 use PHPStan\Type\TypeUtils;
 use PHPStan\Type\UnionType;
 use PHPStan\Type\VerbosityLevel;
+use function count;
 use function sprintf;
 
 /**
@@ -30,16 +31,10 @@ public function getNodeType(): string
 	}
 
 	/**
-	 * @param \PhpParser\Node $node
-	 * @param \PHPStan\Analyser\Scope $scope
-	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 * @return (string|RuleError)[] errors
 	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
-		if (!$node instanceof MethodCall) {
-			throw new ShouldNotHappenException();
-		};
-
 		if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) {
 			return [];
 		}
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index 3eb2df1b..ab52d626 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -6,7 +6,7 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\ShouldNotHappenException;
+use PHPStan\Rules\RuleError;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantIntegerType;
@@ -18,6 +18,7 @@
 use PHPStan\Type\TypeUtils;
 use PHPStan\Type\UnionType;
 use PHPStan\Type\VerbosityLevel;
+use function count;
 use function sprintf;
 
 /**
@@ -32,16 +33,10 @@ public function getNodeType(): string
 	}
 
 	/**
-	 * @param \PhpParser\Node $node
-	 * @param \PHPStan\Analyser\Scope $scope
-	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 * @return (string|RuleError)[] errors
 	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
-		if (!$node instanceof MethodCall) {
-			throw new ShouldNotHappenException();
-		};
-
 		if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) {
 			return [];
 		}
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index 59f71ed6..52f90241 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -8,7 +8,7 @@
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\ShouldNotHappenException;
+use PHPStan\Rules\RuleError;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
@@ -22,10 +22,10 @@
 final class UndefinedArgumentRule implements Rule
 {
 
-	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
-	/** @var \PhpParser\PrettyPrinter\Standard */
+	/** @var Standard */
 	private $printer;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
@@ -40,16 +40,10 @@ public function getNodeType(): string
 	}
 
 	/**
-	 * @param \PhpParser\Node $node
-	 * @param \PHPStan\Analyser\Scope $scope
-	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 * @return (string|RuleError)[] errors
 	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
-		if (!$node instanceof MethodCall) {
-			throw new ShouldNotHappenException();
-		};
-
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
 			return [];
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index 29e08819..00dd750e 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -8,7 +8,7 @@
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\ShouldNotHappenException;
+use PHPStan\Rules\RuleError;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
@@ -22,10 +22,10 @@
 final class UndefinedOptionRule implements Rule
 {
 
-	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
-	/** @var \PhpParser\PrettyPrinter\Standard */
+	/** @var Standard */
 	private $printer;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
@@ -40,16 +40,10 @@ public function getNodeType(): string
 	}
 
 	/**
-	 * @param \PhpParser\Node $node
-	 * @param \PHPStan\Analyser\Scope $scope
-	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 * @return (string|RuleError)[] errors
 	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
-		if (!$node instanceof MethodCall) {
-			throw new ShouldNotHappenException();
-		};
-
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
 			return [];
diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index efee6292..6d4ec810 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -5,16 +5,18 @@
 use PHPStan\Reflection\ClassReflection;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Type\ObjectType;
+use Symfony\Component\Console\Application;
 use Symfony\Component\Console\Command\Command;
 use function file_exists;
 use function get_class;
 use function is_readable;
+use function method_exists;
 use function sprintf;
 
 final class ConsoleApplicationResolver
 {
 
-	/** @var \Symfony\Component\Console\Application|null */
+	/** @var Application|null */
 	private $consoleApplication;
 
 	public function __construct(Configuration $configuration)
@@ -27,7 +29,7 @@ public function __construct(Configuration $configuration)
 	}
 
 	/**
-	 * @return \Symfony\Component\Console\Application|null
+	 * @return Application|null
 	 * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint
 	 */
 	private function loadConsoleApplication(string $consoleApplicationLoader)
@@ -42,7 +44,7 @@ private function loadConsoleApplication(string $consoleApplicationLoader)
 	}
 
 	/**
-	 * @return \Symfony\Component\Console\Command\Command[]
+	 * @return Command[]
 	 */
 	public function findCommands(ClassReflection $classReflection): array
 	{
diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php
index a58ed9fc..fe29b34f 100644
--- a/src/Symfony/DefaultParameterMap.php
+++ b/src/Symfony/DefaultParameterMap.php
@@ -10,11 +10,11 @@
 final class DefaultParameterMap implements ParameterMap
 {
 
-	/** @var \PHPStan\Symfony\ParameterDefinition[] */
+	/** @var ParameterDefinition[] */
 	private $parameters;
 
 	/**
-	 * @param \PHPStan\Symfony\ParameterDefinition[] $parameters
+	 * @param ParameterDefinition[] $parameters
 	 */
 	public function __construct(array $parameters)
 	{
@@ -22,7 +22,7 @@ public function __construct(array $parameters)
 	}
 
 	/**
-	 * @return \PHPStan\Symfony\ParameterDefinition[]
+	 * @return ParameterDefinition[]
 	 */
 	public function getParameters(): array
 	{
diff --git a/src/Symfony/DefaultServiceMap.php b/src/Symfony/DefaultServiceMap.php
index 6339b450..cb8259d2 100644
--- a/src/Symfony/DefaultServiceMap.php
+++ b/src/Symfony/DefaultServiceMap.php
@@ -10,11 +10,11 @@
 final class DefaultServiceMap implements ServiceMap
 {
 
-	/** @var \PHPStan\Symfony\ServiceDefinition[] */
+	/** @var ServiceDefinition[] */
 	private $services;
 
 	/**
-	 * @param \PHPStan\Symfony\ServiceDefinition[] $services
+	 * @param ServiceDefinition[] $services
 	 */
 	public function __construct(array $services)
 	{
@@ -22,7 +22,7 @@ public function __construct(array $services)
 	}
 
 	/**
-	 * @return \PHPStan\Symfony\ServiceDefinition[]
+	 * @return ServiceDefinition[]
 	 */
 	public function getServices(): array
 	{
diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php
index 7e723411..0bf450e7 100644
--- a/src/Symfony/FakeParameterMap.php
+++ b/src/Symfony/FakeParameterMap.php
@@ -9,7 +9,7 @@ final class FakeParameterMap implements ParameterMap
 {
 
 	/**
-	 * @return \PHPStan\Symfony\ParameterDefinition[]
+	 * @return ParameterDefinition[]
 	 */
 	public function getParameters(): array
 	{
diff --git a/src/Symfony/FakeServiceMap.php b/src/Symfony/FakeServiceMap.php
index d05fe8ed..88f9edac 100644
--- a/src/Symfony/FakeServiceMap.php
+++ b/src/Symfony/FakeServiceMap.php
@@ -9,7 +9,7 @@ final class FakeServiceMap implements ServiceMap
 {
 
 	/**
-	 * @return \PHPStan\Symfony\ServiceDefinition[]
+	 * @return ServiceDefinition[]
 	 */
 	public function getServices(): array
 	{
diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php
index 94fd2104..834ca228 100644
--- a/src/Symfony/InputBagStubFilesExtension.php
+++ b/src/Symfony/InputBagStubFilesExtension.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Symfony;
 
 use PHPStan\PhpDoc\StubFilesExtension;
+use function class_exists;
 
 class InputBagStubFilesExtension implements StubFilesExtension
 {
diff --git a/src/Symfony/Parameter.php b/src/Symfony/Parameter.php
index 1df679bc..8ff3f7a8 100644
--- a/src/Symfony/Parameter.php
+++ b/src/Symfony/Parameter.php
@@ -12,7 +12,6 @@ final class Parameter implements ParameterDefinition
 	private $value;
 
 	/**
-	 * @param string $key
 	 * @param array<mixed>|bool|float|int|string $value
 	 */
 	public function __construct(
diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php
index fc14fb2c..c768e2f2 100644
--- a/src/Symfony/ParameterMap.php
+++ b/src/Symfony/ParameterMap.php
@@ -9,7 +9,7 @@ interface ParameterMap
 {
 
 	/**
-	 * @return \PHPStan\Symfony\ParameterDefinition[]
+	 * @return ParameterDefinition[]
 	 */
 	public function getParameters(): array;
 
diff --git a/src/Symfony/ServiceMap.php b/src/Symfony/ServiceMap.php
index b954dc72..6665ede0 100644
--- a/src/Symfony/ServiceMap.php
+++ b/src/Symfony/ServiceMap.php
@@ -9,7 +9,7 @@ interface ServiceMap
 {
 
 	/**
-	 * @return \PHPStan\Symfony\ServiceDefinition[]
+	 * @return ServiceDefinition[]
 	 */
 	public function getServices(): array;
 
diff --git a/src/Symfony/XmlContainerNotExistsException.php b/src/Symfony/XmlContainerNotExistsException.php
index 7238cc6e..59b600de 100644
--- a/src/Symfony/XmlContainerNotExistsException.php
+++ b/src/Symfony/XmlContainerNotExistsException.php
@@ -2,7 +2,9 @@
 
 namespace PHPStan\Symfony;
 
-final class XmlContainerNotExistsException extends \InvalidArgumentException
+use InvalidArgumentException;
+
+final class XmlContainerNotExistsException extends InvalidArgumentException
 {
 
 }
diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php
index 9b34d14d..0891423a 100644
--- a/src/Symfony/XmlParameterMapFactory.php
+++ b/src/Symfony/XmlParameterMapFactory.php
@@ -2,7 +2,14 @@
 
 namespace PHPStan\Symfony;
 
+use InvalidArgumentException;
+use SimpleXMLElement;
+use function base64_decode;
+use function file_get_contents;
+use function is_numeric;
+use function simplexml_load_string;
 use function sprintf;
+use function strpos;
 
 final class XmlParameterMapFactory implements ParameterMapFactory
 {
@@ -31,10 +38,10 @@ public function create(): ParameterMap
 			throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml));
 		}
 
-		/** @var \PHPStan\Symfony\Parameter[] $parameters */
+		/** @var Parameter[] $parameters */
 		$parameters = [];
 		foreach ($xml->parameters->parameter as $def) {
-			/** @var \SimpleXMLElement $attrs */
+			/** @var SimpleXMLElement $attrs */
 			$attrs = $def->attributes();
 
 			$parameter = new Parameter(
@@ -51,9 +58,9 @@ public function create(): ParameterMap
 	/**
 	 * @return array<mixed>|bool|float|int|string
 	 */
-	private function getNodeValue(\SimpleXMLElement $def)
+	private function getNodeValue(SimpleXMLElement $def)
 	{
-		/** @var \SimpleXMLElement $attrs */
+		/** @var SimpleXMLElement $attrs */
 		$attrs = $def->attributes();
 
 		$value = null;
@@ -61,7 +68,7 @@ private function getNodeValue(\SimpleXMLElement $def)
 			case 'collection':
 				$value = [];
 				foreach ($def->children() as $child) {
-					/** @var \SimpleXMLElement $childAttrs */
+					/** @var SimpleXMLElement $childAttrs */
 					$childAttrs = $child->attributes();
 
 					if (isset($childAttrs->key)) {
@@ -79,7 +86,7 @@ private function getNodeValue(\SimpleXMLElement $def)
 			case 'binary':
 				$value = base64_decode((string) $def, true);
 				if ($value === false) {
-					throw new \InvalidArgumentException(sprintf('Parameter "%s" of binary type is not valid base64 encoded string.', (string) $attrs->key));
+					throw new InvalidArgumentException(sprintf('Parameter "%s" of binary type is not valid base64 encoded string.', (string) $attrs->key));
 				}
 
 				break;
diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 60af1326..ad52c373 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -2,6 +2,8 @@
 
 namespace PHPStan\Symfony;
 
+use SimpleXMLElement;
+use function file_get_contents;
 use function simplexml_load_string;
 use function sprintf;
 use function strpos;
@@ -34,12 +36,12 @@ public function create(): ServiceMap
 			throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml));
 		}
 
-		/** @var \PHPStan\Symfony\Service[] $services */
+		/** @var Service[] $services */
 		$services = [];
-		/** @var \PHPStan\Symfony\Service[] $aliases */
+		/** @var Service[] $aliases */
 		$aliases = [];
 		foreach ($xml->services->service as $def) {
-			/** @var \SimpleXMLElement $attrs */
+			/** @var SimpleXMLElement $attrs */
 			$attrs = $def->attributes();
 			if (!isset($attrs->id)) {
 				continue;
diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
index 74b6981f..b57f3c35 100644
--- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
@@ -15,10 +15,10 @@
 final class ArgumentTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	/** @var \PhpParser\PrettyPrinter\Standard */
+	/** @var Standard */
 	private $printer;
 
-	/** @var \PHPStan\Analyser\TypeSpecifier */
+	/** @var TypeSpecifier */
 	private $typeSpecifier;
 
 	public function __construct(Standard $printer)
diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
index e9b6ed7e..db2348e3 100644
--- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
@@ -18,7 +18,7 @@
 final class CommandGetHelperDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
index cd1d4e51..828b000e 100644
--- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
@@ -11,6 +11,8 @@
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeUtils;
 use PHPStan\Type\VerbosityLevel;
+use function count;
+use function in_array;
 
 final class ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
index 5d19b60f..6ef28bbd 100644
--- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
@@ -10,6 +10,7 @@
 use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
 use PHPStan\Type\Type;
 use PHPStan\Type\VerbosityLevel;
+use function in_array;
 
 final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
@@ -21,7 +22,6 @@ final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodR
 	private $methods;
 
 	/**
-	 * @param string $className
 	 * @param string[] $methods
 	 */
 	public function __construct(string $className, array $methods)
diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
index 8dd47a90..4babdcd7 100644
--- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
@@ -9,6 +9,7 @@
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
 use PHPStan\Type\Type;
+use function in_array;
 
 final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
@@ -20,7 +21,6 @@ final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodRetur
 	private $methods;
 
 	/**
-	 * @param string $className
 	 * @param string[] $methods
 	 */
 	public function __construct(string $className, array $methods)
diff --git a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
index a5f4f724..8cf3170a 100644
--- a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
@@ -6,10 +6,12 @@
 use PhpParser\Node\Name;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
+use PHPStan\ShouldNotHappenException;
 use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
 use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeUtils;
+use function count;
 
 final class TreeBuilderDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
 {
@@ -37,7 +39,7 @@ public function isStaticMethodSupported(MethodReflection $methodReflection): boo
 	public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): Type
 	{
 		if (!$methodCall->class instanceof Name) {
-			throw new \PHPStan\ShouldNotHappenException();
+			throw new ShouldNotHappenException();
 		}
 
 		$className = $scope->resolveName($methodCall->class);
diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
index e4e6a34c..5ac61c2f 100644
--- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php
+++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
@@ -11,6 +11,7 @@
 use PHPStan\Type\MixedType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
+use function count;
 
 final class EnvelopeReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
index b4875829..af35a548 100644
--- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
@@ -18,6 +18,7 @@
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeCombinator;
 use PHPStan\Type\UnionType;
+use function in_array;
 
 final class InputBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index 15e75d0e..a8c35200 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -20,7 +20,7 @@
 final class InputInterfaceGetArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
index fb397311..7b543a16 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -22,7 +22,7 @@
 final class InputInterfaceGetOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
index 112a2bcf..dc933e14 100644
--- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
@@ -18,7 +18,7 @@
 final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
index eec7201c..3f239fe6 100644
--- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
@@ -18,7 +18,7 @@
 final class InputInterfaceHasOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
index 347cf591..98e0e3fd 100644
--- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
@@ -15,10 +15,10 @@
 final class OptionTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	/** @var \PhpParser\PrettyPrinter\Standard */
+	/** @var Standard */
 	private $printer;
 
-	/** @var \PHPStan\Analyser\TypeSpecifier */
+	/** @var TypeSpecifier */
 	private $typeSpecifier;
 
 	public function __construct(Standard $printer)
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 0de91de1..0fa8e7bc 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -27,7 +27,17 @@
 use PHPStan\Type\TypeTraverser;
 use PHPStan\Type\UnionType;
 use Symfony\Component\DependencyInjection\EnvVarProcessor;
+use function array_filter;
+use function array_keys;
+use function array_map;
+use function array_values;
+use function class_exists;
+use function count;
 use function in_array;
+use function is_array;
+use function is_string;
+use function preg_match;
+use function strlen;
 
 final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
@@ -44,10 +54,10 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy
 	/** @var bool */
 	private $constantHassers;
 
-	/** @var \PHPStan\Symfony\ParameterMap */
+	/** @var ParameterMap */
 	private $parameterMap;
 
-	/** @var \PHPStan\PhpDoc\TypeStringResolver */
+	/** @var TypeStringResolver */
 	private $typeStringResolver;
 
 	public function __construct(
@@ -74,7 +84,7 @@ public function getClass(): string
 
 	public function isMethodSupported(MethodReflection $methodReflection): bool
 	{
-		$methods = array_filter([$this->methodGet, $this->methodHas], function (?string $method): bool {
+		$methods = array_filter([$this->methodGet, $this->methodHas], static function (?string $method): bool {
 			return $method !== null;
 		});
 
@@ -124,7 +134,6 @@ private function getGetTypeFromMethodCall(
 	}
 
 	/**
-	 * @param Scope								 $scope
 	 * @param array<mixed>|bool|float|int|string $value
 	 */
 	private function generalizeTypeFromValue(Scope $scope, $value): Type
diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
index fda9b048..5525a770 100755
--- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
@@ -11,6 +11,7 @@
 use PHPStan\Type\MixedType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
+use function substr;
 
 class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index 41c3a394..04608c17 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -24,7 +24,7 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
 	/** @var bool */
 	private $constantHassers;
 
-	/** @var \PHPStan\Symfony\ServiceMap */
+	/** @var ServiceMap */
 	private $serviceMap;
 
 	public function __construct(string $className, Configuration $configuration, ServiceMap $symfonyServiceMap)
diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
index a5aad54e..b52c1531 100644
--- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
@@ -18,10 +18,10 @@ final class ServiceTypeSpecifyingExtension implements MethodTypeSpecifyingExtens
 	/** @var string */
 	private $className;
 
-	/** @var \PhpParser\PrettyPrinter\Standard */
+	/** @var Standard */
 	private $printer;
 
-	/** @var \PHPStan\Analyser\TypeSpecifier */
+	/** @var TypeSpecifier */
 	private $typeSpecifier;
 
 	public function __construct(string $className, Standard $printer)
diff --git a/tests/Rules/NonexistentInputBagClassTest.php b/tests/Rules/NonexistentInputBagClassTest.php
index 6619a8c7..5dc674d4 100644
--- a/tests/Rules/NonexistentInputBagClassTest.php
+++ b/tests/Rules/NonexistentInputBagClassTest.php
@@ -11,7 +11,7 @@
 class NonexistentInputBagClassTest extends RuleTestCase
 {
 
-	protected function getRule(): \PHPStan\Rules\Rule
+	protected function getRule(): Rule
 	{
 		return self::getContainer()->getByType(CallMethodsRule::class);
 	}
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
index 23bdd4e6..4551accd 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -6,6 +6,8 @@
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
+use function class_exists;
+use function interface_exists;
 
 /**
  * @extends RuleTestCase<ContainerInterfacePrivateServiceRule>
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index c349b695..49afaff5 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -6,6 +6,8 @@
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
+use function class_exists;
+use function interface_exists;
 
 /**
  * @extends RuleTestCase<ContainerInterfacePrivateServiceRule>
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index 46ea3b63..f975a179 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -7,7 +7,9 @@
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
+use PHPStan\Type\MethodTypeSpecifyingExtension;
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
+use function class_exists;
 
 /**
  * @extends RuleTestCase<ContainerInterfaceUnknownServiceRule>
@@ -21,7 +23,7 @@ protected function getRule(): Rule
 	}
 
 	/**
-	 * @return \PHPStan\Type\MethodTypeSpecifyingExtension[]
+	 * @return MethodTypeSpecifyingExtension[]
 	 */
 	protected function getMethodTypeSpecifyingExtensions(): array
 	{
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index e433a233..12893bab 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -7,6 +7,8 @@
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
+use function class_exists;
+use function interface_exists;
 
 /**
  * @extends RuleTestCase<ContainerInterfaceUnknownServiceRule>
diff --git a/tests/Rules/Symfony/ExampleAbstractController.php b/tests/Rules/Symfony/ExampleAbstractController.php
index 4f0d2abd..22e5900e 100644
--- a/tests/Rules/Symfony/ExampleAbstractController.php
+++ b/tests/Rules/Symfony/ExampleAbstractController.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Rules\Symfony;
 
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Bundle\FrameworkBundle\Test\TestContainer;
 
 final class ExampleAbstractController extends AbstractController
 {
@@ -14,7 +15,7 @@ public function privateService(): void
 
 	public function privateServiceInTestContainer(): void
 	{
-		/** @var \Symfony\Bundle\FrameworkBundle\Test\TestContainer $container */
+		/** @var TestContainer $container */
 		$container = doFoo();
 		$container->get('private');
 	}
diff --git a/tests/Rules/Symfony/ExampleController.php b/tests/Rules/Symfony/ExampleController.php
index edbdaaf5..5b9e1ca4 100644
--- a/tests/Rules/Symfony/ExampleController.php
+++ b/tests/Rules/Symfony/ExampleController.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Rules\Symfony;
 
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Bundle\FrameworkBundle\Test\TestContainer;
 
 final class ExampleController extends Controller
 {
@@ -14,7 +15,7 @@ public function privateService(): void
 
 	public function privateServiceInTestContainer(): void
 	{
-		/** @var \Symfony\Bundle\FrameworkBundle\Test\TestContainer $container */
+		/** @var TestContainer $container */
 		$container = doFoo();
 		$container->get('private');
 	}
diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php
index 3d7f294d..9005cf60 100644
--- a/tests/Rules/Symfony/ExampleServiceSubscriber.php
+++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Rules\Symfony;
 
 use Psr\Container\ContainerInterface;
+use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag;
 use Symfony\Contracts\Service\ServiceSubscriberInterface;
 
 final class ExampleServiceSubscriber implements ServiceSubscriberInterface
@@ -24,7 +25,7 @@ public function privateService(): void
 
 	public function containerParameter(): void
 	{
-		/** @var \Symfony\Component\DependencyInjection\ParameterBag\ContainerBag $containerBag */
+		/** @var ContainerBag $containerBag */
 		$containerBag = doFoo();
 		$containerBag->get('parameter_name');
 	}
diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php
index caab6f97..8e2e5b58 100644
--- a/tests/Symfony/DefaultParameterMapTest.php
+++ b/tests/Symfony/DefaultParameterMapTest.php
@@ -26,19 +26,19 @@ public function testGetParameterEscapedPath(): void
 	}
 
 	/**
-	 * @return \Iterator<mixed>
+	 * @return Iterator<mixed>
 	 */
 	public function getParameterProvider(): Iterator
 	{
 		yield [
 			'unknown',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNull($parameter);
 			},
 		];
 		yield [
 			'app.string',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.string', $parameter->getKey());
 				self::assertSame('abcdef', $parameter->getValue());
@@ -46,7 +46,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.int',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.int', $parameter->getKey());
 				self::assertSame(123, $parameter->getValue());
@@ -54,7 +54,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.int_as_string',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.int_as_string', $parameter->getKey());
 				self::assertSame('123', $parameter->getValue());
@@ -62,7 +62,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.float',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.float', $parameter->getKey());
 				self::assertSame(123.45, $parameter->getValue());
@@ -70,7 +70,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.float_as_string',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.float_as_string', $parameter->getKey());
 				self::assertSame('123.45', $parameter->getValue());
@@ -78,7 +78,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.boolean',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.boolean', $parameter->getKey());
 				self::assertTrue($parameter->getValue());
@@ -86,7 +86,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.boolean_as_string',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.boolean_as_string', $parameter->getKey());
 				self::assertSame('true', $parameter->getValue());
@@ -94,7 +94,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.list',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.list', $parameter->getKey());
 				self::assertEquals(['en', 'es', 'fr'], $parameter->getValue());
@@ -102,7 +102,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.list_of_list',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.list_of_list', $parameter->getKey());
 				self::assertEquals([
@@ -113,7 +113,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.map',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.map', $parameter->getKey());
 				self::assertEquals([
@@ -125,7 +125,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.binary',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.binary', $parameter->getKey());
 				self::assertSame('This is a Bell char ', $parameter->getValue());
@@ -133,7 +133,7 @@ function (?Parameter $parameter): void {
 		];
 		yield [
 			'app.constant',
-			function (?Parameter $parameter): void {
+			static function (?Parameter $parameter): void {
 				self::assertNotNull($parameter);
 				self::assertSame('app.constant', $parameter->getKey());
 				self::assertSame('Y-m-d\TH:i:sP', $parameter->getValue());
diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php
index 0ffbb457..dc8487e5 100644
--- a/tests/Symfony/DefaultServiceMapTest.php
+++ b/tests/Symfony/DefaultServiceMapTest.php
@@ -26,19 +26,19 @@ public function testGetContainerEscapedPath(): void
 	}
 
 	/**
-	 * @return \Iterator<mixed>
+	 * @return Iterator<mixed>
 	 */
 	public function getServiceProvider(): Iterator
 	{
 		yield [
 			'unknown',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNull($service);
 			},
 		];
 		yield [
 			'withoutClass',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('withoutClass', $service->getId());
 				self::assertNull($service->getClass());
@@ -49,7 +49,7 @@ function (?Service $service): void {
 		];
 		yield [
 			'withClass',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('withClass', $service->getId());
 				self::assertSame('Foo', $service->getClass());
@@ -60,7 +60,7 @@ function (?Service $service): void {
 		];
 		yield [
 			'withoutPublic',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('withoutPublic', $service->getId());
 				self::assertSame('Foo', $service->getClass());
@@ -71,7 +71,7 @@ function (?Service $service): void {
 		];
 		yield [
 			'publicNotTrue',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('publicNotTrue', $service->getId());
 				self::assertSame('Foo', $service->getClass());
@@ -82,7 +82,7 @@ function (?Service $service): void {
 		];
 		yield [
 			'public',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('public', $service->getId());
 				self::assertSame('Foo', $service->getClass());
@@ -93,7 +93,7 @@ function (?Service $service): void {
 		];
 		yield [
 			'synthetic',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('synthetic', $service->getId());
 				self::assertSame('Foo', $service->getClass());
@@ -104,7 +104,7 @@ function (?Service $service): void {
 		];
 		yield [
 			'alias',
-			function (?Service $service): void {
+			static function (?Service $service): void {
 				self::assertNotNull($service);
 				self::assertSame('alias', $service->getId());
 				self::assertSame('Foo', $service->getClass());
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index c0aa7f94..a0bb8115 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -5,6 +5,8 @@
 use PHPStan\Testing\TypeInferenceTestCase;
 use ReflectionMethod;
 use Symfony\Component\HttpFoundation\Request;
+use function class_exists;
+use function strpos;
 
 class ExtensionTest extends TypeInferenceTestCase
 {
@@ -58,8 +60,6 @@ public function dataFileAsserts(): iterable
 
 	/**
 	 * @dataProvider dataFileAsserts
-	 * @param string $assertType
-	 * @param string $file
 	 * @param mixed ...$args
 	 */
 	public function testFileAsserts(
diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
index 997b7e02..fd1785c7 100644
--- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php
+++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Type\Symfony;
 
 use PHPStan\Testing\TypeInferenceTestCase;
+use function class_exists;
 
 class ExtensionTestWithoutContainer extends TypeInferenceTestCase
 {
@@ -30,8 +31,6 @@ public function dataAbstractController(): iterable
 	/**
 	 * @dataProvider dataExampleController
 	 * @dataProvider dataAbstractController
-	 * @param string $assertType
-	 * @param string $file
 	 * @param mixed ...$args
 	 */
 	public function testFileAsserts(
diff --git a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
index ab247b9f..bde62b57 100644
--- a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
+++ b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
@@ -4,11 +4,12 @@
 
 use PHPStan\Rules\Comparison\ImpossibleCheckTypeMethodCallRule;
 use PHPStan\Rules\Rule;
+use PHPStan\Testing\RuleTestCase;
 
 /**
- * @extends \PHPStan\Testing\RuleTestCase<ImpossibleCheckTypeMethodCallRule>
+ * @extends RuleTestCase<ImpossibleCheckTypeMethodCallRule>
  */
-class ImpossibleCheckTypeMethodCallRuleTest extends \PHPStan\Testing\RuleTestCase
+class ImpossibleCheckTypeMethodCallRuleTest extends RuleTestCase
 {
 
 	protected function getRule(): Rule
diff --git a/tests/Type/Symfony/console_application_loader.php b/tests/Type/Symfony/console_application_loader.php
index 0ef2e0ae..b6755aea 100644
--- a/tests/Type/Symfony/console_application_loader.php
+++ b/tests/Type/Symfony/console_application_loader.php
@@ -15,7 +15,7 @@
 $application->add(new ExampleOptionCommand());
 
 if (class_exists(LazyCommand::class)) {
-	$application->add(new LazyCommand('lazy-example-option', [], '', false, function () {
+	$application->add(new LazyCommand('lazy-example-option', [], '', false, static function () {
 		return new ExampleOptionLazyCommand();
 	}));
 } else {

From 5fe3c2668a88738e1696264bf49aa5513b5216c3 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 8 Feb 2022 12:55:19 +0100
Subject: [PATCH 155/297] Fix

---
 .../Symfony/ContainerInterfacePrivateServiceRuleTest.php      | 2 +-
 .../Symfony/ContainerInterfaceUnknownServiceRuleTest.php      | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index 49afaff5..51513b09 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -32,7 +32,7 @@ public function testGetPrivateService(): void
 			[
 				[
 					'Service "private" is private.',
-					12,
+					13,
 				],
 			]
 		);
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 12893bab..4bd233e9 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -33,7 +33,7 @@ public function testGetPrivateService(): void
 			[
 				[
 					'Service "unknown" is not registered in the container.',
-					24,
+					25,
 				],
 			]
 		);
@@ -52,7 +52,7 @@ public function testGetPrivateServiceInAbstractController(): void
 			[
 				[
 					'Service "unknown" is not registered in the container.',
-					24,
+					25,
 				],
 			]
 		);

From 261955fc667e8c133077eb87863d74e0bb6d8b43 Mon Sep 17 00:00:00 2001
From: Grzegorz Korba <wirone@gmail.com>
Date: Fri, 25 Feb 2022 14:16:34 +0100
Subject: [PATCH 156/297] Support for parameterised service class

---
 composer.json                                 |  2 ++
 .../ServiceDynamicReturnTypeExtension.php     | 32 +++++++++++++++++--
 tests/Type/Symfony/container.xml              |  3 ++
 .../data/ExampleAbstractController.php        |  2 ++
 tests/Type/Symfony/data/ExampleController.php |  2 ++
 5 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 08f5b685..5b5438ff 100644
--- a/composer.json
+++ b/composer.json
@@ -26,8 +26,10 @@
     "phpstan/phpstan-phpunit": "^1.0",
     "phpstan/phpstan-strict-rules": "^1.0",
     "phpunit/phpunit": "^9.5",
+    "psr/container": "1.0 || 1.1.1",
     "symfony/config": "^4.2 || ^5.0",
     "symfony/console": "^4.0 || ^5.0",
+    "symfony/dependency-injection": "^4.0 || ^5.0",
     "symfony/form": "^4.0 || ^5.0",
     "symfony/framework-bundle": "^4.4 || ^5.0",
     "symfony/http-foundation": "^5.1",
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index 04608c17..cd3d2a46 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -8,11 +8,14 @@
 use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Symfony\Configuration;
+use PHPStan\Symfony\ParameterMap;
+use PHPStan\Symfony\ServiceDefinition;
 use PHPStan\Symfony\ServiceMap;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 use function in_array;
 
 final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -27,11 +30,20 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
 	/** @var ServiceMap */
 	private $serviceMap;
 
-	public function __construct(string $className, Configuration $configuration, ServiceMap $symfonyServiceMap)
+	/** @var ParameterBag */
+	private $parameterBag;
+
+	public function __construct(
+		string $className,
+		Configuration $configuration,
+		ServiceMap $symfonyServiceMap,
+		ParameterMap $symfonyParameterMap
+	)
 	{
 		$this->className = $className;
 		$this->constantHassers = $configuration->hasConstantHassers();
 		$this->serviceMap = $symfonyServiceMap;
+		$this->parameterBag = $this->createParameterBag($symfonyParameterMap);
 	}
 
 	public function getClass(): string
@@ -70,7 +82,7 @@ private function getGetTypeFromMethodCall(
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
 			if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) {
-				return new ObjectType($service->getClass() ?? $serviceId);
+				return new ObjectType($this->determineServiceClass($service) ?? $serviceId);
 			}
 		}
 
@@ -97,4 +109,20 @@ private function getHasTypeFromMethodCall(
 		return $returnType;
 	}
 
+	private function determineServiceClass(ServiceDefinition $service): ?string
+	{
+		return $this->parameterBag->resolveValue($service->getClass());
+	}
+
+	private function createParameterBag(ParameterMap $symfonyParameterMap): ParameterBag
+	{
+		$parameters = [];
+
+		foreach ($symfonyParameterMap->getParameters() as $parameterDefinition) {
+			$parameters[$parameterDefinition->getKey()] = $parameterDefinition->getValue();
+		}
+
+		return new ParameterBag($parameters);
+	}
+
 }
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index e6f8f03b..992289f2 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
     <parameters>
+        <parameter key="app.class">Foo</parameter>
         <parameter key="app.string">abcdef</parameter>
         <parameter key="app.int">123</parameter>
         <parameter key="app.int_as_string" type="string">123</parameter>
@@ -51,6 +52,8 @@
 
     <services>
         <service id="foo" class="Foo" public="true"></service>
+        <service id="parameterised_foo" class="%app.class%"></service>
+        <service id="parameterised_bar" class="%app.class%\Bar"></service>
         <service id="synthetic" class="Synthetic" public="true" synthetic="true" />
     </services>
 </container>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index 6dc6ef7e..6fdb281d 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -13,6 +13,8 @@ final class ExampleAbstractController extends AbstractController
 	public function services(): void
 	{
 		assertType('Foo', $this->get('foo'));
+		assertType('Foo', $this->get('parameterised_foo'));
+		assertType('Foo\Bar', $this->get('parameterised_bar'));
 		assertType('Synthetic', $this->get('synthetic'));
 		assertType('object', $this->get('bar'));
 		assertType('object', $this->get(doFoo()));
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index 8cd0c93a..b09cc25d 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -13,6 +13,8 @@ final class ExampleController extends Controller
 	public function services(): void
 	{
 		assertType('Foo', $this->get('foo'));
+		assertType('Foo', $this->get('parameterised_foo'));
+		assertType('Foo\Bar', $this->get('parameterised_bar'));
 		assertType('Synthetic', $this->get('synthetic'));
 		assertType('object', $this->get('bar'));
 		assertType('object', $this->get(doFoo()));

From 8ef80f5c4f70ebd31a55647bc082ea9df4bf0132 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 25 Feb 2022 20:36:13 +0100
Subject: [PATCH 157/297] Fix for ParameterBag class not present

---
 .../ServiceDynamicReturnTypeExtension.php     | 54 +++++++++++++------
 1 file changed, 38 insertions(+), 16 deletions(-)

diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index cd3d2a46..a2168a91 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -16,6 +16,7 @@
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use function class_exists;
 use function in_array;
 
 final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -30,7 +31,10 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
 	/** @var ServiceMap */
 	private $serviceMap;
 
-	/** @var ParameterBag */
+	/** @var ParameterMap */
+	private $parameterMap;
+
+	/** @var ParameterBag|null */
 	private $parameterBag;
 
 	public function __construct(
@@ -43,7 +47,7 @@ public function __construct(
 		$this->className = $className;
 		$this->constantHassers = $configuration->hasConstantHassers();
 		$this->serviceMap = $symfonyServiceMap;
-		$this->parameterBag = $this->createParameterBag($symfonyParameterMap);
+		$this->parameterMap = $symfonyParameterMap;
 	}
 
 	public function getClass(): string
@@ -78,17 +82,46 @@ private function getGetTypeFromMethodCall(
 			return $returnType;
 		}
 
+		$parameterBag = $this->tryGetParameterBag();
+		if ($parameterBag === null) {
+			return $returnType;
+		}
+
 		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope);
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
 			if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) {
-				return new ObjectType($this->determineServiceClass($service) ?? $serviceId);
+				return new ObjectType($this->determineServiceClass($parameterBag, $service) ?? $serviceId);
 			}
 		}
 
 		return $returnType;
 	}
 
+	private function tryGetParameterBag(): ?ParameterBag
+	{
+		if ($this->parameterBag !== null) {
+			return $this->parameterBag;
+		}
+
+		return $this->parameterBag = $this->tryCreateParameterBag();
+	}
+
+	private function tryCreateParameterBag(): ?ParameterBag
+	{
+		if (!class_exists(ParameterBag::class)) {
+			return null;
+		}
+
+		$parameters = [];
+
+		foreach ($this->parameterMap->getParameters() as $parameterDefinition) {
+			$parameters[$parameterDefinition->getKey()] = $parameterDefinition->getValue();
+		}
+
+		return new ParameterBag($parameters);
+	}
+
 	private function getHasTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
@@ -109,20 +142,9 @@ private function getHasTypeFromMethodCall(
 		return $returnType;
 	}
 
-	private function determineServiceClass(ServiceDefinition $service): ?string
-	{
-		return $this->parameterBag->resolveValue($service->getClass());
-	}
-
-	private function createParameterBag(ParameterMap $symfonyParameterMap): ParameterBag
+	private function determineServiceClass(ParameterBag $parameterBag, ServiceDefinition $service): ?string
 	{
-		$parameters = [];
-
-		foreach ($symfonyParameterMap->getParameters() as $parameterDefinition) {
-			$parameters[$parameterDefinition->getKey()] = $parameterDefinition->getValue();
-		}
-
-		return new ParameterBag($parameters);
+		return $parameterBag->resolveValue($service->getClass());
 	}
 
 }

From b6bcbea8204fd6e09c5587228ff448d0414de54c Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Mon, 7 Mar 2022 01:48:35 +0000
Subject: [PATCH 158/297] Update actions/checkout action to v3

---
 .github/workflows/build.yml   | 8 ++++----
 .github/workflows/release.yml | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bccd397b..7afaf7bf 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,7 +25,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: "actions/checkout@v2"
+        uses: actions/checkout@v3
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
@@ -53,7 +53,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: "actions/checkout@v2"
+        uses: actions/checkout@v3
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
@@ -96,7 +96,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: "actions/checkout@v2"
+        uses: actions/checkout@v3
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
@@ -139,7 +139,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: "actions/checkout@v2"
+        uses: actions/checkout@v3
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0ebed840..5fed0458 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -14,7 +14,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: "actions/checkout@v2"
+        uses: actions/checkout@v3
 
       - name: Generate changelog
         id: changelog

From 79a26b97a85af36367fc90fe2cef481e66c27bf3 Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Mon, 7 Mar 2022 01:48:32 +0000
Subject: [PATCH 159/297] Update dependency slevomat/coding-standard to v7.0.19

---
 build-cs/composer.lock | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 70af78f9..96177edd 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -200,32 +200,32 @@
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "7.0.18",
+            "version": "7.0.19",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc"
+                "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b81ac84f41a4797dc25c8ede1b0718e2a74be0fc",
-                "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37",
+                "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37",
                 "shasum": ""
             },
             "require": {
                 "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
                 "php": "^7.1 || ^8.0",
                 "phpstan/phpdoc-parser": "^1.0.0",
-                "squizlabs/php_codesniffer": "^3.6.1"
+                "squizlabs/php_codesniffer": "^3.6.2"
             },
             "require-dev": {
-                "phing/phing": "2.17.0",
-                "php-parallel-lint/php-parallel-lint": "1.3.1",
-                "phpstan/phpstan": "1.2.0",
+                "phing/phing": "2.17.2",
+                "php-parallel-lint/php-parallel-lint": "1.3.2",
+                "phpstan/phpstan": "1.4.6",
                 "phpstan/phpstan-deprecation-rules": "1.0.0",
                 "phpstan/phpstan-phpunit": "1.0.0",
                 "phpstan/phpstan-strict-rules": "1.1.0",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.5.10"
+                "phpunit/phpunit": "7.5.20|8.5.21|9.5.16"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
@@ -245,7 +245,7 @@
             "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/7.0.18"
+                "source": "https://github.com/slevomat/coding-standard/tree/7.0.19"
             },
             "funding": [
                 {
@@ -257,7 +257,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-12-07T17:19:06+00:00"
+            "time": "2022-03-01T18:01:41+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From 4bdffcf1033b6c60662a28419d0192e0dda5684f Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Tue, 22 Mar 2022 22:18:26 +0100
Subject: [PATCH 160/297] Update DenormalizerInterface::denormalize

---
 .../Component/Serializer/Normalizer/DenormalizerInterface.stub  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
index 83c6ddff..da4ab673 100644
--- a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
@@ -9,7 +9,7 @@ interface DenormalizerInterface
      * @param string $type
      * @param string|null $format
      * @param array<mixed> $context
-     * @return object|array<mixed>
+     * @return mixed
      */
     public function denormalize($data, $type, $format = null, array $context = []);
 

From 31a8e6366a628ce4a788cee06f2292a965832fe0 Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Mon, 28 Mar 2022 09:40:14 +0000
Subject: [PATCH 161/297] Update dependency slevomat/coding-standard to v7.0.20

---
 build-cs/composer.lock | 37 ++++++++++++++++---------------------
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 96177edd..54c9a22c 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -151,35 +151,30 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.2.0",
+            "version": "1.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e"
+                "reference": "c08946968bda74869e696982b0af40a9a8784c84"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
-                "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c08946968bda74869e696982b0af40a9a8784c84",
+                "reference": "c08946968bda74869e696982b0af40a9a8784c84",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1 || ^8.0"
+                "php": "^7.2 || ^8.0"
             },
             "require-dev": {
                 "php-parallel-lint/php-parallel-lint": "^1.2",
                 "phpstan/extension-installer": "^1.0",
-                "phpstan/phpstan": "^1.0",
+                "phpstan/phpstan": "^1.5",
                 "phpstan/phpstan-strict-rules": "^1.0",
                 "phpunit/phpunit": "^9.5",
                 "symfony/process": "^5.2"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "PHPStan\\PhpDocParser\\": [
@@ -194,22 +189,22 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.2.0"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.3.0"
             },
-            "time": "2021-09-16T20:46:02+00:00"
+            "time": "2022-03-28T07:53:31+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "7.0.19",
+            "version": "7.0.20",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37"
+                "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37",
-                "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cbfadfe34c2c29473bf1e891306b3950b3b4350b",
+                "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b",
                 "shasum": ""
             },
             "require": {
@@ -221,11 +216,11 @@
             "require-dev": {
                 "phing/phing": "2.17.2",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.6",
+                "phpstan/phpstan": "1.4.10|1.5.0",
                 "phpstan/phpstan-deprecation-rules": "1.0.0",
                 "phpstan/phpstan-phpunit": "1.0.0",
                 "phpstan/phpstan-strict-rules": "1.1.0",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.5.16"
+                "phpunit/phpunit": "7.5.20|8.5.21|9.5.19"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
@@ -245,7 +240,7 @@
             "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/7.0.19"
+                "source": "https://github.com/slevomat/coding-standard/tree/7.0.20"
             },
             "funding": [
                 {
@@ -257,7 +252,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-03-01T18:01:41+00:00"
+            "time": "2022-03-25T09:43:20+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From 02dceb254659bd9e7db4dee623add012853b0b42 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 28 Mar 2022 11:52:17 +0200
Subject: [PATCH 162/297] Drop support for PHP 7.1, require PHPStan 1.5.0

---
 .github/workflows/build.yml | 12 +++---------
 composer.json               |  4 ++--
 2 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7afaf7bf..8e19c3ed 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,7 +16,6 @@ jobs:
     strategy:
       matrix:
         php-version:
-          - "7.1"
           - "7.2"
           - "7.3"
           - "7.4"
@@ -40,7 +39,7 @@ jobs:
         run: "composer install --no-interaction --no-progress"
 
       - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3'
+        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"
@@ -81,7 +80,6 @@ jobs:
       fail-fast: false
       matrix:
         php-version:
-          - "7.1"
           - "7.2"
           - "7.3"
           - "7.4"
@@ -90,9 +88,6 @@ jobs:
         dependencies:
           - "lowest"
           - "highest"
-        exclude:
-          - php-version: "7.1"
-            dependencies: "highest"
 
     steps:
       - name: "Checkout"
@@ -113,7 +108,7 @@ jobs:
         run: "composer update --no-interaction --no-progress"
 
       - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3'
+        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"
@@ -127,7 +122,6 @@ jobs:
       fail-fast: false
       matrix:
         php-version:
-          - "7.1"
           - "7.2"
           - "7.3"
           - "7.4"
@@ -158,7 +152,7 @@ jobs:
         run: "composer update --no-interaction --no-progress"
 
       - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3'
+        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"
diff --git a/composer.json b/composer.json
index 5b5438ff..7abefc05 100644
--- a/composer.json
+++ b/composer.json
@@ -13,9 +13,9 @@
     }
   ],
   "require": {
-    "php": "^7.1 || ^8.0",
+    "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.4"
+    "phpstan/phpstan": "^1.5"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From 31d7dad0da29322d556524d3645d4e768f098dfe Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 28 Mar 2022 11:53:32 +0200
Subject: [PATCH 163/297] Update workflow

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

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8e19c3ed..38309f6a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "master"
+      - "1.2.x"
 
 jobs:
   lint:

From 513f395f79da61e6c74cf92e6e3842db1f96673e Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 28 Mar 2022 11:53:49 +0200
Subject: [PATCH 164/297] Drop alias

---
 composer.json | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/composer.json b/composer.json
index 7abefc05..723c1163 100644
--- a/composer.json
+++ b/composer.json
@@ -44,9 +44,6 @@
     "sort-packages": true
   },
   "extra": {
-    "branch-alias": {
-      "dev-master": "1.0-dev"
-    },
     "phpstan": {
       "includes": [
         "extension.neon",

From 7953474b9af0872baaa469ddd041887aba0a3a63 Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Mon, 4 Apr 2022 01:16:16 +0000
Subject: [PATCH 165/297] Update dependency slevomat/coding-standard to v7.1

---
 build-cs/composer.lock | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 54c9a22c..5a10fffd 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -151,16 +151,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.3.0",
+            "version": "1.4.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "c08946968bda74869e696982b0af40a9a8784c84"
+                "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c08946968bda74869e696982b0af40a9a8784c84",
-                "reference": "c08946968bda74869e696982b0af40a9a8784c84",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d",
+                "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d",
                 "shasum": ""
             },
             "require": {
@@ -189,36 +189,36 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.3.0"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.4.2"
             },
-            "time": "2022-03-28T07:53:31+00:00"
+            "time": "2022-03-30T13:33:37+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "7.0.20",
+            "version": "7.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b"
+                "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cbfadfe34c2c29473bf1e891306b3950b3b4350b",
-                "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b521bd358b5f7a7d69e9637fd139e036d8adeb6f",
+                "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f",
                 "shasum": ""
             },
             "require": {
                 "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
-                "php": "^7.1 || ^8.0",
-                "phpstan/phpdoc-parser": "^1.0.0",
+                "php": "^7.2 || ^8.0",
+                "phpstan/phpdoc-parser": "^1.4.1",
                 "squizlabs/php_codesniffer": "^3.6.2"
             },
             "require-dev": {
                 "phing/phing": "2.17.2",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.10|1.5.0",
+                "phpstan/phpstan": "1.4.10|1.5.2",
                 "phpstan/phpstan-deprecation-rules": "1.0.0",
-                "phpstan/phpstan-phpunit": "1.0.0",
+                "phpstan/phpstan-phpunit": "1.0.0|1.1.0",
                 "phpstan/phpstan-strict-rules": "1.1.0",
                 "phpunit/phpunit": "7.5.20|8.5.21|9.5.19"
             },
@@ -240,7 +240,7 @@
             "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/7.0.20"
+                "source": "https://github.com/slevomat/coding-standard/tree/7.1"
             },
             "funding": [
                 {
@@ -252,7 +252,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-03-25T09:43:20+00:00"
+            "time": "2022-03-29T12:44:16+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",
@@ -318,5 +318,5 @@
     "prefer-lowest": false,
     "platform": [],
     "platform-dev": [],
-    "plugin-api-version": "2.2.0"
+    "plugin-api-version": "2.3.0"
 }

From e32e40e7f82b0fcb3e0dd4a38362034f895a11c5 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 18 Apr 2022 21:35:06 +0200
Subject: [PATCH 166/297] Require PHPStan 1.6.0

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 723c1163..a563528a 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.5"
+    "phpstan/phpstan": "^1.6"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From 811fb232c30000b7572c715f049a7fe6b9676d31 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 18 Apr 2022 21:35:14 +0200
Subject: [PATCH 167/297] Fix

---
 src/Symfony/XmlParameterMapFactory.php | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php
index 0891423a..1467d050 100644
--- a/src/Symfony/XmlParameterMapFactory.php
+++ b/src/Symfony/XmlParameterMapFactory.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Symfony;
 
 use InvalidArgumentException;
+use PHPStan\ShouldNotHappenException;
 use SimpleXMLElement;
 use function base64_decode;
 use function file_get_contents;
@@ -67,7 +68,11 @@ private function getNodeValue(SimpleXMLElement $def)
 		switch ((string) $attrs->type) {
 			case 'collection':
 				$value = [];
-				foreach ($def->children() as $child) {
+				$children = $def->children();
+				if ($children === null) {
+					throw new ShouldNotHappenException();
+				}
+				foreach ($children as $child) {
 					/** @var SimpleXMLElement $childAttrs */
 					$childAttrs = $child->attributes();
 

From b1c515245615797c081105be04d5741c3deda843 Mon Sep 17 00:00:00 2001
From: Thomas Gnandt <thomas.gnandt@mayflower.de>
Date: Fri, 29 Apr 2022 14:56:07 +0200
Subject: [PATCH 168/297] add documentation to load DI helper

---
 README.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/README.md b/README.md
index 7fe1a8bb..663e0381 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,9 @@ parameters:
     # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`:
     scanDirectories:
         - var/cache/dev/Symfony/Config
+    # If you're using PHP config files you need this to load the helper functions (i.e. service())
+    scanFiles:
+        - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php
 ```
 
 ## Constant hassers

From 94e6c34d81ee842d9ace775698c86596aaed3ffc Mon Sep 17 00:00:00 2001
From: Thomas Gnandt <thomas.gnandt@mayflower.de>
Date: Thu, 5 May 2022 15:58:38 +0200
Subject: [PATCH 169/297] Revert "add documentation to load DI helper"

This reverts commit b1c515245615797c081105be04d5741c3deda843.
---
 README.md | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/README.md b/README.md
index 663e0381..7fe1a8bb 100644
--- a/README.md
+++ b/README.md
@@ -69,9 +69,6 @@ parameters:
     # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`:
     scanDirectories:
         - var/cache/dev/Symfony/Config
-    # If you're using PHP config files you need this to load the helper functions (i.e. service())
-    scanFiles:
-        - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php
 ```
 
 ## Constant hassers

From 063ce41831590e55994c47ba734f3defacb14f4a Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Mon, 9 May 2022 01:47:25 +0000
Subject: [PATCH 170/297] Update dependency slevomat/coding-standard to v7.2.0

---
 build-cs/composer.lock | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 5a10fffd..6e7fb818 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -151,16 +151,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.4.2",
+            "version": "1.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d"
+                "reference": "981cc368a216c988e862a75e526b6076987d1b50"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d",
-                "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50",
+                "reference": "981cc368a216c988e862a75e526b6076987d1b50",
                 "shasum": ""
             },
             "require": {
@@ -189,38 +189,38 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.4.2"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1"
             },
-            "time": "2022-03-30T13:33:37+00:00"
+            "time": "2022-05-05T11:32:40+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "7.1",
+            "version": "7.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f"
+                "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b521bd358b5f7a7d69e9637fd139e036d8adeb6f",
-                "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b4f96a8beea515d2d89141b7b9ad72f526d84071",
+                "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071",
                 "shasum": ""
             },
             "require": {
                 "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
                 "php": "^7.2 || ^8.0",
-                "phpstan/phpdoc-parser": "^1.4.1",
+                "phpstan/phpdoc-parser": "^1.5.1",
                 "squizlabs/php_codesniffer": "^3.6.2"
             },
             "require-dev": {
-                "phing/phing": "2.17.2",
+                "phing/phing": "2.17.3",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.10|1.5.2",
+                "phpstan/phpstan": "1.4.10|1.6.7",
                 "phpstan/phpstan-deprecation-rules": "1.0.0",
-                "phpstan/phpstan-phpunit": "1.0.0|1.1.0",
-                "phpstan/phpstan-strict-rules": "1.1.0",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.5.19"
+                "phpstan/phpstan-phpunit": "1.0.0|1.1.1",
+                "phpstan/phpstan-strict-rules": "1.2.3",
+                "phpunit/phpunit": "7.5.20|8.5.21|9.5.20"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
@@ -240,7 +240,7 @@
             "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/7.1"
+                "source": "https://github.com/slevomat/coding-standard/tree/7.2.0"
             },
             "funding": [
                 {
@@ -252,7 +252,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-03-29T12:44:16+00:00"
+            "time": "2022-05-06T10:58:42+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From 5d2d5ad60dbdefd234b5e58ef43fc3f077a312ed Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Fri, 27 May 2022 10:37:19 +0200
Subject: [PATCH 171/297] Fix formInterface stub

---
 stubs/Symfony/Component/Form/FormInterface.stub | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/stubs/Symfony/Component/Form/FormInterface.stub b/stubs/Symfony/Component/Form/FormInterface.stub
index 868c4ae2..6960cce5 100644
--- a/stubs/Symfony/Component/Form/FormInterface.stub
+++ b/stubs/Symfony/Component/Form/FormInterface.stub
@@ -3,9 +3,10 @@
 namespace Symfony\Component\Form;
 
 /**
- * @extends \Traversable<string|int, \Symfony\Component\Form\FormInterface>
+ * @extends \ArrayAccess<string, \Symfony\Component\Form\FormInterface>
+ * @extends \Traversable<string, \Symfony\Component\Form\FormInterface>
  */
-interface FormInterface extends \Traversable
+interface FormInterface extends \ArrayAccess, \Traversable, \Countable
 {
 
 }

From 2063d60f1bfe97c5702a839b3bc68d7f3fa75318 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Sat, 28 May 2022 17:11:34 +0200
Subject: [PATCH 172/297] DIC parameters: do not generalize string-indexed
 arrays

---
 .../ParameterDynamicReturnTypeExtension.php   | 24 +++++++++++++++
 tests/Type/Symfony/container.xml              | 30 +++++++++++++++++++
 .../data/ExampleAbstractController.php        | 12 ++++++--
 ...mpleAbstractControllerWithoutContainer.php |  3 ++
 tests/Type/Symfony/data/ExampleController.php | 12 ++++++--
 .../ExampleControllerWithoutContainer.php     |  3 ++
 6 files changed, 78 insertions(+), 6 deletions(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 0fa8e7bc..6a197a8f 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -14,6 +14,8 @@
 use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantArrayType;
 use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\Constant\ConstantIntegerType;
+use PHPStan\Type\Constant\ConstantStringType;
 use PHPStan\Type\ConstantType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FloatType;
@@ -35,6 +37,7 @@
 use function count;
 use function in_array;
 use function is_array;
+use function is_int;
 use function is_string;
 use function preg_match;
 use function strlen;
@@ -139,6 +142,27 @@ private function getGetTypeFromMethodCall(
 	private function generalizeTypeFromValue(Scope $scope, $value): Type
 	{
 		if (is_array($value) && $value !== []) {
+			$hasOnlyStringKey = true;
+			foreach (array_keys($value) as $key) {
+				if (is_int($key)) {
+					$hasOnlyStringKey = false;
+					break;
+				}
+			}
+
+			if ($hasOnlyStringKey) {
+				$keyTypes = [];
+				$valueTypes = [];
+				foreach ($value as $key => $element) {
+					/** @var ConstantIntegerType|ConstantStringType $keyType */
+					$keyType = $scope->getTypeFromValue($key);
+					$keyTypes[] = $keyType;
+					$valueTypes[] = $this->generalizeTypeFromValue($scope, $element);
+				}
+
+				return new ConstantArrayType($keyTypes, $valueTypes);
+			}
+
 			return $this->generalizeType(
 				new ArrayType(
 					TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index 992289f2..5a14f9b5 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -37,6 +37,36 @@
                 <parameter key="value">another value</parameter>
             </parameter>
         </parameter>
+        <parameter key="app.array_of_list" type="collection">
+            <parameter key="foo" type="collection">
+                <parameter key="name">the name</parameter>
+                <parameter key="value">the value</parameter>
+            </parameter>
+            <parameter type="collection">
+                <parameter key="name">another name</parameter>
+                <parameter key="value">another value</parameter>
+            </parameter>
+        </parameter>
+        <parameter key="app.list_of_things" type="collection">
+            <parameter key="url">%env(string:APP_STRING)%</parameter>
+            <parameter key="endpoint">%env(string:APP_STRING)%</parameter>
+            <parameter key="version">%env(string:APP_STRING)%</parameter>
+            <parameter key="payment" type="collection">
+                <parameter key="default" type="collection">
+                    <parameter key="username">%env(string:APP_STRING)%</parameter>
+                    <parameter key="password">%env(string:APP_STRING)%</parameter>
+                    <parameter key="signature">%env(string:APP_STRING)%</parameter>
+                </parameter>
+            </parameter>
+            <parameter key="api" type="collection">
+                <parameter key="mode">%env(string:APP_STRING)%</parameter>
+                <parameter key="default" type="collection">
+                    <parameter key="username">%env(string:APP_STRING)%</parameter>
+                    <parameter key="password">%env(string:APP_STRING)%</parameter>
+                    <parameter key="signature">%env(string:APP_STRING)%</parameter>
+                </parameter>
+            </parameter>
+        </parameter>
         <parameter key="app.map" type="collection">
             <parameter key="a">value of a</parameter>
             <parameter key="b">value of b</parameter>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index 6fdb281d..8f599d7b 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -68,9 +68,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
 		assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
 		assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
-		assertType("array<string, string>", $container->getParameter('app.map'));
-		assertType("array<string, string>", $parameterBag->get('app.map'));
-		assertType("array<string, string>", $this->getParameter('app.map'));
+		assertType("array<int|string, array<string, string>>", $container->getParameter('app.array_of_list'));
+		assertType("array<int|string, array<string, string>>", $parameterBag->get('app.array_of_list'));
+		assertType("array<int|string, array<string, string>>", $this->getParameter('app.array_of_list'));
+		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things'));
+		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things'));
+		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things'));
+		assertType("array{a: string, b: string, c: string}", $container->getParameter('app.map'));
+		assertType("array{a: string, b: string, c: string}", $parameterBag->get('app.map'));
+		assertType("array{a: string, b: string, c: string}", $this->getParameter('app.map'));
 		assertType("string", $container->getParameter('app.binary'));
 		assertType("string", $parameterBag->get('app.binary'));
 		assertType("string", $this->getParameter('app.binary'));
diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
index 26d0f366..edc6438a 100644
--- a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
+++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php
@@ -65,6 +65,9 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.array_of_list'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.array_of_list'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.array_of_list'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.map'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.map'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.map'));
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index b09cc25d..89d2b5f8 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -68,9 +68,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
 		assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
 		assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
-		assertType("array<string, string>", $container->getParameter('app.map'));
-		assertType("array<string, string>", $parameterBag->get('app.map'));
-		assertType("array<string, string>", $this->getParameter('app.map'));
+		assertType("array<int|string, array<string, string>>", $container->getParameter('app.array_of_list'));
+		assertType("array<int|string, array<string, string>>", $parameterBag->get('app.array_of_list'));
+		assertType("array<int|string, array<string, string>>", $this->getParameter('app.array_of_list'));
+		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things'));
+		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things'));
+		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things'));
+		assertType("array{a: string, b: string, c: string}", $container->getParameter('app.map'));
+		assertType("array{a: string, b: string, c: string}", $parameterBag->get('app.map'));
+		assertType("array{a: string, b: string, c: string}", $this->getParameter('app.map'));
 		assertType("string", $container->getParameter('app.binary'));
 		assertType("string", $parameterBag->get('app.binary'));
 		assertType("string", $this->getParameter('app.binary'));
diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
index fc48c0e5..2e48dc80 100644
--- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
+++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php
@@ -65,6 +65,9 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list'));
+		assertType('array|bool|float|int|string|null', $container->getParameter('app.array_of_list'));
+		assertType('array|bool|float|int|string|null', $parameterBag->get('app.array_of_list'));
+		assertType('array|bool|float|int|string|null', $this->getParameter('app.array_of_list'));
 		assertType('array|bool|float|int|string|null', $container->getParameter('app.map'));
 		assertType('array|bool|float|int|string|null', $parameterBag->get('app.map'));
 		assertType('array|bool|float|int|string|null', $this->getParameter('app.map'));

From 30f12aeab960c7f324eee3b39645655cf8a84146 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Sat, 28 May 2022 17:18:51 +0200
Subject: [PATCH 173/297] Try to support union of strings as parameter name

---
 src/Symfony/DefaultParameterMap.php           | 10 ++--
 src/Symfony/FakeParameterMap.php              |  4 +-
 src/Symfony/ParameterMap.php                  |  5 +-
 .../ParameterDynamicReturnTypeExtension.php   | 46 +++++++++++++------
 tests/Type/Symfony/data/ExampleController.php | 21 +++++++++
 5 files changed, 67 insertions(+), 19 deletions(-)

diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php
index fe29b34f..26317468 100644
--- a/src/Symfony/DefaultParameterMap.php
+++ b/src/Symfony/DefaultParameterMap.php
@@ -4,8 +4,9 @@
 
 use PhpParser\Node\Expr;
 use PHPStan\Analyser\Scope;
+use PHPStan\Type\Type;
 use PHPStan\Type\TypeUtils;
-use function count;
+use function array_map;
 
 final class DefaultParameterMap implements ParameterMap
 {
@@ -34,10 +35,13 @@ public function getParameter(string $key): ?ParameterDefinition
 		return $this->parameters[$key] ?? null;
 	}
 
-	public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
+	public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
 	{
 		$strings = TypeUtils::getConstantStrings($scope->getType($node));
-		return count($strings) === 1 ? $strings[0]->getValue() : null;
+
+		return array_map(static function (Type $type) {
+			return $type->getValue();
+		}, $strings);
 	}
 
 }
diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php
index 0bf450e7..53acdc3c 100644
--- a/src/Symfony/FakeParameterMap.php
+++ b/src/Symfony/FakeParameterMap.php
@@ -21,9 +21,9 @@ public function getParameter(string $key): ?ParameterDefinition
 		return null;
 	}
 
-	public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
+	public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
 	{
-		return null;
+		return [];
 	}
 
 }
diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php
index c768e2f2..ff0f5224 100644
--- a/src/Symfony/ParameterMap.php
+++ b/src/Symfony/ParameterMap.php
@@ -15,6 +15,9 @@ public function getParameters(): array;
 
 	public function getParameter(string $key): ?ParameterDefinition;
 
-	public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string;
+	/**
+	 * @return array<string>
+	 */
+	public static function getParameterKeysFromNode(Expr $node, Scope $scope): array;
 
 }
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 6a197a8f..b18a386f 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -113,7 +113,7 @@ private function getGetTypeFromMethodCall(
 	{
 		// We don't use the method's return type because this won't work properly with lowest and
 		// highest versions of Symfony ("mixed" for lowest, "array|bool|float|integer|string|null" for highest).
-		$returnType = new UnionType([
+		$defaultReturnType = new UnionType([
 			new ArrayType(new MixedType(), new MixedType()),
 			new BooleanType(),
 			new FloatType(),
@@ -122,18 +122,25 @@ private function getGetTypeFromMethodCall(
 			new NullType(),
 		]);
 		if (!isset($methodCall->getArgs()[0])) {
-			return $returnType;
+			return $defaultReturnType;
 		}
 
-		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
-		if ($parameterKey !== null) {
+		$parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope);
+		if ($parameterKeys === []) {
+			return $defaultReturnType;
+		}
+
+		$returnTypes = [];
+		foreach ($parameterKeys as $parameterKey) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
-			if ($parameter !== null) {
-				return $this->generalizeTypeFromValue($scope, $parameter->getValue());
+			if ($parameter === null) {
+				return $defaultReturnType;
 			}
+
+			$returnTypes[] = $this->generalizeTypeFromValue($scope, $parameter->getValue());
 		}
 
-		return $returnType;
+		return TypeCombinator::union(...$returnTypes);
 	}
 
 	/**
@@ -211,18 +218,31 @@ private function getHasTypeFromMethodCall(
 		Scope $scope
 	): Type
 	{
-		$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		$defaultReturnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
 		if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) {
-			return $returnType;
+			return $defaultReturnType;
 		}
 
-		$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
-		if ($parameterKey !== null) {
+		$parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope);
+		if ($parameterKeys === []) {
+			return $defaultReturnType;
+		}
+
+		$has = null;
+		foreach ($parameterKeys as $parameterKey) {
 			$parameter = $this->parameterMap->getParameter($parameterKey);
-			return new ConstantBooleanType($parameter !== null);
+
+			if ($has === null) {
+				$has = $parameter !== null;
+			} elseif (
+				($has === true && $parameter === null)
+				|| ($has === false && $parameter !== null)
+			) {
+				return $defaultReturnType;
+			}
 		}
 
-		return $returnType;
+		return new ConstantBooleanType($has);
 	}
 
 }
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index 89d2b5f8..528b06a7 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -116,6 +116,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType('true', $parameterBag->has('app.binary'));
 		assertType('true', $container->hasParameter('app.constant'));
 		assertType('true', $parameterBag->has('app.constant'));
+
+		$key = rand(0, 1) ? 'app.string' : 'app.int';
+		assertType("int|string", $container->getParameter($key));
+		assertType("int|string", $parameterBag->get($key));
+		assertType("int|string", $this->getParameter($key));
+		assertType('true', $container->hasParameter($key));
+		assertType('true', $parameterBag->has($key));
+
+		$key = rand(0, 1) ? 'app.string' : 'app.foo';
+		assertType("array|bool|float|int|string|null", $container->getParameter($key));
+		assertType("array|bool|float|int|string|null", $parameterBag->get($key));
+		assertType("array|bool|float|int|string|null", $this->getParameter($key));
+		assertType('bool', $container->hasParameter($key));
+		assertType('bool', $parameterBag->has($key));
+
+		$key = rand(0, 1) ? 'app.bar' : 'app.foo';
+		assertType("array|bool|float|int|string|null", $container->getParameter($key));
+		assertType("array|bool|float|int|string|null", $parameterBag->get($key));
+		assertType("array|bool|float|int|string|null", $this->getParameter($key));
+		assertType('false', $container->hasParameter($key));
+		assertType('false', $parameterBag->has($key));
 	}
 
 }

From 26b650a7eb5b2d4ac63e4164c470d6949c240787 Mon Sep 17 00:00:00 2001
From: Renovate Bot <bot@renovateapp.com>
Date: Mon, 30 May 2022 00:04:50 +0000
Subject: [PATCH 174/297] Update dependency slevomat/coding-standard to v7.2.1

---
 build-cs/composer.lock | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 6e7fb818..4bcc8de4 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -195,16 +195,16 @@
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "7.2.0",
+            "version": "7.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071"
+                "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b4f96a8beea515d2d89141b7b9ad72f526d84071",
-                "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90",
+                "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90",
                 "shasum": ""
             },
             "require": {
@@ -216,7 +216,7 @@
             "require-dev": {
                 "phing/phing": "2.17.3",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.10|1.6.7",
+                "phpstan/phpstan": "1.4.10|1.7.1",
                 "phpstan/phpstan-deprecation-rules": "1.0.0",
                 "phpstan/phpstan-phpunit": "1.0.0|1.1.1",
                 "phpstan/phpstan-strict-rules": "1.2.3",
@@ -240,7 +240,7 @@
             "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/7.2.0"
+                "source": "https://github.com/slevomat/coding-standard/tree/7.2.1"
             },
             "funding": [
                 {
@@ -252,7 +252,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-06T10:58:42+00:00"
+            "time": "2022-05-25T10:58:12+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From 7c24540375a53538190190496f077f76b14c63fb Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 3 Jun 2022 15:22:15 +0200
Subject: [PATCH 175/297] Fix stub

---
 stubs/Symfony/Component/HttpFoundation/Request.stub | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub
index f32365db..e554ec95 100644
--- a/stubs/Symfony/Component/HttpFoundation/Request.stub
+++ b/stubs/Symfony/Component/HttpFoundation/Request.stub
@@ -29,6 +29,6 @@ class Request
     /**
      * @return \Symfony\Component\HttpFoundation\Session\Session
      */
-    public function getSession(): SessionInterface;
+    public function getSession();
 
 }

From 9451f103bfbd7bac5d236405eb97106a37930197 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Thu, 9 Jun 2022 14:00:34 +0200
Subject: [PATCH 176/297] ParameterDynamicReturnTypeExtension: Only
 generalizeType for keys

---
 .../ParameterDynamicReturnTypeExtension.php   | 19 ++++++++-----------
 tests/Type/Symfony/container.xml              | 10 ++++++++++
 .../data/ExampleAbstractController.php        | 15 +++++++++------
 tests/Type/Symfony/data/ExampleController.php | 15 +++++++++------
 4 files changed, 36 insertions(+), 23 deletions(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index b18a386f..b160ddc8 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -14,7 +14,6 @@
 use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantArrayType;
 use PHPStan\Type\Constant\ConstantBooleanType;
-use PHPStan\Type\Constant\ConstantIntegerType;
 use PHPStan\Type\Constant\ConstantStringType;
 use PHPStan\Type\ConstantType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
@@ -161,7 +160,7 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type
 				$keyTypes = [];
 				$valueTypes = [];
 				foreach ($value as $key => $element) {
-					/** @var ConstantIntegerType|ConstantStringType $keyType */
+					/** @var ConstantStringType $keyType */
 					$keyType = $scope->getTypeFromValue($key);
 					$keyTypes[] = $keyType;
 					$valueTypes[] = $this->generalizeTypeFromValue($scope, $element);
@@ -170,15 +169,13 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type
 				return new ConstantArrayType($keyTypes, $valueTypes);
 			}
 
-			return $this->generalizeType(
-				new ArrayType(
-					TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
-						return $this->generalizeTypeFromValue($scope, $item);
-					}, array_keys($value))),
-					TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
-						return $this->generalizeTypeFromValue($scope, $item);
-					}, array_values($value)))
-				)
+			return new ArrayType(
+				TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
+					return $this->generalizeTypeFromValue($scope, $item);
+				}, array_keys($value))),
+				TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
+					return $this->generalizeTypeFromValue($scope, $item);
+				}, array_values($value)))
 			);
 		}
 
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index 5a14f9b5..e1ef6d89 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -37,6 +37,16 @@
                 <parameter key="value">another value</parameter>
             </parameter>
         </parameter>
+        <parameter key="app.list_of_different_list" type="collection">
+            <parameter type="collection">
+                <parameter key="name">the name</parameter>
+                <parameter key="value">the value</parameter>
+            </parameter>
+            <parameter type="collection">
+                <parameter key="age">12</parameter>
+                <parameter key="size">32</parameter>
+            </parameter>
+        </parameter>
         <parameter key="app.array_of_list" type="collection">
             <parameter key="foo" type="collection">
                 <parameter key="name">the name</parameter>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index 8f599d7b..d9857a53 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -65,12 +65,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("array<int, int>", $container->getParameter('app.list_of_int_as_processor'));
 		assertType("array<int, int>", $parameterBag->get('app.list_of_int_as_processor'));
 		assertType("array<int, int>", $this->getParameter('app.list_of_int_as_processor'));
-		assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
-		assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
-		assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
-		assertType("array<int|string, array<string, string>>", $container->getParameter('app.array_of_list'));
-		assertType("array<int|string, array<string, string>>", $parameterBag->get('app.array_of_list'));
-		assertType("array<int|string, array<string, string>>", $this->getParameter('app.array_of_list'));
+		assertType("array<int, array{name: string, value: string}>", $container->getParameter('app.list_of_list'));
+		assertType("array<int, array{name: string, value: string}>", $parameterBag->get('app.list_of_list'));
+		assertType("array<int, array{name: string, value: string}>", $this->getParameter('app.list_of_list'));
+		assertType("array<int, array{age: int, size: int}|array{name: string, value: string}>", $container->getParameter('app.list_of_different_list'));
+		assertType("array<int, array{age: int, size: int}|array{name: string, value: string}>", $parameterBag->get('app.list_of_different_list'));
+		assertType("array<int, array{age: int, size: int}|array{name: string, value: string}>", $this->getParameter('app.list_of_different_list'));
+		assertType("array<int|string, array{name: string, value: string}>", $container->getParameter('app.array_of_list'));
+		assertType("array<int|string, array{name: string, value: string}>", $parameterBag->get('app.array_of_list'));
+		assertType("array<int|string, array{name: string, value: string}>", $this->getParameter('app.array_of_list'));
 		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things'));
 		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things'));
 		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things'));
diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php
index 528b06a7..c7563537 100644
--- a/tests/Type/Symfony/data/ExampleController.php
+++ b/tests/Type/Symfony/data/ExampleController.php
@@ -65,12 +65,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("array<int, string>", $container->getParameter('app.list'));
 		assertType("array<int, string>", $parameterBag->get('app.list'));
 		assertType("array<int, string>", $this->getParameter('app.list'));
-		assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
-		assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
-		assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
-		assertType("array<int|string, array<string, string>>", $container->getParameter('app.array_of_list'));
-		assertType("array<int|string, array<string, string>>", $parameterBag->get('app.array_of_list'));
-		assertType("array<int|string, array<string, string>>", $this->getParameter('app.array_of_list'));
+		assertType("array<int, array{name: string, value: string}>", $container->getParameter('app.list_of_list'));
+		assertType("array<int, array{name: string, value: string}>", $parameterBag->get('app.list_of_list'));
+		assertType("array<int, array{name: string, value: string}>", $this->getParameter('app.list_of_list'));
+		assertType("array<int, array{age: int, size: int}|array{name: string, value: string}>", $container->getParameter('app.list_of_different_list'));
+		assertType("array<int, array{age: int, size: int}|array{name: string, value: string}>", $parameterBag->get('app.list_of_different_list'));
+		assertType("array<int, array{age: int, size: int}|array{name: string, value: string}>", $this->getParameter('app.list_of_different_list'));
+		assertType("array<int|string, array{name: string, value: string}>", $container->getParameter('app.array_of_list'));
+		assertType("array<int|string, array{name: string, value: string}>", $parameterBag->get('app.array_of_list'));
+		assertType("array<int|string, array{name: string, value: string}>", $this->getParameter('app.array_of_list'));
 		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things'));
 		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things'));
 		assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things'));

From 85be852a17fd5a6b67d4fc6daed21e794f935b2d Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Fri, 10 Jun 2022 09:46:26 +0200
Subject: [PATCH 177/297] Type array in request methods

---
 .../Component/HttpFoundation/Request.stub     | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub
index e554ec95..861769df 100644
--- a/stubs/Symfony/Component/HttpFoundation/Request.stub
+++ b/stubs/Symfony/Component/HttpFoundation/Request.stub
@@ -31,4 +31,47 @@ class Request
      */
     public function getSession();
 
+    /**
+     * @return string[]
+     */
+    public static function getTrustedProxies(): array;
+
+    /**
+     * @return string[]
+     */
+    public static function getTrustedHosts(): array;
+
+    /**
+     * @param string $format
+     *
+     * @return string[]
+     */
+    public static function getMimeTypes($format): array;
+
+    /**
+     * @param string|null     $format
+     * @param string|string[] $mimeTypes
+     */
+    public function setFormat($format, $mimeTypes): void;
+
+    /**
+     * @return string[]
+     */
+    public function getLanguages(): array;
+
+    /**
+     * @return string[]
+     */
+    public function getCharsets(): array;
+
+    /**
+     * @return string[]
+     */
+    public function getEncodings(): array;
+
+    /**
+     * @return string[]
+     */
+    public function getAcceptableContentTypes(): array;
+
 }

From 7d81c0875c400c3da0ede7eac75ea4ca88e81f57 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 28 Jun 2022 23:07:02 +0200
Subject: [PATCH 178/297] Require PHPStan 1.8.0

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index a563528a..9029bd42 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.6"
+    "phpstan/phpstan": "^1.8"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From f5ad07e74a7e9ece0362232c724a6b1feb42bcfc Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 28 Jun 2022 23:07:29 +0200
Subject: [PATCH 179/297] PHPStan baseline

---
 Makefile              |  4 ++++
 phpstan-baseline.neon | 11 +++++++++++
 phpstan.neon          |  1 +
 3 files changed, 16 insertions(+)
 create mode 100644 phpstan-baseline.neon

diff --git a/Makefile b/Makefile
index fe917d3b..b34d0fec 100644
--- a/Makefile
+++ b/Makefile
@@ -21,3 +21,7 @@ cs-fix:
 .PHONY: phpstan
 phpstan:
 	php vendor/bin/phpstan analyse -l 8 -c phpstan.neon src tests
+
+.PHONY: phpstan-generate-baseline
+phpstan-generate-baseline:
+	php vendor/bin/phpstan analyse -l 8 -c phpstan.neon src tests -b phpstan-baseline.neon
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 00000000..a86a4a2d
--- /dev/null
+++ b/phpstan-baseline.neon
@@ -0,0 +1,11 @@
+parameters:
+	ignoreErrors:
+		-
+			message: "#^Accessing PHPStan\\\\Rules\\\\Methods\\\\CallMethodsRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
+			count: 1
+			path: tests/Rules/NonexistentInputBagClassTest.php
+
+		-
+			message: "#^Accessing PHPStan\\\\Rules\\\\Comparison\\\\ImpossibleCheckTypeMethodCallRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
+			count: 1
+			path: tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php
diff --git a/phpstan.neon b/phpstan.neon
index 39998f59..f13073e1 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -5,6 +5,7 @@ includes:
 	- vendor/phpstan/phpstan-phpunit/rules.neon
 	- vendor/phpstan/phpstan-strict-rules/rules.neon
 	- phar://phpstan.phar/conf/bleedingEdge.neon
+	- phpstan-baseline.neon
 
 parameters:
 	excludePaths:

From a5359244a932729613044fa306bbdeba16694253 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 28 Jun 2022 23:12:12 +0200
Subject: [PATCH 180/297] Update .gitattributes

---
 .gitattributes | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitattributes b/.gitattributes
index 17405d6e..184bf6ae 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -9,4 +9,5 @@ tmp export-ignore
 Makefile export-ignore
 phpcs.xml export-ignore
 phpstan.neon export-ignore
+phpstan-baseline.neon export-ignore
 phpunit.xml export-ignore

From 401247f8d2aa3bddfa18cf94bec12693464fe2a6 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 13 Jul 2022 12:54:15 +0200
Subject: [PATCH 181/297] Create tag workflow

---
 .github/workflows/create-tag.yml | 53 ++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 .github/workflows/create-tag.yml

diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml
new file mode 100644
index 00000000..8452d986
--- /dev/null
+++ b/.github/workflows/create-tag.yml
@@ -0,0 +1,53 @@
+# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
+
+name: "Create tag"
+
+on:
+  # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
+  workflow_dispatch:
+    inputs:
+      version:
+        description: 'Next version'
+        required: true
+        default: 'patch'
+        type: choice
+        options:
+          - patch
+          - minor
+
+jobs:
+  create-tag:
+    name: "Create tag"
+    runs-on: "ubuntu-latest"
+    steps:
+      - name: "Checkout"
+        uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+          token: ${{ secrets.PHPSTAN_BOT_TOKEN }}
+
+      - name: 'Get Previous tag'
+        id: previoustag
+        uses: "WyriHaximus/github-action-get-previous-tag@v1"
+        env:
+          GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+
+      - name: 'Get next versions'
+        id: semvers
+        uses: "WyriHaximus/github-action-next-semvers@v1"
+        with:
+          version: ${{ steps.previoustag.outputs.tag }}
+
+      - name: "Create new minor tag"
+        uses: rickstaa/action-create-tag@v1
+        if: inputs.version == 'minor'
+        with:
+          tag: ${{ steps.semvers.outputs.minor }}
+          message: ${{ steps.semvers.outputs.minor }}
+
+      - name: "Create new patch tag"
+        uses: rickstaa/action-create-tag@v1
+        if: inputs.version == 'patch'
+        with:
+          tag: ${{ steps.semvers.outputs.patch }}
+          message: ${{ steps.semvers.outputs.patch }}

From 5f4d20e20bec093ae636384a39a76df91f4d2452 Mon Sep 17 00:00:00 2001
From: Mathieu Rochette <mathieu@rochette.cc>
Date: Wed, 13 Jul 2022 14:32:58 +0200
Subject: [PATCH 182/297] Add example to load single command application

---
 README.md | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/README.md b/README.md
index 7fe1a8bb..78f02320 100644
--- a/README.md
+++ b/README.md
@@ -132,6 +132,22 @@ $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
 return new Application($kernel);
 ```
 
+[Single Command Application](https://symfony.com/doc/current/components/console/single_command_tool.html):
+
+```php
+// tests/console-application.php
+
+use App\Application; // where Application extends Symfony\Component\Console\SingleCommandApplication
+use Symfony\Component\Console;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$application = new Console\Application();
+$application->add(new Application());
+
+return $application;
+```
+
 You may then encounter an error with PhpParser:
 
 > Compile Error: Cannot Declare interface PhpParser\NodeVisitor, because the name is already in use

From b3df0da98adc391d23204d843b6320d480084ee1 Mon Sep 17 00:00:00 2001
From: fluffycondor <62219548+fluffycondor@users.noreply.github.com>
Date: Mon, 18 Jul 2022 15:46:26 +0000
Subject: [PATCH 183/297] Add stub for write and writeln console methods

---
 extension.neon                                |  2 ++
 stubs/Php/Stringable.stub                     |  5 ++++
 .../Console/Output/OutputInterface.stub       | 30 +++++++++++++++++++
 3 files changed, 37 insertions(+)
 create mode 100644 stubs/Php/Stringable.stub
 create mode 100644 stubs/Symfony/Component/Console/Output/OutputInterface.stub

diff --git a/extension.neon b/extension.neon
index 1aba591e..40e4e8be 100644
--- a/extension.neon
+++ b/extension.neon
@@ -12,12 +12,14 @@ parameters:
 		console_application_loader: null
 		consoleApplicationLoader: null
 	stubFiles:
+		- stubs/Php/Stringable.stub
 		- stubs/Psr/Cache/CacheItemInterface.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
 		- stubs/Symfony/Component/Console/Command.stub
 		- stubs/Symfony/Component/Console/Helper/HelperInterface.stub
+		- stubs/Symfony/Component/Console/Output/OutputInterface.stub
 		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
 		- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
 		- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
diff --git a/stubs/Php/Stringable.stub b/stubs/Php/Stringable.stub
new file mode 100644
index 00000000..81e1c5d5
--- /dev/null
+++ b/stubs/Php/Stringable.stub
@@ -0,0 +1,5 @@
+<?php
+
+interface Stringable
+{
+}
diff --git a/stubs/Symfony/Component/Console/Output/OutputInterface.stub b/stubs/Symfony/Component/Console/Output/OutputInterface.stub
new file mode 100644
index 00000000..6729c372
--- /dev/null
+++ b/stubs/Symfony/Component/Console/Output/OutputInterface.stub
@@ -0,0 +1,30 @@
+<?php
+
+namespace Symfony\Component\Console\Output;
+
+use Stringable;
+
+interface OutputInterface
+{
+    public const VERBOSITY_QUIET = 16;
+    public const VERBOSITY_NORMAL = 32;
+    public const VERBOSITY_VERBOSE = 64;
+    public const VERBOSITY_VERY_VERBOSE = 128;
+    public const VERBOSITY_DEBUG = 256;
+
+    public const OUTPUT_NORMAL = 1;
+    public const OUTPUT_RAW = 2;
+    public const OUTPUT_PLAIN = 4;
+
+    /**
+     * @param string|Stringable|iterable<string|Stringable> $messages
+     * @param int-mask-of<self::VERBOSITY_*|self::OUTPUT_*> $options
+     */
+    public function write($messages, bool $newline = false, int $options = 0): void;
+
+    /**
+     * @param string|Stringable|iterable<string|Stringable> $messages
+     * @param int-mask-of<self::VERBOSITY_*|self::OUTPUT_*> $options
+     */
+    public function writeln($messages, int $options = 0): void;
+}

From 240c15b678b00a12868baa0e5aa97cc84f53f4fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Tue, 19 Jul 2022 11:17:59 +0200
Subject: [PATCH 184/297] Require PHPStan 1.8.2

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 9029bd42..21a245dc 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.8"
+    "phpstan/phpstan": "^1.8.2"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From c90de3df26ea64590214798db4e4f2d7fd67e32f Mon Sep 17 00:00:00 2001
From: Alexander Schranz <alexander@sulu.io>
Date: Tue, 19 Jul 2022 11:23:36 +0200
Subject: [PATCH 185/297] Add stub for Options interface

---
 extension.neon                                |  4 ++
 .../Component/OptionsResolver/Options.stub    | 41 +++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 stubs/Symfony/Component/OptionsResolver/Options.stub

diff --git a/extension.neon b/extension.neon
index 40e4e8be..7ea78616 100644
--- a/extension.neon
+++ b/extension.neon
@@ -11,6 +11,9 @@ parameters:
 		constantHassers: true
 		console_application_loader: null
 		consoleApplicationLoader: null
+	featureToggles:
+		skipCheckGenericClasses:
+			- Symfony\Component\OptionsResolver\Options
 	stubFiles:
 		- stubs/Php/Stringable.stub
 		- stubs/Psr/Cache/CacheItemInterface.stub
@@ -38,6 +41,7 @@ parameters:
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Messenger/StampInterface.stub
 		- stubs/Symfony/Component/Messenger/Envelope.stub
+		- stubs/Symfony/Component/OptionsResolver/Options.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
diff --git a/stubs/Symfony/Component/OptionsResolver/Options.stub b/stubs/Symfony/Component/OptionsResolver/Options.stub
new file mode 100644
index 00000000..31ca0207
--- /dev/null
+++ b/stubs/Symfony/Component/OptionsResolver/Options.stub
@@ -0,0 +1,41 @@
+<?php
+
+namespace Symfony\Component\OptionsResolver;
+
+/**
+ * @template TArray of array
+ * @extends \ArrayAccess<key-of<TArray>, value-of<TArray>>
+ */
+interface Options extends \ArrayAccess, \Countable
+{
+	/**
+	 * @param key-of<TArray> $offset
+	 *
+	 * @return bool
+	 */
+	public function offsetExists($offset);
+
+	/**
+	 * @template TOffset of key-of<TArray>
+	 * @param TOffset $offset
+	 * @return TArray[TOffset]
+	 */
+	public function offsetGet($offset);
+
+	/**
+	 * @template TOffset of key-of<TArray>
+	 * @param TOffset|null $offset
+	 * @param TArray[TOffset] $value
+	 *
+	 * @return void
+	 */
+	public function offsetSet($offset, $value);
+
+	/**
+	 * @template TOffset of key-of<TArray>
+	 * @param TOffset $offset
+	 *
+	 * @return void
+	 */
+	public function offsetUnset($offset);
+}

From c612f674fb04909e1c01657c57757b913ea70432 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Tue, 19 Jul 2022 13:18:16 +0200
Subject: [PATCH 186/297] Update build.yml

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

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 38309f6a..4070c178 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,6 +21,7 @@ jobs:
           - "7.4"
           - "8.0"
           - "8.1"
+          - "8.2"
 
     steps:
       - name: "Checkout"
@@ -85,6 +86,7 @@ jobs:
           - "7.4"
           - "8.0"
           - "8.1"
+          - "8.2"
         dependencies:
           - "lowest"
           - "highest"
@@ -127,6 +129,7 @@ jobs:
           - "7.4"
           - "8.0"
           - "8.1"
+          - "8.2"
         dependencies:
           - "lowest"
           - "highest"

From 28fe677ec3ad43eb4c0946c502535365e3040ecf Mon Sep 17 00:00:00 2001
From: Semyon <7ionmail@gmail.com>
Date: Wed, 20 Jul 2022 14:51:09 +0300
Subject: [PATCH 187/297] Remove Stringable from the signature, it's actually
 not supported

---
 extension.neon                                              | 1 -
 stubs/Php/Stringable.stub                                   | 5 -----
 stubs/Symfony/Component/Console/Output/OutputInterface.stub | 4 ++--
 3 files changed, 2 insertions(+), 8 deletions(-)
 delete mode 100644 stubs/Php/Stringable.stub

diff --git a/extension.neon b/extension.neon
index 7ea78616..b4c3ee38 100644
--- a/extension.neon
+++ b/extension.neon
@@ -15,7 +15,6 @@ parameters:
 		skipCheckGenericClasses:
 			- Symfony\Component\OptionsResolver\Options
 	stubFiles:
-		- stubs/Php/Stringable.stub
 		- stubs/Psr/Cache/CacheItemInterface.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
diff --git a/stubs/Php/Stringable.stub b/stubs/Php/Stringable.stub
deleted file mode 100644
index 81e1c5d5..00000000
--- a/stubs/Php/Stringable.stub
+++ /dev/null
@@ -1,5 +0,0 @@
-<?php
-
-interface Stringable
-{
-}
diff --git a/stubs/Symfony/Component/Console/Output/OutputInterface.stub b/stubs/Symfony/Component/Console/Output/OutputInterface.stub
index 6729c372..805c72ca 100644
--- a/stubs/Symfony/Component/Console/Output/OutputInterface.stub
+++ b/stubs/Symfony/Component/Console/Output/OutputInterface.stub
@@ -17,13 +17,13 @@ interface OutputInterface
     public const OUTPUT_PLAIN = 4;
 
     /**
-     * @param string|Stringable|iterable<string|Stringable> $messages
+     * @param string|iterable<string> $messages
      * @param int-mask-of<self::VERBOSITY_*|self::OUTPUT_*> $options
      */
     public function write($messages, bool $newline = false, int $options = 0): void;
 
     /**
-     * @param string|Stringable|iterable<string|Stringable> $messages
+     * @param string|iterable<string> $messages
      * @param int-mask-of<self::VERBOSITY_*|self::OUTPUT_*> $options
      */
     public function writeln($messages, int $options = 0): void;

From 140f41035420a74eb7387406661a1d2d4289e905 Mon Sep 17 00:00:00 2001
From: Semyon <7ionmail@gmail.com>
Date: Wed, 20 Jul 2022 15:07:44 +0300
Subject: [PATCH 188/297] Remove redundant use

---
 stubs/Symfony/Component/Console/Output/OutputInterface.stub | 2 --
 1 file changed, 2 deletions(-)

diff --git a/stubs/Symfony/Component/Console/Output/OutputInterface.stub b/stubs/Symfony/Component/Console/Output/OutputInterface.stub
index 805c72ca..6e4acd3b 100644
--- a/stubs/Symfony/Component/Console/Output/OutputInterface.stub
+++ b/stubs/Symfony/Component/Console/Output/OutputInterface.stub
@@ -2,8 +2,6 @@
 
 namespace Symfony\Component\Console\Output;
 
-use Stringable;
-
 interface OutputInterface
 {
     public const VERBOSITY_QUIET = 16;

From f4cb3b8915d3656e780f305f01c86b70ff933272 Mon Sep 17 00:00:00 2001
From: Yassine Guedidi <yassine@guedidi.com>
Date: Fri, 15 Jul 2022 13:16:39 +0200
Subject: [PATCH 189/297] Add stub for VoterInterface

---
 extension.neon                                   |  2 ++
 .../Authentication/Token/TokenInterface.stub     |  7 +++++++
 .../Core/Authorization/Voter/VoterInterface.stub | 16 ++++++++++++++++
 3 files changed, 25 insertions(+)
 create mode 100644 stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub

diff --git a/extension.neon b/extension.neon
index b4c3ee38..ff6fb339 100644
--- a/extension.neon
+++ b/extension.neon
@@ -45,6 +45,8 @@ parameters:
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
+		- stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub
+		- stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
diff --git a/stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub b/stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub
new file mode 100644
index 00000000..204a9c40
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Security\Core\Authentication\Token;
+
+interface TokenInterface
+{
+}
diff --git a/stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub b/stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub
new file mode 100644
index 00000000..1e8d7c5d
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Component\Security\Core\Authorization\Voter;
+
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+interface VoterInterface
+{
+	/**
+	 * @param mixed   $subject
+	 * @param mixed[] $attributes
+	 *
+	 * @return int
+	 */
+	public function vote(TokenInterface $token, $subject, array $attributes);
+}

From 70f84527938b3c68eaa79f564c79f70adc966372 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 25 Aug 2022 16:58:42 +0200
Subject: [PATCH 190/297] Infer correct type for `$input->getOptions()` call

---
 extension.neon                                |  7 ++
 src/Type/Symfony/GetOptionTypeHelper.php      | 35 +++++++++
 ...aceGetOptionDynamicReturnTypeExtension.php | 25 ++-----
 ...ceGetOptionsDynamicReturnTypeExtension.php | 71 +++++++++++++++++++
 .../Symfony/data/ExampleOptionCommand.php     |  2 +
 .../Symfony/data/ExampleOptionLazyCommand.php |  2 +
 6 files changed, 123 insertions(+), 19 deletions(-)
 create mode 100644 src/Type/Symfony/GetOptionTypeHelper.php
 create mode 100644 src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php

diff --git a/extension.neon b/extension.neon
index ff6fb339..f4c0dbf4 100644
--- a/extension.neon
+++ b/extension.neon
@@ -175,10 +175,17 @@ services:
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
 	# InputInterface::getOption() return type
+	-
+		factory: PHPStan\Type\Symfony\GetOptionTypeHelper
 	-
 		factory: PHPStan\Type\Symfony\InputInterfaceGetOptionDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
+	# InputInterface::getOptions() return type
+	-
+		factory: PHPStan\Type\Symfony\InputInterfaceGetOptionsDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
 	# InputInterface::hasOption() type specification
 	-
 		factory: PHPStan\Type\Symfony\OptionTypeSpecifyingExtension
diff --git a/src/Type/Symfony/GetOptionTypeHelper.php b/src/Type/Symfony/GetOptionTypeHelper.php
new file mode 100644
index 00000000..a67606e1
--- /dev/null
+++ b/src/Type/Symfony/GetOptionTypeHelper.php
@@ -0,0 +1,35 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PHPStan\Analyser\Scope;
+use PHPStan\Type\ArrayType;
+use PHPStan\Type\BooleanType;
+use PHPStan\Type\IntegerType;
+use PHPStan\Type\NullType;
+use PHPStan\Type\StringType;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
+use Symfony\Component\Console\Input\InputOption;
+
+class GetOptionTypeHelper
+{
+
+	public function getOptionType(Scope $scope, InputOption $option): Type
+	{
+		if (!$option->acceptValue()) {
+			return new BooleanType();
+		}
+
+		$optType = TypeCombinator::union(new StringType(), new NullType());
+		if ($option->isValueRequired() && ($option->isArray() || $option->getDefault() !== null)) {
+			$optType = TypeCombinator::removeNull($optType);
+		}
+		if ($option->isArray()) {
+			$optType = new ArrayType(new IntegerType(), $optType);
+		}
+
+		return TypeCombinator::union($optType, $scope->getTypeFromValue($option->getDefault()));
+	}
+
+}
diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
index 7b543a16..ad2bc6c6 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -8,12 +8,7 @@
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Symfony\ConsoleApplicationResolver;
-use PHPStan\Type\ArrayType;
-use PHPStan\Type\BooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
-use PHPStan\Type\IntegerType;
-use PHPStan\Type\NullType;
-use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeCombinator;
 use PHPStan\Type\TypeUtils;
@@ -25,9 +20,13 @@ final class InputInterfaceGetOptionDynamicReturnTypeExtension implements Dynamic
 	/** @var ConsoleApplicationResolver */
 	private $consoleApplicationResolver;
 
-	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
+	/** @var GetOptionTypeHelper */
+	private $getOptionTypeHelper;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, GetOptionTypeHelper $getOptionTypeHelper)
 	{
 		$this->consoleApplicationResolver = $consoleApplicationResolver;
+		$this->getOptionTypeHelper = $getOptionTypeHelper;
 	}
 
 	public function getClass(): string
@@ -64,19 +63,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			try {
 				$command->mergeApplicationDefinition();
 				$option = $command->getDefinition()->getOption($optName);
-				if (!$option->acceptValue()) {
-					$optType = new BooleanType();
-				} else {
-					$optType = TypeCombinator::union(new StringType(), new NullType());
-					if ($option->isValueRequired() && ($option->isArray() || $option->getDefault() !== null)) {
-						$optType = TypeCombinator::removeNull($optType);
-					}
-					if ($option->isArray()) {
-						$optType = new ArrayType(new IntegerType(), $optType);
-					}
-					$optType = TypeCombinator::union($optType, $scope->getTypeFromValue($option->getDefault()));
-				}
-				$optTypes[] = $optType;
+				$optTypes[] = $this->getOptionTypeHelper->getOptionType($scope, $option);
 			} catch (InvalidArgumentException $e) {
 				// noop
 			}
diff --git a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..80c6d4e2
--- /dev/null
+++ b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
@@ -0,0 +1,71 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use InvalidArgumentException;
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
+use PHPStan\Type\Constant\ConstantStringType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
+use function count;
+
+final class InputInterfaceGetOptionsDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	/** @var GetOptionTypeHelper */
+	private $getOptionTypeHelper;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, GetOptionTypeHelper $getOptionTypeHelper)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+		$this->getOptionTypeHelper = $getOptionTypeHelper;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Input\InputInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getOptions';
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	{
+		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			return $defaultReturnType;
+		}
+
+		$optTypes = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
+			try {
+				$command->mergeApplicationDefinition();
+				$options = $command->getDefinition()->getOptions();
+				$builder = ConstantArrayTypeBuilder::createEmpty();
+				foreach ($options as $name => $option) {
+					$optionType = $this->getOptionTypeHelper->getOptionType($scope, $option);
+					$builder->setOffsetValueType(new ConstantStringType($name), $optionType);
+				}
+
+				$optTypes[] = $builder->getArray();
+			} catch (InvalidArgumentException $e) {
+				// noop
+			}
+		}
+
+		return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : $defaultReturnType;
+	}
+
+}
diff --git a/tests/Type/Symfony/data/ExampleOptionCommand.php b/tests/Type/Symfony/data/ExampleOptionCommand.php
index b6a07574..00c35894 100644
--- a/tests/Type/Symfony/data/ExampleOptionCommand.php
+++ b/tests/Type/Symfony/data/ExampleOptionCommand.php
@@ -40,6 +40,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
 		assertType('1|string', $input->getOption('cc'));
 		assertType('array<int, 1|string|null>', $input->getOption('dd'));
 		assertType('array<int, 1|string>', $input->getOption('ee'));
+
+		assertType('array{a: bool, b: string|null, c: string|null, d: array<int, string|null>, e: array<int, string>, bb: 1|string|null, cc: 1|string, dd: array<int, 1|string|null>, ee: array<int, 1|string>, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions());
 	}
 
 }
diff --git a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
index 7933cd41..1aaebf20 100644
--- a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
+++ b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
@@ -42,6 +42,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
 		assertType('1|string', $input->getOption('cc'));
 		assertType('array<int, 1|string|null>', $input->getOption('dd'));
 		assertType('array<int, 1|string>', $input->getOption('ee'));
+
+		assertType('array{a: bool, b: string|null, c: string|null, d: array<int, string|null>, e: array<int, string>, bb: 1|string|null, cc: 1|string, dd: array<int, 1|string|null>, ee: array<int, 1|string>, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions());
 	}
 
 }

From 298f7aee5f36f7d72e575970013b46965456b7f4 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 25 Aug 2022 17:24:08 +0200
Subject: [PATCH 191/297] Do not longer test unsupported versions of Symfony

---
 composer.json | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/composer.json b/composer.json
index 21a245dc..8ab6d172 100644
--- a/composer.json
+++ b/composer.json
@@ -27,15 +27,15 @@
     "phpstan/phpstan-strict-rules": "^1.0",
     "phpunit/phpunit": "^9.5",
     "psr/container": "1.0 || 1.1.1",
-    "symfony/config": "^4.2 || ^5.0",
-    "symfony/console": "^4.0 || ^5.0",
-    "symfony/dependency-injection": "^4.0 || ^5.0",
-    "symfony/form": "^4.0 || ^5.0",
-    "symfony/framework-bundle": "^4.4 || ^5.0",
-    "symfony/http-foundation": "^5.1",
-    "symfony/messenger": "^4.2 || ^5.0",
+    "symfony/config": "^5.4",
+    "symfony/console": "^5.4",
+    "symfony/dependency-injection": "^5.4",
+    "symfony/form": "^5.4",
+    "symfony/framework-bundle": "^5.4",
+    "symfony/http-foundation": "^5.4",
+    "symfony/messenger": "^5.4",
     "symfony/polyfill-php80": "^1.24",
-    "symfony/serializer": "^4.0 || ^5.0"
+    "symfony/serializer": "^5.4"
   },
   "config": {
     "platform": {

From 120d3a8f3b16f79a570e4ef68a96fe9f85186fd1 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 25 Aug 2022 17:24:53 +0200
Subject: [PATCH 192/297] Test Symfony ^6.1

---
 composer.json | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/composer.json b/composer.json
index 8ab6d172..65dfa3df 100644
--- a/composer.json
+++ b/composer.json
@@ -27,20 +27,17 @@
     "phpstan/phpstan-strict-rules": "^1.0",
     "phpunit/phpunit": "^9.5",
     "psr/container": "1.0 || 1.1.1",
-    "symfony/config": "^5.4",
-    "symfony/console": "^5.4",
-    "symfony/dependency-injection": "^5.4",
-    "symfony/form": "^5.4",
-    "symfony/framework-bundle": "^5.4",
-    "symfony/http-foundation": "^5.4",
+    "symfony/config": "^5.4 || ^6.1",
+    "symfony/console": "^5.4 || ^6.1",
+    "symfony/dependency-injection": "^5.4 || ^6.1",
+    "symfony/form": "^5.4 || ^6.1",
+    "symfony/framework-bundle": "^5.4 || ^6.1",
+    "symfony/http-foundation": "^5.4 || ^6.1",
     "symfony/messenger": "^5.4",
     "symfony/polyfill-php80": "^1.24",
     "symfony/serializer": "^5.4"
   },
   "config": {
-    "platform": {
-      "php": "7.4.6"
-    },
     "sort-packages": true
   },
   "extra": {

From 82d54e8d9070a131770732afd0492b9acff73227 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 25 Aug 2022 17:30:20 +0200
Subject: [PATCH 193/297] Fix build by downgrading PHPUnit

---
 .github/workflows/build.yml | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4070c178..1045f11c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -33,16 +33,16 @@ jobs:
           coverage: "none"
           php-version: "${{ matrix.php-version }}"
 
+      - name: "Downgrade PHPUnit"
+        if: matrix.php-version == '7.2'
+        run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies"
+
       - name: "Validate Composer"
         run: "composer validate"
 
       - 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"
 
@@ -101,6 +101,10 @@ jobs:
           coverage: "none"
           php-version: "${{ matrix.php-version }}"
 
+      - name: "Downgrade PHPUnit"
+        if: matrix.php-version == '7.2'
+        run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies"
+
       - name: "Install lowest dependencies"
         if: ${{ matrix.dependencies == 'lowest' }}
         run: "composer update --prefer-lowest --no-interaction --no-progress"
@@ -109,10 +113,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"
 
@@ -146,6 +146,10 @@ jobs:
           extensions: mbstring
           tools: composer:v2
 
+      - name: "Downgrade PHPUnit"
+        if: matrix.php-version == '7.2'
+        run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies"
+
       - name: "Install lowest dependencies"
         if: ${{ matrix.dependencies == 'lowest' }}
         run: "composer update --prefer-lowest --no-interaction --no-progress"
@@ -154,9 +158,5 @@ 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: "PHPStan"
         run: "make phpstan"

From a14d467083e8a4ca6860921e68f646831a88260c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= <elias@haeussler.dev>
Date: Thu, 25 Aug 2022 19:30:02 +0200
Subject: [PATCH 194/297] Add support for negatable input options

Since symfony/console 5.3, input options can be marked as negatable.
The appropriate return type when calling `$input->getOption()` can
be either a boolean or null. With this change, support for return
types of such negatable input options is added.
---
 src/Type/Symfony/GetOptionTypeHelper.php             | 5 +++++
 tests/Type/Symfony/data/ExampleOptionCommand.php     | 4 +++-
 tests/Type/Symfony/data/ExampleOptionLazyCommand.php | 4 +++-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/Type/Symfony/GetOptionTypeHelper.php b/src/Type/Symfony/GetOptionTypeHelper.php
index a67606e1..9406698e 100644
--- a/src/Type/Symfony/GetOptionTypeHelper.php
+++ b/src/Type/Symfony/GetOptionTypeHelper.php
@@ -10,6 +10,7 @@
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeCombinator;
+use PHPStan\Type\UnionType;
 use Symfony\Component\Console\Input\InputOption;
 
 class GetOptionTypeHelper
@@ -18,6 +19,10 @@ class GetOptionTypeHelper
 	public function getOptionType(Scope $scope, InputOption $option): Type
 	{
 		if (!$option->acceptValue()) {
+			if ($option->isNegatable()) {
+				return new UnionType([new BooleanType(), new NullType()]);
+			}
+
 			return new BooleanType();
 		}
 
diff --git a/tests/Type/Symfony/data/ExampleOptionCommand.php b/tests/Type/Symfony/data/ExampleOptionCommand.php
index 00c35894..b880173d 100644
--- a/tests/Type/Symfony/data/ExampleOptionCommand.php
+++ b/tests/Type/Symfony/data/ExampleOptionCommand.php
@@ -21,6 +21,7 @@ protected function configure(): void
 		$this->addOption('c', null, InputOption::VALUE_REQUIRED);
 		$this->addOption('d', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL);
 		$this->addOption('e', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED);
+		$this->addOption('f', null, InputOption::VALUE_NEGATABLE);
 
 		$this->addOption('bb', null, InputOption::VALUE_OPTIONAL, '', 1);
 		$this->addOption('cc', null, InputOption::VALUE_REQUIRED, '', 1);
@@ -35,13 +36,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
 		assertType('string|null', $input->getOption('c'));
 		assertType('array<int, string|null>', $input->getOption('d'));
 		assertType('array<int, string>', $input->getOption('e'));
+		assertType('bool|null', $input->getOption('f'));
 
 		assertType('1|string|null', $input->getOption('bb'));
 		assertType('1|string', $input->getOption('cc'));
 		assertType('array<int, 1|string|null>', $input->getOption('dd'));
 		assertType('array<int, 1|string>', $input->getOption('ee'));
 
-		assertType('array{a: bool, b: string|null, c: string|null, d: array<int, string|null>, e: array<int, string>, bb: 1|string|null, cc: 1|string, dd: array<int, 1|string|null>, ee: array<int, 1|string>, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions());
+		assertType('array{a: bool, b: string|null, c: string|null, d: array<int, string|null>, e: array<int, string>, f: bool|null, bb: 1|string|null, cc: 1|string, dd: array<int, 1|string|null>, ee: array<int, 1|string>, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool|null, no-interaction: bool}', $input->getOptions());
 	}
 
 }
diff --git a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
index 1aaebf20..433e1cfe 100644
--- a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
+++ b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php
@@ -23,6 +23,7 @@ protected function configure(): void
 		$this->addOption('c', null, InputOption::VALUE_REQUIRED);
 		$this->addOption('d', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL);
 		$this->addOption('e', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED);
+		$this->addOption('f', null, InputOption::VALUE_NEGATABLE);
 
 		$this->addOption('bb', null, InputOption::VALUE_OPTIONAL, '', 1);
 		$this->addOption('cc', null, InputOption::VALUE_REQUIRED, '', 1);
@@ -37,13 +38,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
 		assertType('string|null', $input->getOption('c'));
 		assertType('array<int, string|null>', $input->getOption('d'));
 		assertType('array<int, string>', $input->getOption('e'));
+		assertType('bool|null', $input->getOption('f'));
 
 		assertType('1|string|null', $input->getOption('bb'));
 		assertType('1|string', $input->getOption('cc'));
 		assertType('array<int, 1|string|null>', $input->getOption('dd'));
 		assertType('array<int, 1|string>', $input->getOption('ee'));
 
-		assertType('array{a: bool, b: string|null, c: string|null, d: array<int, string|null>, e: array<int, string>, bb: 1|string|null, cc: 1|string, dd: array<int, 1|string|null>, ee: array<int, 1|string>, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions());
+		assertType('array{a: bool, b: string|null, c: string|null, d: array<int, string|null>, e: array<int, string>, f: bool|null, bb: 1|string|null, cc: 1|string, dd: array<int, 1|string|null>, ee: array<int, 1|string>, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool|null, no-interaction: bool}', $input->getOptions());
 	}
 
 }

From e7767d9cf191e6865a2205f77ecac3716872e055 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= <e.haeussler@familie-redlich.de>
Date: Fri, 26 Aug 2022 16:27:59 +0200
Subject: [PATCH 195/297] Skip check for negatable input options on
 symfony/console < 5.3

Related: #300
---
 phpstan-baseline.neon                    | 5 +++++
 src/Type/Symfony/GetOptionTypeHelper.php | 3 ++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index a86a4a2d..4abdaa0a 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,5 +1,10 @@
 parameters:
 	ignoreErrors:
+		-
+			message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Console\\\\Input\\\\InputOption and 'isNegatable' will always evaluate to true\\.$#"
+			count: 1
+			path: src/Type/Symfony/GetOptionTypeHelper.php
+
 		-
 			message: "#^Accessing PHPStan\\\\Rules\\\\Methods\\\\CallMethodsRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
 			count: 1
diff --git a/src/Type/Symfony/GetOptionTypeHelper.php b/src/Type/Symfony/GetOptionTypeHelper.php
index 9406698e..6ddf87d8 100644
--- a/src/Type/Symfony/GetOptionTypeHelper.php
+++ b/src/Type/Symfony/GetOptionTypeHelper.php
@@ -12,6 +12,7 @@
 use PHPStan\Type\TypeCombinator;
 use PHPStan\Type\UnionType;
 use Symfony\Component\Console\Input\InputOption;
+use function method_exists;
 
 class GetOptionTypeHelper
 {
@@ -19,7 +20,7 @@ class GetOptionTypeHelper
 	public function getOptionType(Scope $scope, InputOption $option): Type
 	{
 		if (!$option->acceptValue()) {
-			if ($option->isNegatable()) {
+			if (method_exists($option, 'isNegatable') && $option->isNegatable()) {
 				return new UnionType([new BooleanType(), new NullType()]);
 			}
 

From 016e441a19a2af79ca0c60920ba0d61747b4e855 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Thu, 18 Aug 2022 12:23:40 +0200
Subject: [PATCH 196/297] Add @throws annotation

---
 extension.neon                                             | 5 ++++-
 stubs/Symfony/Component/Form/DataTransformerInterface.stub | 4 ++++
 .../Component/Form/Exception/ExceptionInterface.stub       | 7 +++++++
 .../Symfony/Component/Form/Exception/RuntimeException.stub | 7 +++++++
 .../Form/Exception/TransformationFailedException.stub      | 7 +++++++
 5 files changed, 29 insertions(+), 1 deletion(-)
 create mode 100644 stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub
 create mode 100644 stubs/Symfony/Component/Form/Exception/RuntimeException.stub
 create mode 100644 stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub

diff --git a/extension.neon b/extension.neon
index f4c0dbf4..30f2139d 100644
--- a/extension.neon
+++ b/extension.neon
@@ -22,12 +22,15 @@ parameters:
 		- stubs/Symfony/Component/Console/Command.stub
 		- stubs/Symfony/Component/Console/Helper/HelperInterface.stub
 		- stubs/Symfony/Component/Console/Output/OutputInterface.stub
-		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
 		- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
 		- stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/GenericEvent.stub
+		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
+		- stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub
+		- stubs/Symfony/Component/Form/Exception/RuntimeException.stub
+		- stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub
 		- stubs/Symfony/Component/Form/DataTransformerInterface.stub
 		- stubs/Symfony/Component/Form/FormBuilderInterface.stub
 		- stubs/Symfony/Component/Form/FormInterface.stub
diff --git a/stubs/Symfony/Component/Form/DataTransformerInterface.stub b/stubs/Symfony/Component/Form/DataTransformerInterface.stub
index edd4411e..393fa803 100644
--- a/stubs/Symfony/Component/Form/DataTransformerInterface.stub
+++ b/stubs/Symfony/Component/Form/DataTransformerInterface.stub
@@ -14,6 +14,8 @@ interface DataTransformerInterface
      * @phpstan-param T|null $value The value in the original representation
      *
      * @phpstan-return R|null The value in the transformed representation
+     *
+     * @throws TransformationFailedException
      */
     public function transform($value);
 
@@ -21,6 +23,8 @@ interface DataTransformerInterface
      * @phpstan-param R|null $value The value in the transformed representation
      *
      * @phpstan-return T|null The value in the original representation
+     *
+     * @throws TransformationFailedException
      */
     public function reverseTransform($value);
 }
diff --git a/stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub b/stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub
new file mode 100644
index 00000000..306015dc
--- /dev/null
+++ b/stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Form\Exception;
+
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/stubs/Symfony/Component/Form/Exception/RuntimeException.stub b/stubs/Symfony/Component/Form/Exception/RuntimeException.stub
new file mode 100644
index 00000000..8c37402d
--- /dev/null
+++ b/stubs/Symfony/Component/Form/Exception/RuntimeException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Form\Exception;
+
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub b/stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub
new file mode 100644
index 00000000..8ec468c1
--- /dev/null
+++ b/stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Form\Exception;
+
+class TransformationFailedException extends RuntimeException
+{
+}

From e5459eb5f2cd53f431047418b15fdc82c4733b83 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 5 Oct 2022 13:17:24 +0200
Subject: [PATCH 197/297] ConsoleApplicationResolver made lazy

---
 src/Symfony/ConsoleApplicationResolver.php | 32 ++++++++++++++--------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 6d4ec810..941e8fef 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -16,31 +16,38 @@
 final class ConsoleApplicationResolver
 {
 
+	/** @var string|null */
+	private $consoleApplicationLoader;
+
 	/** @var Application|null */
 	private $consoleApplication;
 
 	public function __construct(Configuration $configuration)
 	{
-		$consoleApplicationLoader = $configuration->getConsoleApplicationLoader();
-		if ($consoleApplicationLoader === null) {
-			return;
-		}
-		$this->consoleApplication = $this->loadConsoleApplication($consoleApplicationLoader);
+		$this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader();
 	}
 
 	/**
 	 * @return Application|null
 	 * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint
 	 */
-	private function loadConsoleApplication(string $consoleApplicationLoader)
+	private function getConsoleApplication()
 	{
-		if (!file_exists($consoleApplicationLoader)
-			|| !is_readable($consoleApplicationLoader)
+		if ($this->consoleApplicationLoader === null) {
+			return null;
+		}
+
+		if ($this->consoleApplication !== null) {
+			return $this->consoleApplication;
+		}
+
+		if (!file_exists($this->consoleApplicationLoader)
+			|| !is_readable($this->consoleApplicationLoader)
 		) {
-			throw new ShouldNotHappenException(sprintf('Cannot load console application. Check the parameters.symfony.consoleApplicationLoader setting in PHPStan\'s config. The offending value is "%s".', $consoleApplicationLoader));
+			throw new ShouldNotHappenException(sprintf('Cannot load console application. Check the parameters.symfony.consoleApplicationLoader setting in PHPStan\'s config. The offending value is "%s".', $this->consoleApplicationLoader));
 		}
 
-		return require $consoleApplicationLoader;
+		return $this->consoleApplication = require $this->consoleApplicationLoader;
 	}
 
 	/**
@@ -48,7 +55,8 @@ private function loadConsoleApplication(string $consoleApplicationLoader)
 	 */
 	public function findCommands(ClassReflection $classReflection): array
 	{
-		if ($this->consoleApplication === null) {
+		$consoleApplication = $this->getConsoleApplication();
+		if ($consoleApplication === null) {
 			return [];
 		}
 
@@ -58,7 +66,7 @@ public function findCommands(ClassReflection $classReflection): array
 		}
 
 		$commands = [];
-		foreach ($this->consoleApplication->all() as $name => $command) {
+		foreach ($consoleApplication->all() as $name => $command) {
 			$commandClass = new ObjectType(get_class($command));
 			$isLazyCommand = (new ObjectType('Symfony\Component\Console\Command\LazyCommand'))->isSuperTypeOf($commandClass)->yes();
 

From f7dd737329504115adaa987697a759a66dd2ee8a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 5 Oct 2022 13:18:42 +0200
Subject: [PATCH 198/297] Native type in ConsoleApplicationResolver

---
 src/Symfony/ConsoleApplicationResolver.php | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 941e8fef..51bd7960 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -27,11 +27,7 @@ public function __construct(Configuration $configuration)
 		$this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader();
 	}
 
-	/**
-	 * @return Application|null
-	 * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint
-	 */
-	private function getConsoleApplication()
+	private function getConsoleApplication(): ?Application
 	{
 		if ($this->consoleApplicationLoader === null) {
 			return null;

From e5e807b5ea8f656cd61fe9f29e79e23494d7a144 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 31 Oct 2022 00:38:35 +0000
Subject: [PATCH 199/297] Update metcalfc/changelog-generator action to v4

---
 .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 5fed0458..bac4a006 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@v3.0.0
+        uses: metcalfc/changelog-generator@v4.0.1
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From 7210072b7fd83bf62eb5f889d4da70b80c07f04c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= <enumag@gmail.com>
Date: Thu, 3 Nov 2022 12:32:52 +0100
Subject: [PATCH 200/297] Improve Envelope::all() return type

---
 stubs/Symfony/Component/Messenger/Envelope.stub | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/stubs/Symfony/Component/Messenger/Envelope.stub b/stubs/Symfony/Component/Messenger/Envelope.stub
index c40a5ee6..af69bb66 100644
--- a/stubs/Symfony/Component/Messenger/Envelope.stub
+++ b/stubs/Symfony/Component/Messenger/Envelope.stub
@@ -14,4 +14,11 @@ final class Envelope
     public function last(string $stampFqcn): ?StampInterface
     {
     }
+    
+    /**
+     * @return ($stampFqcn is null ? array<string, list<StampInterface>> : list<StampInterface>)
+     */
+    public function all(?string $stampFqcn = null): array
+    {
+    }
 }

From d6ea16206b1b645ded5b43736d8ef5ae1168eb55 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= <enumag@gmail.com>
Date: Fri, 4 Nov 2022 09:35:44 +0100
Subject: [PATCH 201/297] Fix Envelope::all() return type

---
 composer.json                                    |  2 +-
 src/Type/Symfony/EnvelopeReturnTypeExtension.php | 13 +++++++++----
 stubs/Symfony/Component/Messenger/Envelope.stub  |  7 -------
 tests/Type/Symfony/ExtensionTest.php             |  1 +
 tests/Type/Symfony/data/envelope_all.php         |  6 +++---
 5 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/composer.json b/composer.json
index 65dfa3df..adc7050a 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.8.2"
+    "phpstan/phpstan": "^1.9.1"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
index 5ac61c2f..ec2531aa 100644
--- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php
+++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
@@ -5,10 +5,12 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
+use PHPStan\Type\Accessory\AccessoryArrayListType;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\Constant\ConstantStringType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
-use PHPStan\Type\MixedType;
+use PHPStan\Type\Generic\GenericClassStringType;
+use PHPStan\Type\IntegerType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
 use function count;
@@ -33,15 +35,18 @@ public function getTypeFromMethodCall(
 	): Type
 	{
 		if (count($methodCall->getArgs()) === 0) {
-			return new ArrayType(new MixedType(), new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')));
+			return new ArrayType(
+				new GenericClassStringType(new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')),
+				AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')))
+			);
 		}
 
 		$argType = $scope->getType($methodCall->getArgs()[0]->value);
 		if (!$argType instanceof ConstantStringType) {
-			return new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'));
+			return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')));
 		}
 
-		return new ArrayType(new MixedType(), new ObjectType($argType->getValue()));
+		return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType($argType->getValue())));
 	}
 
 }
diff --git a/stubs/Symfony/Component/Messenger/Envelope.stub b/stubs/Symfony/Component/Messenger/Envelope.stub
index af69bb66..c40a5ee6 100644
--- a/stubs/Symfony/Component/Messenger/Envelope.stub
+++ b/stubs/Symfony/Component/Messenger/Envelope.stub
@@ -14,11 +14,4 @@ final class Envelope
     public function last(string $stampFqcn): ?StampInterface
     {
     }
-    
-    /**
-     * @return ($stampFqcn is null ? array<string, list<StampInterface>> : list<StampInterface>)
-     */
-    public function all(?string $stampFqcn = null): array
-    {
-    }
 }
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index a0bb8115..8dff057e 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -76,6 +76,7 @@ public static function getAdditionalConfigFiles(): array
 		return [
 			__DIR__ . '/../../../extension.neon',
 			__DIR__ . '/extension-test.neon',
+			'phar://' . __DIR__ . '/../../../vendor/phpstan/phpstan/phpstan.phar/conf/bleedingEdge.neon',
 		];
 	}
 
diff --git a/tests/Type/Symfony/data/envelope_all.php b/tests/Type/Symfony/data/envelope_all.php
index 95521530..aac9d583 100644
--- a/tests/Type/Symfony/data/envelope_all.php
+++ b/tests/Type/Symfony/data/envelope_all.php
@@ -4,6 +4,6 @@
 
 $envelope = new \Symfony\Component\Messenger\Envelope(new stdClass());
 
-assertType('array<Symfony\Component\Messenger\Stamp\ReceivedStamp>', $envelope->all(\Symfony\Component\Messenger\Stamp\ReceivedStamp::class));
-assertType('array<Symfony\Component\Messenger\Stamp\StampInterface>', $envelope->all(random_bytes(1)));
-assertType('array<array<Symfony\Component\Messenger\Stamp\StampInterface>>', $envelope->all());
+assertType('list<Symfony\Component\Messenger\Stamp\ReceivedStamp>', $envelope->all(\Symfony\Component\Messenger\Stamp\ReceivedStamp::class));
+assertType('list<Symfony\Component\Messenger\Stamp\StampInterface>', $envelope->all(random_bytes(1)));
+assertType('array<class-string<Symfony\Component\Messenger\Stamp\StampInterface>, list<Symfony\Component\Messenger\Stamp\StampInterface>>', $envelope->all());

From e08844440952110852cb223171f1907c15a344db Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 5 Dec 2022 09:14:55 +0100
Subject: [PATCH 202/297] Fix build

---
 .../Symfony/ServiceDynamicReturnTypeExtension.php   | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index a2168a91..b0101cf4 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -18,6 +18,7 @@
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 use function class_exists;
 use function in_array;
+use function is_string;
 
 final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
@@ -144,7 +145,17 @@ private function getHasTypeFromMethodCall(
 
 	private function determineServiceClass(ParameterBag $parameterBag, ServiceDefinition $service): ?string
 	{
-		return $parameterBag->resolveValue($service->getClass());
+		$class = $service->getClass();
+		if ($class === null) {
+			return null;
+		}
+
+		$value = $parameterBag->resolveValue($class);
+		if (!is_string($value)) {
+			return null;
+		}
+
+		return $value;
 	}
 
 }

From c55237a027f71f0cdb09c77fd27783dd806d228f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 5 Dec 2022 02:23:50 +0000
Subject: [PATCH 203/297] Update dessant/lock-threads action to v4

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

diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml
index a05d4173..4c7990df 100644
--- a/.github/workflows/lock-closed-issues.yml
+++ b/.github/workflows/lock-closed-issues.yml
@@ -8,7 +8,7 @@ jobs:
   lock:
     runs-on: ubuntu-latest
     steps:
-      - uses: dessant/lock-threads@v3
+      - uses: dessant/lock-threads@v4
         with:
           github-token: ${{ github.token }}
           issue-inactive-days: '31'

From 738920755edd12c693ae83ab73b8f6bfa220a091 Mon Sep 17 00:00:00 2001
From: Fran Moreno <franmomu@gmail.com>
Date: Thu, 15 Dec 2022 15:28:17 +0100
Subject: [PATCH 204/297] Remove nullable type after calling `HeaderBag::has`

---
 extension.neon                                  | 11 ++++++++++-
 ...nsion.php => BagTypeSpecifyingExtension.php} | 17 +++++++++++++----
 tests/Type/Symfony/ExtensionTest.php            |  2 +-
 .../data/{header_bag_get.php => header_bag.php} |  6 ++++++
 4 files changed, 30 insertions(+), 6 deletions(-)
 rename src/Type/Symfony/{InputBagTypeSpecifyingExtension.php => BagTypeSpecifyingExtension.php} (79%)
 rename tests/Type/Symfony/data/{header_bag_get.php => header_bag.php} (82%)

diff --git a/extension.neon b/extension.neon
index 30f2139d..ab503e1e 100644
--- a/extension.neon
+++ b/extension.neon
@@ -298,5 +298,14 @@ services:
 
 	# InputBag::get() type specification
 	-
-		factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension
+		factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension
 		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
+		arguments:
+		    className: Symfony\Component\HttpFoundation\InputBag
+
+	# HeaderBag::get() type specification
+	-
+		factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension
+		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
+		arguments:
+		    className: Symfony\Component\HttpFoundation\HeaderBag
diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/BagTypeSpecifyingExtension.php
similarity index 79%
rename from src/Type/Symfony/InputBagTypeSpecifyingExtension.php
rename to src/Type/Symfony/BagTypeSpecifyingExtension.php
index d3ad578c..96aa91f1 100644
--- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/BagTypeSpecifyingExtension.php
@@ -11,21 +11,30 @@
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
 use PHPStan\Type\NullType;
-use Symfony\Component\HttpFoundation\InputBag;
 
-final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
+final class BagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	private const INPUT_BAG_CLASS = InputBag::class;
 	private const HAS_METHOD_NAME = 'has';
 	private const GET_METHOD_NAME = 'get';
 
 	/** @var TypeSpecifier */
 	private $typeSpecifier;
 
+	/** @var class-string */
+	private $className;
+
+	/**
+	 * @param class-string $className
+	 */
+	public function __construct(string $className)
+	{
+		$this->className = $className;
+	}
+
 	public function getClass(): string
 	{
-		return self::INPUT_BAG_CLASS;
+		return $this->className;
 	}
 
 	public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 8dff057e..afe60ca4 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -15,7 +15,7 @@ class ExtensionTest extends TypeInferenceTestCase
 	public function dataFileAsserts(): iterable
 	{
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php');
 
 		if (class_exists('Symfony\Component\HttpFoundation\InputBag')) {
diff --git a/tests/Type/Symfony/data/header_bag_get.php b/tests/Type/Symfony/data/header_bag.php
similarity index 82%
rename from tests/Type/Symfony/data/header_bag_get.php
rename to tests/Type/Symfony/data/header_bag.php
index e3f4c561..ce513bc1 100644
--- a/tests/Type/Symfony/data/header_bag_get.php
+++ b/tests/Type/Symfony/data/header_bag.php
@@ -4,6 +4,12 @@
 
 $bag = new \Symfony\Component\HttpFoundation\HeaderBag(['foo' => ['bar']]);
 
+if ($bag->has('bar')) {
+	assertType('string', $bag->get('bar'));
+} else {
+	assertType('null', $bag->get('bar'));
+}
+
 assertType('string|null', $bag->get('foo'));
 assertType('string|null', $bag->get('foo', null));
 assertType('string', $bag->get('foo', 'baz'));

From 3178f15a60b62df21ddd202f6a668851eeb138c7 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 16 Dec 2022 10:43:26 +0100
Subject: [PATCH 205/297] Revert "Remove nullable type after calling
 `HeaderBag::has`"

This reverts commit 738920755edd12c693ae83ab73b8f6bfa220a091.
---
 extension.neon                                  | 11 +----------
 ....php => InputBagTypeSpecifyingExtension.php} | 17 ++++-------------
 tests/Type/Symfony/ExtensionTest.php            |  2 +-
 .../data/{header_bag.php => header_bag_get.php} |  6 ------
 4 files changed, 6 insertions(+), 30 deletions(-)
 rename src/Type/Symfony/{BagTypeSpecifyingExtension.php => InputBagTypeSpecifyingExtension.php} (79%)
 rename tests/Type/Symfony/data/{header_bag.php => header_bag_get.php} (82%)

diff --git a/extension.neon b/extension.neon
index ab503e1e..30f2139d 100644
--- a/extension.neon
+++ b/extension.neon
@@ -298,14 +298,5 @@ services:
 
 	# InputBag::get() type specification
 	-
-		factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension
+		factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension
 		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
-		arguments:
-		    className: Symfony\Component\HttpFoundation\InputBag
-
-	# HeaderBag::get() type specification
-	-
-		factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension
-		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
-		arguments:
-		    className: Symfony\Component\HttpFoundation\HeaderBag
diff --git a/src/Type/Symfony/BagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
similarity index 79%
rename from src/Type/Symfony/BagTypeSpecifyingExtension.php
rename to src/Type/Symfony/InputBagTypeSpecifyingExtension.php
index 96aa91f1..d3ad578c 100644
--- a/src/Type/Symfony/BagTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
@@ -11,30 +11,21 @@
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
 use PHPStan\Type\NullType;
+use Symfony\Component\HttpFoundation\InputBag;
 
-final class BagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
+final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
+	private const INPUT_BAG_CLASS = InputBag::class;
 	private const HAS_METHOD_NAME = 'has';
 	private const GET_METHOD_NAME = 'get';
 
 	/** @var TypeSpecifier */
 	private $typeSpecifier;
 
-	/** @var class-string */
-	private $className;
-
-	/**
-	 * @param class-string $className
-	 */
-	public function __construct(string $className)
-	{
-		$this->className = $className;
-	}
-
 	public function getClass(): string
 	{
-		return $this->className;
+		return self::INPUT_BAG_CLASS;
 	}
 
 	public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index afe60ca4..8dff057e 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -15,7 +15,7 @@ class ExtensionTest extends TypeInferenceTestCase
 	public function dataFileAsserts(): iterable
 	{
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php');
-		yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php');
 
 		if (class_exists('Symfony\Component\HttpFoundation\InputBag')) {
diff --git a/tests/Type/Symfony/data/header_bag.php b/tests/Type/Symfony/data/header_bag_get.php
similarity index 82%
rename from tests/Type/Symfony/data/header_bag.php
rename to tests/Type/Symfony/data/header_bag_get.php
index ce513bc1..e3f4c561 100644
--- a/tests/Type/Symfony/data/header_bag.php
+++ b/tests/Type/Symfony/data/header_bag_get.php
@@ -4,12 +4,6 @@
 
 $bag = new \Symfony\Component\HttpFoundation\HeaderBag(['foo' => ['bar']]);
 
-if ($bag->has('bar')) {
-	assertType('string', $bag->get('bar'));
-} else {
-	assertType('null', $bag->get('bar'));
-}
-
 assertType('string|null', $bag->get('foo'));
 assertType('string|null', $bag->get('foo', null));
 assertType('string', $bag->get('foo', 'baz'));

From 4a920ef43ae4253d1831c33ebeaa9c01de1db49b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Mon, 19 Dec 2022 13:24:33 +0100
Subject: [PATCH 206/297] Create release-toot.yml

---
 .github/workflows/release-toot.yml | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 .github/workflows/release-toot.yml

diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml
new file mode 100644
index 00000000..2af0f176
--- /dev/null
+++ b/.github/workflows/release-toot.yml
@@ -0,0 +1,21 @@
+name: Toot release
+
+# More triggers
+# https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#release
+on:
+  release:
+    types: [published]
+
+jobs:
+  toot:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: cbrgm/mastodon-github-action@v1
+        if: ${{ !github.event.repository.private }}
+        with:
+          # GitHub event payload
+          # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
+          message: "New release: ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} ${{ github.event.release.html_url }} #phpstan"
+        env:
+          MASTODON_URL: phpc.social
+          MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}

From 72cf8cfa4991747e9d858e602b26090682b77ba0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Tue, 20 Dec 2022 22:08:30 +0100
Subject: [PATCH 207/297] Update release-toot.yml

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

diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml
index 2af0f176..6a1c8156 100644
--- a/.github/workflows/release-toot.yml
+++ b/.github/workflows/release-toot.yml
@@ -17,5 +17,5 @@ jobs:
           # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
           message: "New release: ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} ${{ github.event.release.html_url }} #phpstan"
         env:
-          MASTODON_URL: phpc.social
+          MASTODON_URL: https://phpc.social
           MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}

From dac2474904b092267f0a19dfba8c46b6f21eab6a Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Thu, 22 Dec 2022 21:05:46 +0100
Subject: [PATCH 208/297] Add generics for PasswordUpgraderInterface

---
 extension.neon                                     |  3 +++
 .../User/PasswordAuthenticatedUserInterface.stub   |  7 +++++++
 .../Core/User/PasswordUpgraderInterface.stub       | 14 ++++++++++++++
 3 files changed, 24 insertions(+)
 create mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
 create mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub

diff --git a/extension.neon b/extension.neon
index 30f2139d..582126af 100644
--- a/extension.neon
+++ b/extension.neon
@@ -14,6 +14,7 @@ parameters:
 	featureToggles:
 		skipCheckGenericClasses:
 			- Symfony\Component\OptionsResolver\Options
+			- Symfony\Component\Security\Core\User\PasswordUpgraderInterface
 	stubFiles:
 		- stubs/Psr/Cache/CacheItemInterface.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
@@ -50,6 +51,8 @@ parameters:
 		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
 		- stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub
 		- stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub
+		- stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
+		- stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
diff --git a/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub b/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
new file mode 100644
index 00000000..19cc6040
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Security\Core\User;
+
+interface PasswordAuthenticatedUserInterface
+{
+}
diff --git a/stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub b/stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub
new file mode 100644
index 00000000..c73afc55
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Security\Core\User;
+
+/**
+ * @template TUser of PasswordAuthenticatedUserInterface
+ */
+interface PasswordUpgraderInterface
+{
+	/**
+	 * @param TUser $user
+	 */
+	public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void;
+}

From fca08349c27978d3dc87543502fadf08ed93cd7d Mon Sep 17 00:00:00 2001
From: Markus Staab <m.staab@complex-it.de>
Date: Thu, 29 Dec 2022 17:48:39 +0100
Subject: [PATCH 209/297] Utilize `null`-default return to simplify extensions

---
 ...CommandGetHelperDynamicReturnTypeExtension.php | 12 +++++-------
 ...rfaceGetArgumentDynamicReturnTypeExtension.php | 13 +++++--------
 ...terfaceGetOptionDynamicReturnTypeExtension.php | 13 +++++--------
 ...erfaceGetOptionsDynamicReturnTypeExtension.php |  8 +++-----
 ...rfaceHasArgumentDynamicReturnTypeExtension.php | 15 ++++++---------
 ...terfaceHasOptionDynamicReturnTypeExtension.php | 15 ++++++---------
 6 files changed, 30 insertions(+), 46 deletions(-)

diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
index db2348e3..3b38258c 100644
--- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
@@ -36,22 +36,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return $methodReflection->getName() === 'getHelper';
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
-		$defaultReturnType = new ObjectType('Symfony\Component\Console\Helper\HelperInterface');
-
 		if (!isset($methodCall->getArgs()[0])) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($argStrings) !== 1) {
-			return $defaultReturnType;
+			return null;
 		}
 		$argName = $argStrings[0]->getValue();
 
@@ -65,7 +63,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			}
 		}
 
-		return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : $defaultReturnType;
+		return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : null;
 	}
 
 }
diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index a8c35200..0af7c88d 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -6,7 +6,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
@@ -38,22 +37,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return $methodReflection->getName() === 'getArgument';
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
-		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
-
 		if (!isset($methodCall->getArgs()[0])) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($argStrings) !== 1) {
-			return $defaultReturnType;
+			return null;
 		}
 		$argName = $argStrings[0]->getValue();
 
@@ -79,7 +76,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			}
 		}
 
-		return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : $defaultReturnType;
+		return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : null;
 	}
 
 }
diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
index ad2bc6c6..d765f65b 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -6,7 +6,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Type;
@@ -39,22 +38,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return $methodReflection->getName() === 'getOption';
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
-		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
-
 		if (!isset($methodCall->getArgs()[0])) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($optStrings) !== 1) {
-			return $defaultReturnType;
+			return null;
 		}
 		$optName = $optStrings[0]->getValue();
 
@@ -69,7 +66,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			}
 		}
 
-		return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : $defaultReturnType;
+		return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : null;
 	}
 
 }
diff --git a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
index 80c6d4e2..515c44d0 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
@@ -6,7 +6,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
 use PHPStan\Type\Constant\ConstantStringType;
@@ -40,12 +39,11 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return $methodReflection->getName() === 'getOptions';
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
-		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType();
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$optTypes = [];
@@ -65,7 +63,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			}
 		}
 
-		return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : $defaultReturnType;
+		return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : null;
 	}
 
 }
diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
index dc933e14..4ce49c4a 100644
--- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
@@ -7,7 +7,6 @@
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Symfony\ConsoleApplicationResolver;
-use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Type;
@@ -36,22 +35,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return $methodReflection->getName() === 'hasArgument';
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
-		$defaultReturnType = new BooleanType();
-
 		if (!isset($methodCall->getArgs()[0])) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($argStrings) !== 1) {
-			return $defaultReturnType;
+			return null;
 		}
 		$argName = $argStrings[0]->getValue();
 
@@ -67,11 +64,11 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 		}
 
 		if (count($returnTypes) === 0) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$returnTypes = array_unique($returnTypes);
-		return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType;
+		return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : null;
 	}
 
 }
diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
index 3f239fe6..3be621f8 100644
--- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
@@ -7,7 +7,6 @@
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Symfony\ConsoleApplicationResolver;
-use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Type;
@@ -36,22 +35,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return $methodReflection->getName() === 'hasOption';
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
-		$defaultReturnType = new BooleanType();
-
 		if (!isset($methodCall->getArgs()[0])) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
 		if (count($optStrings) !== 1) {
-			return $defaultReturnType;
+			return null;
 		}
 		$optName = $optStrings[0]->getValue();
 
@@ -67,11 +64,11 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 		}
 
 		if (count($returnTypes) === 0) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$returnTypes = array_unique($returnTypes);
-		return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType;
+		return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : null;
 	}
 
 }

From d89a521e7e31822409bf37f70691f7a12a6e7d76 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Sat, 14 Jan 2023 23:21:38 +0100
Subject: [PATCH 210/297] Revert wrong typehint for getSession

---
 stubs/Symfony/Component/HttpFoundation/Request.stub  | 5 -----
 tests/Type/Symfony/data/request_get_session.php      | 6 +++---
 tests/Type/Symfony/data/request_get_session_null.php | 6 +++---
 3 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub
index 861769df..0c2140cc 100644
--- a/stubs/Symfony/Component/HttpFoundation/Request.stub
+++ b/stubs/Symfony/Component/HttpFoundation/Request.stub
@@ -26,11 +26,6 @@ class Request
 	 */
 	public $cookies;
 
-    /**
-     * @return \Symfony\Component\HttpFoundation\Session\Session
-     */
-    public function getSession();
-
     /**
      * @return string[]
      */
diff --git a/tests/Type/Symfony/data/request_get_session.php b/tests/Type/Symfony/data/request_get_session.php
index 17ef88a6..9a0335e5 100644
--- a/tests/Type/Symfony/data/request_get_session.php
+++ b/tests/Type/Symfony/data/request_get_session.php
@@ -1,14 +1,14 @@
 <?php declare(strict_types = 1);
 
-use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
 use function PHPStan\Testing\assertType;
 
 /** @var \Symfony\Component\HttpFoundation\Request $request */
 $request = doRequest();
 
 $session1 = $request->getSession();
-assertType(Session::class, $request->getSession());
+assertType(SessionInterface::class, $request->getSession());
 
 if ($request->hasSession()) {
-	assertType(Session::class, $request->getSession());
+	assertType(SessionInterface::class, $request->getSession());
 }
diff --git a/tests/Type/Symfony/data/request_get_session_null.php b/tests/Type/Symfony/data/request_get_session_null.php
index be0fd0d1..9c37979d 100644
--- a/tests/Type/Symfony/data/request_get_session_null.php
+++ b/tests/Type/Symfony/data/request_get_session_null.php
@@ -1,14 +1,14 @@
 <?php declare(strict_types = 1);
 
-use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
 use function PHPStan\Testing\assertType;
 
 /** @var \Symfony\Component\HttpFoundation\Request $request */
 $request = doRequest();
 
 $session1 = $request->getSession();
-assertType(Session::class . '|null', $request->getSession());
+assertType(SessionInterface::class . '|null', $request->getSession());
 
 if ($request->hasSession()) {
-	assertType(Session::class, $request->getSession());
+	assertType(SessionInterface::class, $request->getSession());
 }

From 44aef787a1062d2eec7932e958f0fbe6b2a22a67 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Tue, 31 Jan 2023 16:20:29 +0100
Subject: [PATCH 211/297] Introduce Voter stub

---
 extension.neon                                |  2 ++
 .../Core/Authorization/Voter/Voter.stub       | 35 +++++++++++++++++++
 2 files changed, 37 insertions(+)
 create mode 100644 stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub

diff --git a/extension.neon b/extension.neon
index 582126af..e0c53ba0 100644
--- a/extension.neon
+++ b/extension.neon
@@ -14,6 +14,7 @@ parameters:
 	featureToggles:
 		skipCheckGenericClasses:
 			- Symfony\Component\OptionsResolver\Options
+			- Symfony\Component\Security\Core\Authorization\Voter\Voter
 			- Symfony\Component\Security\Core\User\PasswordUpgraderInterface
 	stubFiles:
 		- stubs/Psr/Cache/CacheItemInterface.stub
@@ -50,6 +51,7 @@ parameters:
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
 		- stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub
+		- stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub
 		- stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub
 		- stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
 		- stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub
diff --git a/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub
new file mode 100644
index 00000000..17485a28
--- /dev/null
+++ b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub
@@ -0,0 +1,35 @@
+<?php
+
+namespace Symfony\Component\Security\Core\Authorization\Voter;
+
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+/**
+ * @template TAttribute of string
+ * @template TSubject of mixed
+ */
+abstract class Voter implements VoterInterface
+{
+    /**
+     * Determines if the attribute and subject are supported by this voter.
+     *
+     * @param mixed $subject
+     *
+     * @phpstan-assert-if-true TSubject $subject
+     * @phpstan-assert-if-true TAttribute $attribute
+     *
+     * @return bool
+     */
+    abstract protected function supports(string $attribute, $subject);
+
+    /**
+     * Perform a single access check operation on a given attribute, subject and token.
+     * It is safe to assume that $attribute and $subject already passed the "supports()" method check.
+     *
+     * @phpstan-param TAttribute $attribute
+     * @phpstan-param TSubject $subject
+     *
+     * @return bool
+     */
+    abstract protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token);
+}

From 99ff19f57de10ce4f2a3132c3eb0d5f117665258 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 31 Jan 2023 16:32:08 +0100
Subject: [PATCH 212/297] Fix build

---
 .../Symfony/InvalidArgumentDefaultValueRule.php      |  2 +-
 src/Rules/Symfony/InvalidOptionDefaultValueRule.php  |  2 +-
 src/Type/Symfony/EnvelopeReturnTypeExtension.php     | 11 ++++++++---
 .../Symfony/SerializerDynamicReturnTypeExtension.php | 12 ++++++++----
 4 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index 51d7c3bc..e48f8718 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -46,7 +46,7 @@ public function processNode(Node $node, Scope $scope): array
 		}
 
 		$modeType = isset($node->getArgs()[1]) ? $scope->getType($node->getArgs()[1]->value) : new NullType();
-		if ($modeType instanceof NullType) {
+		if ($modeType->isNull()->yes()) {
 			$modeType = new ConstantIntegerType(2); // InputArgument::OPTIONAL
 		}
 		$modeTypes = TypeUtils::getConstantScalars($modeType);
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index ab52d626..171f94f1 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -48,7 +48,7 @@ public function processNode(Node $node, Scope $scope): array
 		}
 
 		$modeType = isset($node->getArgs()[2]) ? $scope->getType($node->getArgs()[2]->value) : new NullType();
-		if ($modeType instanceof NullType) {
+		if ($modeType->isNull()->yes()) {
 			$modeType = new ConstantIntegerType(1); // InputOption::VALUE_NONE
 		}
 		$modeTypes = TypeUtils::getConstantScalars($modeType);
diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
index ec2531aa..2e01dec7 100644
--- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php
+++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
@@ -7,12 +7,12 @@
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\Accessory\AccessoryArrayListType;
 use PHPStan\Type\ArrayType;
-use PHPStan\Type\Constant\ConstantStringType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Generic\GenericClassStringType;
 use PHPStan\Type\IntegerType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
 use function count;
 
 final class EnvelopeReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -42,11 +42,16 @@ public function getTypeFromMethodCall(
 		}
 
 		$argType = $scope->getType($methodCall->getArgs()[0]->value);
-		if (!$argType instanceof ConstantStringType) {
+		if (count($argType->getConstantStrings()) === 0) {
 			return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')));
 		}
 
-		return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType($argType->getValue())));
+		$objectTypes = [];
+		foreach ($argType->getConstantStrings() as $constantString) {
+			$objectTypes[] = new ObjectType($constantString->getValue());
+		}
+
+		return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), TypeCombinator::union(...$objectTypes)));
 	}
 
 }
diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
index 5525a770..d3ea959d 100755
--- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
@@ -6,11 +6,12 @@
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\ArrayType;
-use PHPStan\Type\Constant\ConstantStringType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\MixedType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
+use function count;
 use function substr;
 
 class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -45,13 +46,16 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 		}
 
 		$argType = $scope->getType($methodCall->getArgs()[1]->value);
-		if (!$argType instanceof ConstantStringType) {
+		if (count($argType->getConstantStrings()) === 0) {
 			return new MixedType();
 		}
 
-		$objectName = $argType->getValue();
+		$types = [];
+		foreach ($argType->getConstantStrings() as $constantString) {
+			$types[] = $this->getType($constantString->getValue());
+		}
 
-		return $this->getType($objectName);
+		return TypeCombinator::union(...$types);
 	}
 
 	private function getType(string $objectName): Type

From d0151d06555e446634c7b8175d1feb292b102085 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 31 Jan 2023 16:41:53 +0100
Subject: [PATCH 213/297] Bump minimum PHPStan version

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index adc7050a..465f7050 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.9.1"
+    "phpstan/phpstan": "^1.9.4"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From 2107deec9d95c0e7fbba35fe4d34287342878ef7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20W=C5=82odarczyk?= <admin@hxv.me>
Date: Tue, 31 Jan 2023 19:10:01 +0100
Subject: [PATCH 214/297] Add ArrayObject to NormalizerInterface::normalize()
 return type.

---
 .../Component/Serializer/Normalizer/NormalizerInterface.stub | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
index a65b5bed..8fc966ad 100644
--- a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
@@ -2,13 +2,16 @@
 
 namespace Symfony\Component\Serializer\Normalizer;
 
+use ArrayObject;
+
 interface NormalizerInterface
 {
     /**
      * @param mixed $object
      * @param string|null $format
      * @param array<mixed> $context
-     * @return array<mixed>|string|int|float|bool|null
+     *
+     * @return array<mixed>|ArrayObject<array-key, mixed>|string|int|float|bool|null
      */
     public function normalize($object, $format = null, array $context = []);
 

From cbf5b9ceadab8365ed46db42dcd23db1a4c26c93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20W=C5=82odarczyk?= <admin@hxv.me>
Date: Tue, 31 Jan 2023 19:23:00 +0100
Subject: [PATCH 215/297] Add @throws annotations in
 DenormalizerInterface::denormalize() and NormalizerInterface::normalize().

---
 extension.neon                                |  8 ++++++++
 .../Exception/BadMethodCallException.stub     |  7 +++++++
 .../Exception/CircularReferenceException.stub |  7 +++++++
 .../Exception/ExceptionInterface.stub         |  7 +++++++
 .../Exception/ExtraAttributesException.stub   | 20 +++++++++++++++++++
 .../Exception/InvalidArgumentException.stub   |  7 +++++++
 .../Serializer/Exception/LogicException.stub  |  7 +++++++
 .../Exception/RuntimeException.stub           |  7 +++++++
 .../Exception/UnexpectedValueException.stub   |  7 +++++++
 .../Normalizer/DenormalizerInterface.stub     | 16 +++++++++++++++
 .../Normalizer/NormalizerInterface.stub       |  9 +++++++++
 11 files changed, 102 insertions(+)
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/CircularReferenceException.stub
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/ExceptionInterface.stub
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/LogicException.stub
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub
 create mode 100644 stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub

diff --git a/extension.neon b/extension.neon
index e0c53ba0..42ae87ba 100644
--- a/extension.neon
+++ b/extension.neon
@@ -58,6 +58,14 @@ parameters:
 		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
+		- stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub
+		- stubs/Symfony/Component/Serializer/Exception/CircularReferenceException.stub
+		- stubs/Symfony/Component/Serializer/Exception/ExceptionInterface.stub
+		- stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub
+		- stubs/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub
+		- stubs/Symfony/Component/Serializer/Exception/LogicException.stub
+		- stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub
+		- stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub
 		- stubs/Symfony/Component/Serializer/Normalizer/ContextAwareDenormalizerInterface.stub
 		- stubs/Symfony/Component/Serializer/Normalizer/ContextAwareNormalizerInterface.stub
 		- stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub
diff --git a/stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub b/stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub
new file mode 100644
index 00000000..3c569971
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/Serializer/Exception/CircularReferenceException.stub b/stubs/Symfony/Component/Serializer/Exception/CircularReferenceException.stub
new file mode 100644
index 00000000..30b5e55b
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/CircularReferenceException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+class CircularReferenceException extends RuntimeException
+{
+}
diff --git a/stubs/Symfony/Component/Serializer/Exception/ExceptionInterface.stub b/stubs/Symfony/Component/Serializer/Exception/ExceptionInterface.stub
new file mode 100644
index 00000000..d9563924
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/ExceptionInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub b/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub
new file mode 100644
index 00000000..18621931
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub
@@ -0,0 +1,20 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+class ExtraAttributesException extends RuntimeException
+{
+    /**
+     * @param string[] $extraAttributes
+     */
+    public function __construct(array $extraAttributes, \Throwable $previous = null)
+    {
+    }
+
+    /**
+     * @return string[]
+     */
+    public function getExtraAttributes()
+    {
+    }
+}
diff --git a/stubs/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub b/stubs/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub
new file mode 100644
index 00000000..045906c0
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/Serializer/Exception/LogicException.stub b/stubs/Symfony/Component/Serializer/Exception/LogicException.stub
new file mode 100644
index 00000000..101feb67
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/LogicException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub b/stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub
new file mode 100644
index 00000000..14a4e78e
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub b/stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub
new file mode 100644
index 00000000..5fbb9b6c
--- /dev/null
+++ b/stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Serializer\Exception;
+
+class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
index da4ab673..cb9af06d 100644
--- a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
@@ -2,6 +2,14 @@
 
 namespace Symfony\Component\Serializer\Normalizer;
 
+use Symfony\Component\Serializer\Exception\BadMethodCallException;
+use Symfony\Component\Serializer\Exception\ExceptionInterface;
+use Symfony\Component\Serializer\Exception\ExtraAttributesException;
+use Symfony\Component\Serializer\Exception\InvalidArgumentException;
+use Symfony\Component\Serializer\Exception\LogicException;
+use Symfony\Component\Serializer\Exception\RuntimeException;
+use Symfony\Component\Serializer\Exception\UnexpectedValueException;
+
 interface DenormalizerInterface
 {
     /**
@@ -10,6 +18,14 @@ interface DenormalizerInterface
      * @param string|null $format
      * @param array<mixed> $context
      * @return mixed
+     *
+     * @throws BadMethodCallException
+     * @throws InvalidArgumentException
+     * @throws UnexpectedValueException
+     * @throws ExtraAttributesException
+     * @throws LogicException
+     * @throws RuntimeException
+     * @throws ExceptionInterface
      */
     public function denormalize($data, $type, $format = null, array $context = []);
 
diff --git a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
index 8fc966ad..fac60042 100644
--- a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
@@ -3,6 +3,10 @@
 namespace Symfony\Component\Serializer\Normalizer;
 
 use ArrayObject;
+use Symfony\Component\Serializer\Exception\CircularReferenceException;
+use Symfony\Component\Serializer\Exception\ExceptionInterface;
+use Symfony\Component\Serializer\Exception\InvalidArgumentException;
+use Symfony\Component\Serializer\Exception\LogicException;
 
 interface NormalizerInterface
 {
@@ -12,6 +16,11 @@ interface NormalizerInterface
      * @param array<mixed> $context
      *
      * @return array<mixed>|ArrayObject<array-key, mixed>|string|int|float|bool|null
+     *
+     * @throws InvalidArgumentException
+     * @throws CircularReferenceException
+     * @throws LogicException
+     * @throws ExceptionInterface
      */
     public function normalize($object, $format = null, array $context = []);
 

From 8a8d0538ca943b20beda7e9799e14fb3683262d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20W=C5=82odarczyk?= <admin@hxv.me>
Date: Sat, 4 Feb 2023 09:56:44 +0100
Subject: [PATCH 216/297] Add missing @throws in stubs.

---
 extension.neon                                             | 5 +++++
 stubs/Psr/Cache/CacheException.stub                        | 7 +++++++
 stubs/Psr/Cache/InvalidArgumentException.stub              | 7 +++++++
 stubs/Symfony/Component/Console/Command.stub               | 6 ++++++
 .../Component/Console/Exception/ExceptionInterface.stub    | 7 +++++++
 .../Console/Exception/InvalidArgumentException.stub        | 7 +++++++
 .../Component/Console/Exception/LogicException.stub        | 7 +++++++
 .../DependencyInjection/Extension/ExtensionInterface.stub  | 2 ++
 .../Component/Serializer/Encoder/DecoderInterface.stub     | 2 ++
 .../Component/Serializer/Encoder/EncoderInterface.stub     | 4 ++++
 stubs/Symfony/Contracts/Cache/CacheInterface.stub          | 4 ++++
 11 files changed, 58 insertions(+)
 create mode 100644 stubs/Psr/Cache/CacheException.stub
 create mode 100644 stubs/Psr/Cache/InvalidArgumentException.stub
 create mode 100644 stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub
 create mode 100644 stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub
 create mode 100644 stubs/Symfony/Component/Console/Exception/LogicException.stub

diff --git a/extension.neon b/extension.neon
index 42ae87ba..e4880077 100644
--- a/extension.neon
+++ b/extension.neon
@@ -17,11 +17,16 @@ parameters:
 			- Symfony\Component\Security\Core\Authorization\Voter\Voter
 			- Symfony\Component\Security\Core\User\PasswordUpgraderInterface
 	stubFiles:
+		- stubs/Psr/Cache/CacheException.stub
 		- stubs/Psr/Cache/CacheItemInterface.stub
+		- stubs/Psr/Cache/InvalidArgumentException.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
 		- stubs/Symfony/Component/Console/Command.stub
+		- stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub
+		- stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub
+		- stubs/Symfony/Component/Console/Exception/LogicException.stub
 		- stubs/Symfony/Component/Console/Helper/HelperInterface.stub
 		- stubs/Symfony/Component/Console/Output/OutputInterface.stub
 		- stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub
diff --git a/stubs/Psr/Cache/CacheException.stub b/stubs/Psr/Cache/CacheException.stub
new file mode 100644
index 00000000..1be3e49b
--- /dev/null
+++ b/stubs/Psr/Cache/CacheException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Psr\Cache;
+
+interface CacheException extends \Throwable
+{
+}
diff --git a/stubs/Psr/Cache/InvalidArgumentException.stub b/stubs/Psr/Cache/InvalidArgumentException.stub
new file mode 100644
index 00000000..b562bfc0
--- /dev/null
+++ b/stubs/Psr/Cache/InvalidArgumentException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Psr\Cache;
+
+interface InvalidArgumentException extends CacheException
+{
+}
diff --git a/stubs/Symfony/Component/Console/Command.stub b/stubs/Symfony/Component/Console/Command.stub
index 6bc2ea4b..c656ef6a 100644
--- a/stubs/Symfony/Component/Console/Command.stub
+++ b/stubs/Symfony/Component/Console/Command.stub
@@ -2,10 +2,16 @@
 
 namespace Symfony\Component\Console\Command;
 
+use Symfony\Component\Console\Exception\InvalidArgumentException;
+use Symfony\Component\Console\Exception\LogicException;
+
 class Command
 {
     /**
      * @return \Symfony\Component\Console\Helper\HelperInterface
+     *
+     * @throws LogicException
+     * @throws InvalidArgumentException
      */
     public function getHelper(string $name);
 }
diff --git a/stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub b/stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub
new file mode 100644
index 00000000..6e3c1226
--- /dev/null
+++ b/stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Console\Exception;
+
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub b/stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub
new file mode 100644
index 00000000..1fe7e7bd
--- /dev/null
+++ b/stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Console\Exception;
+
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/Console/Exception/LogicException.stub b/stubs/Symfony/Component/Console/Exception/LogicException.stub
new file mode 100644
index 00000000..a959b8e9
--- /dev/null
+++ b/stubs/Symfony/Component/Console/Exception/LogicException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\Console\Exception;
+
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub b/stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
index d0b04792..b1229723 100644
--- a/stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
+++ b/stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub
@@ -8,6 +8,8 @@ interface ExtensionInterface
 {
     /**
      * @param array<mixed> $configs
+     *
+     * @throws \InvalidArgumentException
      */
     public function load(array $configs, ContainerBuilder $container): void;
 }
diff --git a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
index 715dadc4..a9923edf 100644
--- a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
@@ -11,6 +11,8 @@ interface DecoderInterface
      * @param string $format
      * @param array<mixed> $context
      * @return mixed
+     *
+     * @throws UnexpectedValueException
      */
     public function decode($data, $format, array $context = []);
 
diff --git a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
index eb6fc97c..2d97bde3 100644
--- a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
@@ -2,6 +2,8 @@
 
 namespace Symfony\Component\Serializer\Encoder;
 
+use Symfony\Component\Serializer\Exception\UnexpectedValueException;
+
 interface EncoderInterface
 {
     /**
@@ -9,6 +11,8 @@ interface EncoderInterface
      * @param string $format
      * @param array<mixed> $context
      * @return string
+     *
+     * @throws UnexpectedValueException
      */
     public function encode($data, $format, array $context = []);
 
diff --git a/stubs/Symfony/Contracts/Cache/CacheInterface.stub b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
index f4cc208d..ff3027b1 100644
--- a/stubs/Symfony/Contracts/Cache/CacheInterface.stub
+++ b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
@@ -2,6 +2,8 @@
 
 namespace Symfony\Contracts\Cache;
 
+use Psr\Cache\InvalidArgumentException;
+
 interface CacheInterface
 {
     /**
@@ -10,6 +12,8 @@ interface CacheInterface
      * @param \Symfony\Contracts\Cache\CallbackInterface<T>|callable(\Symfony\Contracts\Cache\ItemInterface, bool): T $callback
      * @param array<mixed> $metadata
      * @return T
+     *
+     * @throws InvalidArgumentException
      */
     public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);
 }

From 58e64ffc912c984f448e5ed800ca6c8ce343a0e9 Mon Sep 17 00:00:00 2001
From: jmleroux <jmleroux.pro@gmail.com>
Date: Wed, 15 Feb 2023 05:55:06 +0100
Subject: [PATCH 217/297] Add PHPUnit compatibility for PHP 7.2

---
 .github/workflows/build.yml | 12 ------------
 composer.json               |  2 +-
 2 files changed, 1 insertion(+), 13 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1045f11c..96765433 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -33,10 +33,6 @@ jobs:
           coverage: "none"
           php-version: "${{ matrix.php-version }}"
 
-      - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.2'
-        run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies"
-
       - name: "Validate Composer"
         run: "composer validate"
 
@@ -101,10 +97,6 @@ jobs:
           coverage: "none"
           php-version: "${{ matrix.php-version }}"
 
-      - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.2'
-        run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies"
-
       - name: "Install lowest dependencies"
         if: ${{ matrix.dependencies == 'lowest' }}
         run: "composer update --prefer-lowest --no-interaction --no-progress"
@@ -146,10 +138,6 @@ jobs:
           extensions: mbstring
           tools: composer:v2
 
-      - name: "Downgrade PHPUnit"
-        if: matrix.php-version == '7.2'
-        run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies"
-
       - name: "Install lowest dependencies"
         if: ${{ matrix.dependencies == 'lowest' }}
         run: "composer update --prefer-lowest --no-interaction --no-progress"
diff --git a/composer.json b/composer.json
index 465f7050..9f8c6569 100644
--- a/composer.json
+++ b/composer.json
@@ -25,7 +25,7 @@
     "php-parallel-lint/php-parallel-lint": "^1.2",
     "phpstan/phpstan-phpunit": "^1.0",
     "phpstan/phpstan-strict-rules": "^1.0",
-    "phpunit/phpunit": "^9.5",
+    "phpunit/phpunit": "^8.5.29 || ^9.5",
     "psr/container": "1.0 || 1.1.1",
     "symfony/config": "^5.4 || ^6.1",
     "symfony/console": "^5.4 || ^6.1",

From 2f363239357b849bc150295b554100c106b6c26b Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 8 Mar 2023 17:31:10 +0100
Subject: [PATCH 218/297] Fix build

---
 src/Type/Symfony/ParameterDynamicReturnTypeExtension.php | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index b160ddc8..82d3599e 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -14,7 +14,6 @@
 use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantArrayType;
 use PHPStan\Type\Constant\ConstantBooleanType;
-use PHPStan\Type\Constant\ConstantStringType;
 use PHPStan\Type\ConstantType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FloatType;
@@ -160,9 +159,12 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type
 				$keyTypes = [];
 				$valueTypes = [];
 				foreach ($value as $key => $element) {
-					/** @var ConstantStringType $keyType */
 					$keyType = $scope->getTypeFromValue($key);
-					$keyTypes[] = $keyType;
+					$keyStringTypes = $keyType->getConstantStrings();
+					if (count($keyStringTypes) !== 1) {
+						throw new ShouldNotHappenException();
+					}
+					$keyTypes[] = $keyStringTypes[0];
 					$valueTypes[] = $this->generalizeTypeFromValue($scope, $element);
 				}
 

From d7425ae62493f0462e6d3aa18cba8b90dea897f1 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 20 Mar 2023 02:31:07 +0000
Subject: [PATCH 219/297] Update metcalfc/changelog-generator action to v4.1.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 bac4a006..92b72547 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.0.1
+        uses: metcalfc/changelog-generator@v4.1.0
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From e0823d3daf5220ea2a07751ddffa3ad0882bdfe8 Mon Sep 17 00:00:00 2001
From: Markus Staab <maggus.staab@googlemail.com>
Date: Wed, 22 Mar 2023 09:34:23 +0100
Subject: [PATCH 220/297] Update PHPCS

---
 build-cs/composer.json |   7 +--
 build-cs/composer.lock | 117 ++++++++++++++++++++++-------------------
 2 files changed, 67 insertions(+), 57 deletions(-)

diff --git a/build-cs/composer.json b/build-cs/composer.json
index e3079710..16a240bc 100644
--- a/build-cs/composer.json
+++ b/build-cs/composer.json
@@ -1,8 +1,9 @@
 {
 	"require-dev": {
-		"consistence-community/coding-standard": "^3.10",
-		"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
-		"slevomat/coding-standard": "^7.0"
+		"consistence-community/coding-standard": "^3.11.0",
+		"dealerdirect/phpcodesniffer-composer-installer": "^1.0.0",
+		"slevomat/coding-standard": "^8.8.0",
+		"squizlabs/php_codesniffer": "^3.5.3"
 	},
 	"config": {
 		"allow-plugins": {
diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 4bcc8de4..c25a151a 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -4,35 +4,35 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "4485bbedba7bcc71ace5f69dbb9b6c47",
+    "content-hash": "e69c1916405a7e3c8001c1b609a0ee61",
     "packages": [],
     "packages-dev": [
         {
             "name": "consistence-community/coding-standard",
-            "version": "3.11.1",
+            "version": "3.11.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/consistence-community/coding-standard.git",
-                "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df"
+                "reference": "adb4be482e76990552bf624309d2acc8754ba1bd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df",
-                "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df",
+                "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/adb4be482e76990552bf624309d2acc8754ba1bd",
+                "reference": "adb4be482e76990552bf624309d2acc8754ba1bd",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.4",
-                "slevomat/coding-standard": "~7.0",
-                "squizlabs/php_codesniffer": "~3.6.0"
+                "php": "~8.0",
+                "slevomat/coding-standard": "~8.0",
+                "squizlabs/php_codesniffer": "~3.7.0"
             },
             "replace": {
                 "consistence/coding-standard": "3.10.*"
             },
             "require-dev": {
-                "phing/phing": "2.16.4",
-                "php-parallel-lint/php-parallel-lint": "1.3.0",
-                "phpunit/phpunit": "9.5.4"
+                "phing/phing": "2.17.0",
+                "php-parallel-lint/php-parallel-lint": "1.3.1",
+                "phpunit/phpunit": "9.5.10"
             },
             "type": "library",
             "autoload": {
@@ -70,41 +70,44 @@
             ],
             "support": {
                 "issues": "https://github.com/consistence-community/coding-standard/issues",
-                "source": "https://github.com/consistence-community/coding-standard/tree/3.11.1"
+                "source": "https://github.com/consistence-community/coding-standard/tree/3.11.2"
             },
-            "time": "2021-05-03T18:13:22+00:00"
+            "time": "2022-06-21T08:36:36+00:00"
         },
         {
             "name": "dealerdirect/phpcodesniffer-composer-installer",
-            "version": "v0.7.2",
+            "version": "v1.0.0",
             "source": {
                 "type": "git",
-                "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
-                "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db"
+                "url": "https://github.com/PHPCSStandards/composer-installer.git",
+                "reference": "4be43904336affa5c2f70744a348312336afd0da"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
-                "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
+                "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
+                "reference": "4be43904336affa5c2f70744a348312336afd0da",
                 "shasum": ""
             },
             "require": {
                 "composer-plugin-api": "^1.0 || ^2.0",
-                "php": ">=5.3",
+                "php": ">=5.4",
                 "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
             },
             "require-dev": {
                 "composer/composer": "*",
+                "ext-json": "*",
+                "ext-zip": "*",
                 "php-parallel-lint/php-parallel-lint": "^1.3.1",
-                "phpcompatibility/php-compatibility": "^9.0"
+                "phpcompatibility/php-compatibility": "^9.0",
+                "yoast/phpunit-polyfills": "^1.0"
             },
             "type": "composer-plugin",
             "extra": {
-                "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+                "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
             },
             "autoload": {
                 "psr-4": {
-                    "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+                    "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -120,7 +123,7 @@
                 },
                 {
                     "name": "Contributors",
-                    "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors"
+                    "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
                 }
             ],
             "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
@@ -144,23 +147,23 @@
                 "tests"
             ],
             "support": {
-                "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
-                "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
+                "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
+                "source": "https://github.com/PHPCSStandards/composer-installer"
             },
-            "time": "2022-02-04T12:51:07+00:00"
+            "time": "2023-01-05T11:28:13+00:00"
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.5.1",
+            "version": "1.15.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "981cc368a216c988e862a75e526b6076987d1b50"
+                "reference": "61800f71a5526081d1b5633766aa88341f1ade76"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50",
-                "reference": "981cc368a216c988e862a75e526b6076987d1b50",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/61800f71a5526081d1b5633766aa88341f1ade76",
+                "reference": "61800f71a5526081d1b5633766aa88341f1ade76",
                 "shasum": ""
             },
             "require": {
@@ -170,6 +173,7 @@
                 "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",
                 "symfony/process": "^5.2"
@@ -189,43 +193,43 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.3"
             },
-            "time": "2022-05-05T11:32:40+00:00"
+            "time": "2022-12-20T20:56:55+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "7.2.1",
+            "version": "8.8.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90"
+                "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90",
-                "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/59e25146a4ef0a7b194c5bc55b32dd414345db89",
+                "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89",
                 "shasum": ""
             },
             "require": {
-                "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
                 "php": "^7.2 || ^8.0",
-                "phpstan/phpdoc-parser": "^1.5.1",
-                "squizlabs/php_codesniffer": "^3.6.2"
+                "phpstan/phpdoc-parser": ">=1.15.2 <1.16.0",
+                "squizlabs/php_codesniffer": "^3.7.1"
             },
             "require-dev": {
-                "phing/phing": "2.17.3",
+                "phing/phing": "2.17.4",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.10|1.7.1",
-                "phpstan/phpstan-deprecation-rules": "1.0.0",
-                "phpstan/phpstan-phpunit": "1.0.0|1.1.1",
-                "phpstan/phpstan-strict-rules": "1.2.3",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.5.20"
+                "phpstan/phpstan": "1.4.10|1.9.6",
+                "phpstan/phpstan-deprecation-rules": "1.1.1",
+                "phpstan/phpstan-phpunit": "1.0.0|1.3.3",
+                "phpstan/phpstan-strict-rules": "1.4.4",
+                "phpunit/phpunit": "7.5.20|8.5.21|9.5.27"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "7.x-dev"
+                    "dev-master": "8.x-dev"
                 }
             },
             "autoload": {
@@ -238,9 +242,13 @@
                 "MIT"
             ],
             "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+            "keywords": [
+                "dev",
+                "phpcs"
+            ],
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/7.2.1"
+                "source": "https://github.com/slevomat/coding-standard/tree/8.8.0"
             },
             "funding": [
                 {
@@ -252,20 +260,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-25T10:58:12+00:00"
+            "time": "2023-01-09T10:46:13+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",
-            "version": "3.6.2",
+            "version": "3.7.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a"
+                "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a",
-                "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879",
+                "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879",
                 "shasum": ""
             },
             "require": {
@@ -301,14 +309,15 @@
             "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
             "keywords": [
                 "phpcs",
-                "standards"
+                "standards",
+                "static analysis"
             ],
             "support": {
                 "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
                 "source": "https://github.com/squizlabs/PHP_CodeSniffer",
                 "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
             },
-            "time": "2021-12-12T21:44:58+00:00"
+            "time": "2023-02-22T23:07:41+00:00"
         }
     ],
     "aliases": [],

From db81b1861aac7cc2e66115cb33b4d1ea2a73d096 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Thu, 30 Mar 2023 10:38:10 +0200
Subject: [PATCH 221/297] Use stubfile extension for PasswordUpgraderInterface

---
 composer.json                                 |  2 +-
 extension.neon                                |  6 ++--
 src/Symfony/InputBagStubFilesExtension.php    | 17 +++++++--
 ...ordAuthenticatedUserStubFilesExtension.php | 36 +++++++++++++++++++
 4 files changed, 56 insertions(+), 5 deletions(-)
 create mode 100644 src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php

diff --git a/composer.json b/composer.json
index 9f8c6569..22501953 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.9.4"
+    "phpstan/phpstan": "^1.9.18"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index e4880077..1a8cf313 100644
--- a/extension.neon
+++ b/extension.neon
@@ -58,8 +58,6 @@ parameters:
 		- stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub
 		- stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub
 		- stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub
-		- stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
-		- stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
 		- stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
@@ -298,6 +296,10 @@ services:
 		class: PHPStan\Symfony\InputBagStubFilesExtension
 		tags:
 			- phpstan.stubFilesExtension
+	-
+		class: PHPStan\Symfony\PasswordAuthenticatedUserStubFilesExtension
+		tags:
+			- phpstan.stubFilesExtension
 
 	# FormInterface::getErrors() return type
 	-
diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php
index 834ca228..140dae99 100644
--- a/src/Symfony/InputBagStubFilesExtension.php
+++ b/src/Symfony/InputBagStubFilesExtension.php
@@ -2,15 +2,28 @@
 
 namespace PHPStan\Symfony;
 
+use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound;
+use PHPStan\BetterReflection\Reflector\Reflector;
 use PHPStan\PhpDoc\StubFilesExtension;
-use function class_exists;
 
 class InputBagStubFilesExtension implements StubFilesExtension
 {
 
+	/** @var Reflector */
+	private $reflector;
+
+	public function __construct(
+		Reflector $reflector
+	)
+	{
+		$this->reflector = $reflector;
+	}
+
 	public function getFiles(): array
 	{
-		if (!class_exists('Symfony\Component\HttpFoundation\InputBag')) {
+		try {
+			$this->reflector->reflectClass('Symfony\Component\HttpFoundation\InputBag');
+		} catch (IdentifierNotFound $e) {
 			return [];
 		}
 
diff --git a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
new file mode 100644
index 00000000..8f8c4782
--- /dev/null
+++ b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound;
+use PHPStan\BetterReflection\Reflector\Reflector;
+use PHPStan\PhpDoc\StubFilesExtension;
+
+class PasswordAuthenticatedUserStubFilesExtension implements StubFilesExtension
+{
+
+	/** @var Reflector */
+	private $reflector;
+
+	public function __construct(
+		Reflector $reflector
+	)
+	{
+		$this->reflector = $reflector;
+	}
+
+	public function getFiles(): array
+	{
+		try {
+			$this->reflector->reflectClass('Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface');
+		} catch (IdentifierNotFound $e) {
+			return [];
+		}
+
+		return [
+			__DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub',
+			__DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub',
+		];
+	}
+
+}

From 1da7bf450c6b351fec08ca0aa97298473d4f6ab3 Mon Sep 17 00:00:00 2001
From: Yassine Guedidi <yassine@guedidi.com>
Date: Thu, 23 Mar 2023 14:44:33 +0100
Subject: [PATCH 222/297] Add stubs for security factories

---
 extension.neon                                   |  2 ++
 .../Factory/AuthenticatorFactoryInterface.stub   | 16 ++++++++++++++++
 .../FirewallListenerFactoryInterface.stub        | 15 +++++++++++++++
 3 files changed, 33 insertions(+)
 create mode 100644 stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub
 create mode 100644 stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub

diff --git a/extension.neon b/extension.neon
index 1a8cf313..1dfcca46 100644
--- a/extension.neon
+++ b/extension.neon
@@ -23,6 +23,8 @@ parameters:
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
+		- stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub
+		- stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub
 		- stubs/Symfony/Component/Console/Command.stub
 		- stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub
 		- stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub
diff --git a/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub
new file mode 100644
index 00000000..8ca4b331
--- /dev/null
+++ b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
+
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+interface AuthenticatorFactoryInterface
+{
+    /**
+     * @param array<string, mixed> $config
+     *
+     * @return string|string[]
+     */
+    public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId);
+}
diff --git a/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub
new file mode 100644
index 00000000..5c6b54fd
--- /dev/null
+++ b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub
@@ -0,0 +1,15 @@
+<?php
+
+namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+interface FirewallListenerFactoryInterface
+{
+    /**
+     * @param array<string, mixed> $config
+     *
+     * @return string[]
+     */
+    public function createListeners(ContainerBuilder $container, string $firewallName, array $config): array;
+}

From f45b9ceccfe81b6cd8229a8f9ad7060a8ee5cb30 Mon Sep 17 00:00:00 2001
From: Natsuki Ikeguchi <me@s6n.jp>
Date: Fri, 14 Apr 2023 21:17:46 +0900
Subject: [PATCH 223/297] Support specifying type of data_class in forms

---
 extension.neon                                |  7 ++
 .../Symfony/Component/Form/AbstractType.stub  | 12 ++++
 .../Component/Form/FormFactoryInterface.stub  | 36 ++++++++++
 .../Symfony/Component/Form/FormInterface.stub | 16 ++++-
 .../Form/FormTypeExtensionInterface.stub      |  5 ++
 .../Component/Form/FormTypeInterface.stub     |  5 ++
 .../Exception/InvalidOptionsException.stub    |  7 ++
 tests/Type/Symfony/ExtensionTest.php          |  1 +
 tests/Type/Symfony/data/form_data_type.php    | 72 +++++++++++++++++++
 9 files changed, 159 insertions(+), 2 deletions(-)
 create mode 100644 stubs/Symfony/Component/Form/AbstractType.stub
 create mode 100644 stubs/Symfony/Component/Form/FormFactoryInterface.stub
 create mode 100644 stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub
 create mode 100644 tests/Type/Symfony/data/form_data_type.php

diff --git a/extension.neon b/extension.neon
index 1dfcca46..40165b39 100644
--- a/extension.neon
+++ b/extension.neon
@@ -13,6 +13,10 @@ parameters:
 		consoleApplicationLoader: null
 	featureToggles:
 		skipCheckGenericClasses:
+			- Symfony\Component\Form\AbstractType
+			- Symfony\Component\Form\FormInterface
+			- Symfony\Component\Form\FormTypeExtensionInterface
+			- Symfony\Component\Form\FormTypeInterface
 			- Symfony\Component\OptionsResolver\Options
 			- Symfony\Component\Security\Core\Authorization\Voter\Voter
 			- Symfony\Component\Security\Core\User\PasswordUpgraderInterface
@@ -36,6 +40,7 @@ parameters:
 		- stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
 		- stubs/Symfony/Component/EventDispatcher/GenericEvent.stub
+		- stubs/Symfony/Component/Form/AbstractType.stub
 		- stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub
 		- stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub
 		- stubs/Symfony/Component/Form/Exception/RuntimeException.stub
@@ -43,6 +48,7 @@ parameters:
 		- stubs/Symfony/Component/Form/DataTransformerInterface.stub
 		- stubs/Symfony/Component/Form/FormBuilderInterface.stub
 		- stubs/Symfony/Component/Form/FormInterface.stub
+		- stubs/Symfony/Component/Form/FormFactoryInterface.stub
 		- stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
 		- stubs/Symfony/Component/Form/FormTypeInterface.stub
 		- stubs/Symfony/Component/Form/FormView.stub
@@ -52,6 +58,7 @@ parameters:
 		- stubs/Symfony/Component/HttpFoundation/Session.stub
 		- stubs/Symfony/Component/Messenger/StampInterface.stub
 		- stubs/Symfony/Component/Messenger/Envelope.stub
+		- stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub
 		- stubs/Symfony/Component/OptionsResolver/Options.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
diff --git a/stubs/Symfony/Component/Form/AbstractType.stub b/stubs/Symfony/Component/Form/AbstractType.stub
new file mode 100644
index 00000000..76170a02
--- /dev/null
+++ b/stubs/Symfony/Component/Form/AbstractType.stub
@@ -0,0 +1,12 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+/**
+ * @template TData
+ *
+ * @implements FormTypeInterface<TData>
+ */
+abstract class AbstractType implements FormTypeInterface
+{
+}
diff --git a/stubs/Symfony/Component/Form/FormFactoryInterface.stub b/stubs/Symfony/Component/Form/FormFactoryInterface.stub
new file mode 100644
index 00000000..a2221ec0
--- /dev/null
+++ b/stubs/Symfony/Component/Form/FormFactoryInterface.stub
@@ -0,0 +1,36 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\Extension\Core\Type\FormType;
+
+interface FormFactoryInterface
+{
+	/**
+	 * @template TFormType of FormTypeInterface<TData>
+	 * @template TData
+	 *
+	 * @param class-string<TFormType> $type
+	 * @param TData                   $data
+	 * @param array<string, mixed>    $options
+	 *
+	 * @phpstan-return ($data is null ? FormInterface<null|TData> : FormInterface<TData>)
+	 *
+	 * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+	 */
+	public function create(string $type = FormType::class, $data = null, array $options = []): FormInterface;
+
+	/**
+	 * @template TFormType of FormTypeInterface<TData>
+	 * @template TData
+	 *
+	  * @param class-string<TFormType> $type
+	 * @param TData                   $data
+	 * @param array<string, mixed>    $options
+	 *
+	 * @phpstan-return ($data is null ? FormInterface<null|TData> : FormInterface<TData>)
+	 *
+	 * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
+	 */
+	public function createNamed(string $name, string $type = FormType::class, $data = null, array $options = []): FormInterface;
+}
diff --git a/stubs/Symfony/Component/Form/FormInterface.stub b/stubs/Symfony/Component/Form/FormInterface.stub
index 6960cce5..4bd21229 100644
--- a/stubs/Symfony/Component/Form/FormInterface.stub
+++ b/stubs/Symfony/Component/Form/FormInterface.stub
@@ -3,10 +3,22 @@
 namespace Symfony\Component\Form;
 
 /**
- * @extends \ArrayAccess<string, \Symfony\Component\Form\FormInterface>
- * @extends \Traversable<string, \Symfony\Component\Form\FormInterface>
+ * @template TData
+ *
+ * @extends \ArrayAccess<string, \Symfony\Component\Form\FormInterface<mixed>>
+ * @extends \Traversable<string, \Symfony\Component\Form\FormInterface<mixed>>
  */
 interface FormInterface extends \ArrayAccess, \Traversable, \Countable
 {
+    /**
+     * @param TData $modelData
+     *
+     * @return $this
+     */
+    public function setData($modelData): FormInterface;
 
+    /**
+     * @return TData
+     */
+    public function getData();
 }
diff --git a/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub b/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
index 25b6f25d..234d1b2e 100644
--- a/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
+++ b/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
@@ -2,6 +2,9 @@
 
 namespace Symfony\Component\Form;
 
+/**
+ * @template TData
+ */
 interface FormTypeExtensionInterface
 {
     /**
@@ -10,11 +13,13 @@ interface FormTypeExtensionInterface
     public function buildForm(FormBuilderInterface $builder, array $options): void;
 
     /**
+     * @phpstan-param FormInterface<TData> $form
      * @param array<string, mixed> $options
      */
     public function buildView(FormView $view, FormInterface $form, array $options): void;
 
     /**
+     * @phpstan-param FormInterface<TData> $form
      * @param array<string, mixed> $options
      */
     public function finishView(FormView $view, FormInterface $form, array $options): void;
diff --git a/stubs/Symfony/Component/Form/FormTypeInterface.stub b/stubs/Symfony/Component/Form/FormTypeInterface.stub
index cebbc1c2..85bb539c 100644
--- a/stubs/Symfony/Component/Form/FormTypeInterface.stub
+++ b/stubs/Symfony/Component/Form/FormTypeInterface.stub
@@ -2,6 +2,9 @@
 
 namespace Symfony\Component\Form;
 
+/**
+ * @template TData
+ */
 interface FormTypeInterface
 {
     /**
@@ -10,11 +13,13 @@ interface FormTypeInterface
     public function buildForm(FormBuilderInterface $builder, array $options): void;
 
     /**
+     * @phpstan-param FormInterface<TData> $form
      * @param array<string, mixed> $options
      */
     public function buildView(FormView $view, FormInterface $form, array $options): void;
 
     /**
+     * @phpstan-param FormInterface<TData> $form
      * @param array<string, mixed> $options
      */
     public function finishView(FormView $view, FormInterface $form, array $options): void;
diff --git a/stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub b/stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub
new file mode 100644
index 00000000..2856e32b
--- /dev/null
+++ b/stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+class InvalidOptionsException extends \InvalidArgumentException
+{
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 8dff057e..879abb5d 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -56,6 +56,7 @@ public function dataFileAsserts(): iterable
 
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/cache.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/form_data_type.php');
 	}
 
 	/**
diff --git a/tests/Type/Symfony/data/form_data_type.php b/tests/Type/Symfony/data/form_data_type.php
new file mode 100644
index 00000000..524a5b7c
--- /dev/null
+++ b/tests/Type/Symfony/data/form_data_type.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+
+namespace GenericFormDataType;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\NumberType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormFactoryInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use function PHPStan\Testing\assertType;
+
+class DataClass
+{
+
+	/** @var int */
+	public $foo;
+
+	/** @var string */
+	public $bar;
+
+}
+
+/**
+ * @extends AbstractType<DataClass>
+ */
+class DataClassType extends AbstractType
+{
+
+	public function buildForm(FormBuilderInterface $builder, array $options): void
+	{
+		$builder
+			->add('foo', NumberType::class)
+			->add('bar', TextType::class)
+		;
+	}
+
+	public function configureOptions(OptionsResolver $resolver): void
+	{
+		$resolver
+			->setDefaults([
+				'data_class' => DataClass::class,
+			])
+		;
+	}
+
+}
+
+class FormFactoryAwareClass
+{
+
+	/** @var FormFactoryInterface */
+	private $formFactory;
+
+	public function __construct(FormFactoryInterface $formFactory)
+	{
+		$this->formFactory = $formFactory;
+	}
+
+	public function doSomething(): void
+	{
+		$form = $this->formFactory->create(DataClassType::class, new DataClass());
+		assertType('GenericFormDataType\DataClass', $form->getData());
+	}
+
+	public function doSomethingNullable(): void
+	{
+		$form = $this->formFactory->create(DataClassType::class);
+		assertType('GenericFormDataType\DataClass|null', $form->getData());
+	}
+
+}

From 7e78605a699d183f5a6936cf91904f4c16ca79b2 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 14 Apr 2023 18:58:33 +0200
Subject: [PATCH 224/297] Fix AbstractType stub

---
 stubs/Symfony/Component/Form/AbstractType.stub | 18 ++++++++++++++++++
 .../Component/Form/FormTypeInterface.stub      |  4 ++--
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/stubs/Symfony/Component/Form/AbstractType.stub b/stubs/Symfony/Component/Form/AbstractType.stub
index 76170a02..da2b1439 100644
--- a/stubs/Symfony/Component/Form/AbstractType.stub
+++ b/stubs/Symfony/Component/Form/AbstractType.stub
@@ -9,4 +9,22 @@ namespace Symfony\Component\Form;
  */
 abstract class AbstractType implements FormTypeInterface
 {
+
+	/**
+	 * @param array<string, mixed> $options
+	 */
+	public function buildForm(FormBuilderInterface $builder, array $options): void;
+
+	/**
+	 * @param FormInterface<TData> $form
+	 * @param array<string, mixed> $options
+	 */
+	public function buildView(FormView $view, FormInterface $form, array $options): void;
+
+	/**
+	 * @param FormInterface<TData> $form
+	 * @param array<string, mixed> $options
+	 */
+	public function finishView(FormView $view, FormInterface $form, array $options): void;
+
 }
diff --git a/stubs/Symfony/Component/Form/FormTypeInterface.stub b/stubs/Symfony/Component/Form/FormTypeInterface.stub
index 85bb539c..2f745283 100644
--- a/stubs/Symfony/Component/Form/FormTypeInterface.stub
+++ b/stubs/Symfony/Component/Form/FormTypeInterface.stub
@@ -13,13 +13,13 @@ interface FormTypeInterface
     public function buildForm(FormBuilderInterface $builder, array $options): void;
 
     /**
-     * @phpstan-param FormInterface<TData> $form
+     * @param FormInterface<TData> $form
      * @param array<string, mixed> $options
      */
     public function buildView(FormView $view, FormInterface $form, array $options): void;
 
     /**
-     * @phpstan-param FormInterface<TData> $form
+     * @param FormInterface<TData> $form
      * @param array<string, mixed> $options
      */
     public function finishView(FormView $view, FormInterface $form, array $options): void;

From db75c81816167e797b8d74b545470d5244c8a7d4 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 17 Apr 2023 01:41:01 +0000
Subject: [PATCH 225/297] Update build-cs

---
 build-cs/composer.lock | 50 +++++++++++++++++++++---------------------
 1 file changed, 25 insertions(+), 25 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index c25a151a..2eafc159 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -9,16 +9,16 @@
     "packages-dev": [
         {
             "name": "consistence-community/coding-standard",
-            "version": "3.11.2",
+            "version": "3.11.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/consistence-community/coding-standard.git",
-                "reference": "adb4be482e76990552bf624309d2acc8754ba1bd"
+                "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/adb4be482e76990552bf624309d2acc8754ba1bd",
-                "reference": "adb4be482e76990552bf624309d2acc8754ba1bd",
+                "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1",
+                "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1",
                 "shasum": ""
             },
             "require": {
@@ -70,9 +70,9 @@
             ],
             "support": {
                 "issues": "https://github.com/consistence-community/coding-standard/issues",
-                "source": "https://github.com/consistence-community/coding-standard/tree/3.11.2"
+                "source": "https://github.com/consistence-community/coding-standard/tree/3.11.3"
             },
-            "time": "2022-06-21T08:36:36+00:00"
+            "time": "2023-03-27T14:55:41+00:00"
         },
         {
             "name": "dealerdirect/phpcodesniffer-composer-installer",
@@ -154,16 +154,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.15.3",
+            "version": "1.18.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "61800f71a5526081d1b5633766aa88341f1ade76"
+                "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/61800f71a5526081d1b5633766aa88341f1ade76",
-                "reference": "61800f71a5526081d1b5633766aa88341f1ade76",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
+                "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
                 "shasum": ""
             },
             "require": {
@@ -193,38 +193,38 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.3"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1"
             },
-            "time": "2022-12-20T20:56:55+00:00"
+            "time": "2023-04-07T11:51:11+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "8.8.0",
+            "version": "8.10.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89"
+                "reference": "c4e213e6e57f741451a08e68ef838802eec92287"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/59e25146a4ef0a7b194c5bc55b32dd414345db89",
-                "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/c4e213e6e57f741451a08e68ef838802eec92287",
+                "reference": "c4e213e6e57f741451a08e68ef838802eec92287",
                 "shasum": ""
             },
             "require": {
                 "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
                 "php": "^7.2 || ^8.0",
-                "phpstan/phpdoc-parser": ">=1.15.2 <1.16.0",
+                "phpstan/phpdoc-parser": ">=1.18.0 <1.19.0",
                 "squizlabs/php_codesniffer": "^3.7.1"
             },
             "require-dev": {
                 "phing/phing": "2.17.4",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.10|1.9.6",
-                "phpstan/phpstan-deprecation-rules": "1.1.1",
-                "phpstan/phpstan-phpunit": "1.0.0|1.3.3",
-                "phpstan/phpstan-strict-rules": "1.4.4",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.5.27"
+                "phpstan/phpstan": "1.4.10|1.10.11",
+                "phpstan/phpstan-deprecation-rules": "1.1.3",
+                "phpstan/phpstan-phpunit": "1.0.0|1.3.11",
+                "phpstan/phpstan-strict-rules": "1.5.1",
+                "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.0.19"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
@@ -234,7 +234,7 @@
             },
             "autoload": {
                 "psr-4": {
-                    "SlevomatCodingStandard\\": "SlevomatCodingStandard"
+                    "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -248,7 +248,7 @@
             ],
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/8.8.0"
+                "source": "https://github.com/slevomat/coding-standard/tree/8.10.0"
             },
             "funding": [
                 {
@@ -260,7 +260,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-09T10:46:13+00:00"
+            "time": "2023-04-10T07:39:29+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From ddaa09397220a43bdfc9e6c0b30c6ece3e0d9a29 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 24 Apr 2023 01:28:58 +0000
Subject: [PATCH 226/297] Update dependency slevomat/coding-standard to v8.11.0

---
 build-cs/composer.lock | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 2eafc159..0cea3f9e 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -154,16 +154,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.18.1",
+            "version": "1.20.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f"
+                "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
-                "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/90490bd8fd8530a272043c4950c180b6d0cf5f81",
+                "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81",
                 "shasum": ""
             },
             "require": {
@@ -193,38 +193,38 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.2"
             },
-            "time": "2023-04-07T11:51:11+00:00"
+            "time": "2023-04-22T12:59:35+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "8.10.0",
+            "version": "8.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "c4e213e6e57f741451a08e68ef838802eec92287"
+                "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/c4e213e6e57f741451a08e68ef838802eec92287",
-                "reference": "c4e213e6e57f741451a08e68ef838802eec92287",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/91428d5bcf7db93a842bcf97f465edf62527f3ea",
+                "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea",
                 "shasum": ""
             },
             "require": {
                 "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
                 "php": "^7.2 || ^8.0",
-                "phpstan/phpdoc-parser": ">=1.18.0 <1.19.0",
+                "phpstan/phpdoc-parser": ">=1.20.0 <1.21.0",
                 "squizlabs/php_codesniffer": "^3.7.1"
             },
             "require-dev": {
                 "phing/phing": "2.17.4",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.10|1.10.11",
+                "phpstan/phpstan": "1.10.14",
                 "phpstan/phpstan-deprecation-rules": "1.1.3",
-                "phpstan/phpstan-phpunit": "1.0.0|1.3.11",
+                "phpstan/phpstan-phpunit": "1.3.11",
                 "phpstan/phpstan-strict-rules": "1.5.1",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.0.19"
+                "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.1.1"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
@@ -248,7 +248,7 @@
             ],
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/8.10.0"
+                "source": "https://github.com/slevomat/coding-standard/tree/8.11.0"
             },
             "funding": [
                 {
@@ -260,7 +260,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-04-10T07:39:29+00:00"
+            "time": "2023-04-21T15:51:44+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From bc67cd94f6c39c07b3427fe06aa7c237d06a704e Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 1 May 2023 02:05:48 +0000
Subject: [PATCH 227/297] Update dependency slevomat/coding-standard to v8.11.1

---
 build-cs/composer.lock | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index 0cea3f9e..c59ffc5f 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -154,16 +154,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.20.2",
+            "version": "1.20.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81"
+                "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/90490bd8fd8530a272043c4950c180b6d0cf5f81",
-                "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2",
+                "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2",
                 "shasum": ""
             },
             "require": {
@@ -193,22 +193,22 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.2"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3"
             },
-            "time": "2023-04-22T12:59:35+00:00"
+            "time": "2023-04-25T09:01:03+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "8.11.0",
+            "version": "8.11.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea"
+                "reference": "af87461316b257e46e15bb041dca6fca3796d822"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/91428d5bcf7db93a842bcf97f465edf62527f3ea",
-                "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/af87461316b257e46e15bb041dca6fca3796d822",
+                "reference": "af87461316b257e46e15bb041dca6fca3796d822",
                 "shasum": ""
             },
             "require": {
@@ -248,7 +248,7 @@
             ],
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/8.11.0"
+                "source": "https://github.com/slevomat/coding-standard/tree/8.11.1"
             },
             "funding": [
                 {
@@ -260,7 +260,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-04-21T15:51:44+00:00"
+            "time": "2023-04-24T08:19:01+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From c8493938b6ea628d9e68ec231c36ad1b13587649 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 15 May 2023 00:51:42 +0000
Subject: [PATCH 228/297] Update dependency slevomat/coding-standard to v8.12.0

---
 build-cs/composer.lock | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/build-cs/composer.lock b/build-cs/composer.lock
index c59ffc5f..db122416 100644
--- a/build-cs/composer.lock
+++ b/build-cs/composer.lock
@@ -154,16 +154,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.20.3",
+            "version": "1.20.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2"
+                "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2",
-                "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
+                "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
                 "shasum": ""
             },
             "require": {
@@ -193,22 +193,22 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4"
             },
-            "time": "2023-04-25T09:01:03+00:00"
+            "time": "2023-05-02T09:19:37+00:00"
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "8.11.1",
+            "version": "8.12.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "af87461316b257e46e15bb041dca6fca3796d822"
+                "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/af87461316b257e46e15bb041dca6fca3796d822",
-                "reference": "af87461316b257e46e15bb041dca6fca3796d822",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cc04334ed0ce5a251389112fbd2dbe1dbc931ae8",
+                "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8",
                 "shasum": ""
             },
             "require": {
@@ -220,11 +220,11 @@
             "require-dev": {
                 "phing/phing": "2.17.4",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.10.14",
+                "phpstan/phpstan": "1.10.15",
                 "phpstan/phpstan-deprecation-rules": "1.1.3",
                 "phpstan/phpstan-phpunit": "1.3.11",
                 "phpstan/phpstan-strict-rules": "1.5.1",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.1.1"
+                "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.1.3"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
@@ -248,7 +248,7 @@
             ],
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/8.11.1"
+                "source": "https://github.com/slevomat/coding-standard/tree/8.12.0"
             },
             "funding": [
                 {
@@ -260,7 +260,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-04-24T08:19:01+00:00"
+            "time": "2023-05-14T20:06:01+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

From 7332b90dfc291ac5b4b83fbca2081936faa1e3f9 Mon Sep 17 00:00:00 2001
From: Nico Haase <nico.haase@interlutions.de>
Date: Fri, 12 May 2023 16:51:50 +0200
Subject: [PATCH 229/297] Extend Serializer stubs

---
 .../Component/Serializer/Encoder/DecoderInterface.stub        | 3 ++-
 .../Component/Serializer/Encoder/EncoderInterface.stub        | 3 ++-
 .../Serializer/Normalizer/DenormalizerInterface.stub          | 3 ++-
 .../Component/Serializer/Normalizer/NormalizerInterface.stub  | 4 +++-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
index a9923edf..e19f772e 100644
--- a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub
@@ -18,7 +18,8 @@ interface DecoderInterface
 
     /**
      * @param string $format Format name
+     * @param array<mixed> $context
      * @return bool
      */
-    public function supportsDecoding($format);
+    public function supportsDecoding($format, array $context = []);
 }
diff --git a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
index 2d97bde3..11e374eb 100644
--- a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub
@@ -18,7 +18,8 @@ interface EncoderInterface
 
     /**
      * @param string $format Format name
+     * @param array<mixed> $context
      * @return bool
      */
-    public function supportsEncoding($format);
+    public function supportsEncoding($format, array $context = []);
 }
diff --git a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
index cb9af06d..b7e9968b 100644
--- a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub
@@ -33,7 +33,8 @@ interface DenormalizerInterface
      * @param mixed $data
      * @param string $type
      * @param string|null $format
+     * @param array<mixed> $context
      * @return bool
      */
-    public function supportsDenormalization($data, $type, $format = null);
+    public function supportsDenormalization($data, $type, $format = null, array $context = []);
 }
diff --git a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
index fac60042..ba86b6b6 100644
--- a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
+++ b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
@@ -27,7 +27,9 @@ interface NormalizerInterface
     /**
      * @param mixed $data
      * @param string|null $format
+     * @param array<mixed> $context
+     *
      * @return bool
      */
-    public function supportsNormalization($data, $format = null);
+    public function supportsNormalization($data, $format = null, array $context = []);
 }

From 8f1dab28569c92d8d3f73d5d5947d3851655759e Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 25 May 2023 15:27:48 +0200
Subject: [PATCH 230/297] Next-gen coding standard workflow

---
 .gitattributes              |   1 -
 .github/renovate.json       |   5 -
 .github/workflows/build.yml |  14 +-
 .gitignore                  |   1 +
 Makefile                    |  10 +-
 build-cs/.gitignore         |   1 -
 build-cs/composer.json      |  13 --
 build-cs/composer.lock      | 331 ------------------------------------
 phpcs.xml                   | 112 ------------
 9 files changed, 21 insertions(+), 467 deletions(-)
 delete mode 100644 build-cs/.gitignore
 delete mode 100644 build-cs/composer.json
 delete mode 100644 build-cs/composer.lock
 delete mode 100644 phpcs.xml

diff --git a/.gitattributes b/.gitattributes
index 184bf6ae..1545ee73 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -7,7 +7,6 @@ tmp export-ignore
 .gitignore export-ignore
 .travis.yml export-ignore
 Makefile export-ignore
-phpcs.xml export-ignore
 phpstan.neon export-ignore
 phpstan-baseline.neon export-ignore
 phpunit.xml export-ignore
diff --git a/.github/renovate.json b/.github/renovate.json
index b775cc18..d3f5961e 100644
--- a/.github/renovate.json
+++ b/.github/renovate.json
@@ -10,11 +10,6 @@
       "enabled": true,
       "groupName": "root-composer"
     },
-    {
-      "matchPaths": ["build-cs/**"],
-      "enabled": true,
-      "groupName": "build-cs"
-    },
     {
       "matchPaths": [".github/**"],
       "enabled": true,
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 96765433..ba129bc9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -42,7 +42,7 @@ jobs:
       - name: "Lint"
         run: "make lint"
 
-  coding-standards:
+  coding-standard:
     name: "Coding Standard"
 
     runs-on: "ubuntu-latest"
@@ -51,11 +51,17 @@ jobs:
       - name: "Checkout"
         uses: actions/checkout@v3
 
+      - name: "Checkout build-cs"
+        uses: actions/checkout@v3
+        with:
+          repository: "phpstan/build-cs"
+          path: "build-cs"
+
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
         with:
           coverage: "none"
-          php-version: "8.0"
+          php-version: "8.2"
 
       - name: "Validate Composer"
         run: "composer validate"
@@ -63,6 +69,10 @@ jobs:
       - name: "Install dependencies"
         run: "composer install --no-interaction --no-progress"
 
+      - name: "Install build-cs dependencies"
+        working-directory: "build-cs"
+        run: "composer install --no-interaction --no-progress"
+
       - name: "Lint"
         run: "make lint"
 
diff --git a/.gitignore b/.gitignore
index 2db21315..7de9f3c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 /tests/tmp
+/build-cs
 /vendor
 /composer.lock
 .phpunit.result.cache
diff --git a/Makefile b/Makefile
index b34d0fec..ecd8cfb2 100644
--- a/Makefile
+++ b/Makefile
@@ -10,13 +10,19 @@ lint:
 	php vendor/bin/parallel-lint --colors \
 		src tests
 
+.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/main
+	composer install --working-dir build-cs
+
 .PHONY: cs
 cs:
-	composer install --working-dir build-cs && php build-cs/vendor/bin/phpcs
+	php build-cs/vendor/bin/phpcs --standard=build-cs/phpcs.xml src tests
 
 .PHONY: cs-fix
 cs-fix:
-	php build-cs/vendor/bin/phpcbf
+	php build-cs/vendor/bin/phpcbf --standard=build-cs/phpcs.xml src tests
 
 .PHONY: phpstan
 phpstan:
diff --git a/build-cs/.gitignore b/build-cs/.gitignore
deleted file mode 100644
index 61ead866..00000000
--- a/build-cs/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/vendor
diff --git a/build-cs/composer.json b/build-cs/composer.json
deleted file mode 100644
index 16a240bc..00000000
--- a/build-cs/composer.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-	"require-dev": {
-		"consistence-community/coding-standard": "^3.11.0",
-		"dealerdirect/phpcodesniffer-composer-installer": "^1.0.0",
-		"slevomat/coding-standard": "^8.8.0",
-		"squizlabs/php_codesniffer": "^3.5.3"
-	},
-	"config": {
-		"allow-plugins": {
-			"dealerdirect/phpcodesniffer-composer-installer": true
-		}
-	}
-}
diff --git a/build-cs/composer.lock b/build-cs/composer.lock
deleted file mode 100644
index db122416..00000000
--- a/build-cs/composer.lock
+++ /dev/null
@@ -1,331 +0,0 @@
-{
-    "_readme": [
-        "This file locks the dependencies of your project to a known state",
-        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
-        "This file is @generated automatically"
-    ],
-    "content-hash": "e69c1916405a7e3c8001c1b609a0ee61",
-    "packages": [],
-    "packages-dev": [
-        {
-            "name": "consistence-community/coding-standard",
-            "version": "3.11.3",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/consistence-community/coding-standard.git",
-                "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1",
-                "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1",
-                "shasum": ""
-            },
-            "require": {
-                "php": "~8.0",
-                "slevomat/coding-standard": "~8.0",
-                "squizlabs/php_codesniffer": "~3.7.0"
-            },
-            "replace": {
-                "consistence/coding-standard": "3.10.*"
-            },
-            "require-dev": {
-                "phing/phing": "2.17.0",
-                "php-parallel-lint/php-parallel-lint": "1.3.1",
-                "phpunit/phpunit": "9.5.10"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "Consistence\\": [
-                        "Consistence"
-                    ]
-                },
-                "classmap": [
-                    "Consistence"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Vašek Purchart",
-                    "email": "me@vasekpurchart.cz",
-                    "homepage": "http://vasekpurchart.cz"
-                }
-            ],
-            "description": "Consistence - Coding Standard - PHP Code Sniffer rules",
-            "keywords": [
-                "Coding Standard",
-                "PHPCodeSniffer",
-                "codesniffer",
-                "coding",
-                "cs",
-                "phpcs",
-                "ruleset",
-                "sniffer",
-                "standard"
-            ],
-            "support": {
-                "issues": "https://github.com/consistence-community/coding-standard/issues",
-                "source": "https://github.com/consistence-community/coding-standard/tree/3.11.3"
-            },
-            "time": "2023-03-27T14:55:41+00:00"
-        },
-        {
-            "name": "dealerdirect/phpcodesniffer-composer-installer",
-            "version": "v1.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/PHPCSStandards/composer-installer.git",
-                "reference": "4be43904336affa5c2f70744a348312336afd0da"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
-                "reference": "4be43904336affa5c2f70744a348312336afd0da",
-                "shasum": ""
-            },
-            "require": {
-                "composer-plugin-api": "^1.0 || ^2.0",
-                "php": ">=5.4",
-                "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
-            },
-            "require-dev": {
-                "composer/composer": "*",
-                "ext-json": "*",
-                "ext-zip": "*",
-                "php-parallel-lint/php-parallel-lint": "^1.3.1",
-                "phpcompatibility/php-compatibility": "^9.0",
-                "yoast/phpunit-polyfills": "^1.0"
-            },
-            "type": "composer-plugin",
-            "extra": {
-                "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
-            },
-            "autoload": {
-                "psr-4": {
-                    "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Franck Nijhof",
-                    "email": "franck.nijhof@dealerdirect.com",
-                    "homepage": "http://www.frenck.nl",
-                    "role": "Developer / IT Manager"
-                },
-                {
-                    "name": "Contributors",
-                    "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
-                }
-            ],
-            "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
-            "homepage": "http://www.dealerdirect.com",
-            "keywords": [
-                "PHPCodeSniffer",
-                "PHP_CodeSniffer",
-                "code quality",
-                "codesniffer",
-                "composer",
-                "installer",
-                "phpcbf",
-                "phpcs",
-                "plugin",
-                "qa",
-                "quality",
-                "standard",
-                "standards",
-                "style guide",
-                "stylecheck",
-                "tests"
-            ],
-            "support": {
-                "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
-                "source": "https://github.com/PHPCSStandards/composer-installer"
-            },
-            "time": "2023-01-05T11:28:13+00:00"
-        },
-        {
-            "name": "phpstan/phpdoc-parser",
-            "version": "1.20.4",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
-                "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.2 || ^8.0"
-            },
-            "require-dev": {
-                "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",
-                "symfony/process": "^5.2"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "PHPStan\\PhpDocParser\\": [
-                        "src/"
-                    ]
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "description": "PHPDoc parser with support for nullable, intersection and generic types",
-            "support": {
-                "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4"
-            },
-            "time": "2023-05-02T09:19:37+00:00"
-        },
-        {
-            "name": "slevomat/coding-standard",
-            "version": "8.12.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cc04334ed0ce5a251389112fbd2dbe1dbc931ae8",
-                "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8",
-                "shasum": ""
-            },
-            "require": {
-                "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
-                "php": "^7.2 || ^8.0",
-                "phpstan/phpdoc-parser": ">=1.20.0 <1.21.0",
-                "squizlabs/php_codesniffer": "^3.7.1"
-            },
-            "require-dev": {
-                "phing/phing": "2.17.4",
-                "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.10.15",
-                "phpstan/phpstan-deprecation-rules": "1.1.3",
-                "phpstan/phpstan-phpunit": "1.3.11",
-                "phpstan/phpstan-strict-rules": "1.5.1",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.1.3"
-            },
-            "type": "phpcodesniffer-standard",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "8.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
-            "keywords": [
-                "dev",
-                "phpcs"
-            ],
-            "support": {
-                "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/8.12.0"
-            },
-            "funding": [
-                {
-                    "url": "https://github.com/kukulich",
-                    "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
-                    "type": "tidelift"
-                }
-            ],
-            "time": "2023-05-14T20:06:01+00:00"
-        },
-        {
-            "name": "squizlabs/php_codesniffer",
-            "version": "3.7.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879",
-                "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879",
-                "shasum": ""
-            },
-            "require": {
-                "ext-simplexml": "*",
-                "ext-tokenizer": "*",
-                "ext-xmlwriter": "*",
-                "php": ">=5.4.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
-            },
-            "bin": [
-                "bin/phpcs",
-                "bin/phpcbf"
-            ],
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.x-dev"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Greg Sherwood",
-                    "role": "lead"
-                }
-            ],
-            "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
-            "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
-            "keywords": [
-                "phpcs",
-                "standards",
-                "static analysis"
-            ],
-            "support": {
-                "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
-                "source": "https://github.com/squizlabs/PHP_CodeSniffer",
-                "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
-            },
-            "time": "2023-02-22T23:07:41+00:00"
-        }
-    ],
-    "aliases": [],
-    "minimum-stability": "stable",
-    "stability-flags": [],
-    "prefer-stable": false,
-    "prefer-lowest": false,
-    "platform": [],
-    "platform-dev": [],
-    "plugin-api-version": "2.3.0"
-}
diff --git a/phpcs.xml b/phpcs.xml
deleted file mode 100644
index 3412c956..00000000
--- a/phpcs.xml
+++ /dev/null
@@ -1,112 +0,0 @@
-<?xml version="1.0"?>
-<ruleset name="PHPStan deprecation rules">
-	<config name="php_version" value="70100"/>
-	<arg name="colors"/>
-	<arg name="extensions" value="php"/>
-	<arg name="encoding" value="utf-8"/>
-	<arg name="tab-width" value="4"/>
-	<arg name="cache" value="tmp/cache/phpcs"/>
-	<arg value="sp"/>
-	<file>src</file>
-	<file>tests</file>
-
-	<rule ref="build-cs/vendor/consistence-community/coding-standard/Consistence/ruleset.xml">
-		<exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat"/>
-		<exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable"/>
-		<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation"/>
-		<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedExceptions"/>
-		<exclude name="Consistence.Exceptions.ExceptionDeclaration"/>
-		<exclude name="Squiz.Commenting.FunctionComment"/>
-		<exclude name="Squiz.PHP.Heredoc.NotAllowed"/>
-		<exclude name="Squiz.WhiteSpace.FunctionSpacing.Before"/>
-	</rule>
-	<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses">
-		<properties>
-			<property name="caseSensitive" value="false"/>
-			<property name="psr12Compatible" value="true"/>
-		</properties>
-	</rule>
-	<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
-		<properties>
-			<property name="declareOnFirstLine" value="true"/>
-		</properties>
-	</rule>
-	<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint">
-		<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
-		<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint"/>
-	</rule>
-	<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.UselessAnnotation">
-		<severity>10</severity>
-	</rule>
-	<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
-		<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
-	</rule>
-	<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint.UselessAnnotation">
-		<severity>10</severity>
-	</rule>
-	<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint">
-		<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
-		<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint"/>
-		<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint"/>
-	</rule>
-	<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.UselessAnnotation">
-		<severity>10</severity>
-	</rule>
-	<rule ref="SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure.UnusedInheritedVariable"/>
-	<rule ref="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException"/>
-	<rule ref="SlevomatCodingStandard.Variables.UnusedVariable.UnusedVariable"/>
-	<rule ref="SlevomatCodingStandard.ControlStructures.AssignmentInCondition"/>
-	<rule ref="SlevomatCodingStandard.ControlStructures.EarlyExit">
-		<exclude name="SlevomatCodingStandard.ControlStructures.EarlyExit.UselessElseIf"/>
-	</rule>
-	<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming"/>
-	<rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming"/>
-	<rule ref="SlevomatCodingStandard.Files.TypeNameMatchesFileName">
-		<properties>
-			<property name="rootNamespaces" type="array">
-				<element key="src" value="PHPStan"/>
-				<element key="tests" value="PHPStan"/>
-			</property>
-
-		</properties>
-	</rule>
-	<rule ref="SlevomatCodingStandard.Classes.ModernClassNameReference"/>
-	<rule ref="SlevomatCodingStandard.Functions.StaticClosure"/>
-	<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators"/>
-	<rule ref="SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator"/>
-	<rule ref="SlevomatCodingStandard.TypeHints.NullTypeHintOnLastPosition"/>
-	<rule ref="SlevomatCodingStandard.Classes.TraitUseDeclaration"/>
-	<rule ref="SlevomatCodingStandard.Classes.TraitUseSpacing"/>
-	<rule ref="SlevomatCodingStandard.Variables.UnusedVariable"/>
-	<rule ref="SlevomatCodingStandard.Variables.UselessVariable"/>
-	<!--<rule ref="SlevomatCodingStandard.Functions.UnusedParameter"/>-->
-	<rule ref="SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure"/>
-	<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly">
-		<properties>
-			<property name="searchAnnotations" value="true"/>
-			<property name="namespacesRequiredToUse" value=""/>
-			<property name="allowPartialUses" value="true"/>
-			<property name="allowFallbackGlobalFunctions" value="false"/>
-			<property name="allowFallbackGlobalConstants" value="false"/>
-			<property name="allowFullyQualifiedExceptions" value="false"/>
-			<property name="allowFullyQualifiedNameForCollidingClasses" value="true"/>
-			<property name="allowFullyQualifiedNameForCollidingFunctions" value="true"/>
-			<property name="allowFullyQualifiedNameForCollidingConstants" value="true"/>
-		</properties>
-	</rule>
-	<rule ref="SlevomatCodingStandard.Namespaces.UselessAlias"/>
-	<rule ref="SlevomatCodingStandard.Namespaces.UseSpacing"/>
-	<rule ref="SlevomatCodingStandard.PHP.UselessSemicolon"/>
-	<rule ref="SlevomatCodingStandard.PHP.UselessParentheses"/>
-	<rule ref="Squiz.WhiteSpace.FunctionSpacing">
-		<properties>
-			<property name="spacing" value="1" />
-			<property name="spacingBeforeFirst" value="1"/>
-			<property name="spacingAfterLast" value="1"/>
-		</properties>
-	</rule>
-	<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps"/>
-	<rule ref="Consistence.NamingConventions.ValidVariableName.NotCamelCaps"/>
-	<exclude-pattern>tests/*/data</exclude-pattern>
-	<exclude-pattern>tests/tmp</exclude-pattern>
-</ruleset>

From 7e7b5bd2e8473d92af0ccd8532d22e83c155da2d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 8 Jun 2023 10:18:51 +0200
Subject: [PATCH 231/297] Modernize rules with RuleErrorBuilder

---
 .../ContainerInterfacePrivateServiceRule.php  | 10 +++++-----
 .../ContainerInterfaceUnknownServiceRule.php  |  9 ++++-----
 .../InvalidArgumentDefaultValueRule.php       | 19 ++++++++++++------
 .../Symfony/InvalidOptionDefaultValueRule.php | 20 +++++++++++++------
 src/Rules/Symfony/UndefinedArgumentRule.php   |  7 ++-----
 src/Rules/Symfony/UndefinedOptionRule.php     |  7 ++-----
 6 files changed, 40 insertions(+), 32 deletions(-)

diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 0a3a3d3e..5e10c2b8 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -6,7 +6,7 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\Rules\RuleError;
+use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Symfony\ServiceMap;
 use PHPStan\TrinaryLogic;
 use PHPStan\Type\ObjectType;
@@ -32,9 +32,6 @@ public function getNodeType(): string
 		return MethodCall::class;
 	}
 
-	/**
-	 * @return (string|RuleError)[] errors
-	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
 		if (!$node->name instanceof Node\Identifier) {
@@ -72,7 +69,10 @@ public function processNode(Node $node, Scope $scope): array
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
 			if ($service !== null && !$service->isPublic()) {
-				return [sprintf('Service "%s" is private.', $serviceId)];
+				return [
+					RuleErrorBuilder::message(sprintf('Service "%s" is private.', $serviceId))
+						->build(),
+				];
 			}
 		}
 
diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index 417f51f5..ccc1999e 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -7,7 +7,7 @@
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\Rules\RuleError;
+use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Symfony\ServiceMap;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
@@ -36,9 +36,6 @@ public function getNodeType(): string
 		return MethodCall::class;
 	}
 
-	/**
-	 * @return (string|RuleError)[] errors
-	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
 		if (!$node->name instanceof Node\Identifier) {
@@ -73,7 +70,9 @@ public function processNode(Node $node, Scope $scope): array
 			$service = $this->serviceMap->getService($serviceId);
 			$serviceIdType = $scope->getType($node->getArgs()[0]->value);
 			if ($service === null && !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType)) {
-				return [sprintf('Service "%s" is not registered in the container.', $serviceId)];
+				return [
+					RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId))->build(),
+				];
 			}
 		}
 
diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index e48f8718..a55881ce 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -6,7 +6,7 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\Rules\RuleError;
+use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\Constant\ConstantIntegerType;
 use PHPStan\Type\IntegerType;
@@ -30,9 +30,6 @@ public function getNodeType(): string
 		return MethodCall::class;
 	}
 
-	/**
-	 * @return (string|RuleError)[] errors
-	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
 		if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) {
@@ -62,12 +59,22 @@ public function processNode(Node $node, Scope $scope): array
 
 		// not an array
 		if (($mode & 4) !== 4 && !(new UnionType([new StringType(), new NullType()]))->isSuperTypeOf($defaultType)->yes()) {
-			return [sprintf('Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()))];
+			return [
+				RuleErrorBuilder::message(sprintf(
+					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.',
+					$defaultType->describe(VerbosityLevel::typeOnly())
+				))->build(),
+			];
 		}
 
 		// is array
 		if (($mode & 4) === 4 && !(new UnionType([new ArrayType(new IntegerType(), new StringType()), new NullType()]))->isSuperTypeOf($defaultType)->yes()) {
-			return [sprintf('Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array<int, string>|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()))];
+			return [
+				RuleErrorBuilder::message(sprintf(
+					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array<int, string>|null, %s given.',
+					$defaultType->describe(VerbosityLevel::typeOnly())
+				))->build(),
+			];
 		}
 
 		return [];
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index 171f94f1..6964d25e 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -6,7 +6,7 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\Rules\RuleError;
+use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantIntegerType;
@@ -32,9 +32,6 @@ public function getNodeType(): string
 		return MethodCall::class;
 	}
 
-	/**
-	 * @return (string|RuleError)[] errors
-	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
 		if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) {
@@ -66,13 +63,24 @@ public function processNode(Node $node, Scope $scope): array
 		if (($mode & 8) !== 8) {
 			$checkType = new UnionType([new StringType(), new IntegerType(), new NullType(), new BooleanType()]);
 			if (!$checkType->isSuperTypeOf($defaultType)->yes()) {
-				return [sprintf('Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.', $checkType->describe(VerbosityLevel::typeOnly()), $defaultType->describe(VerbosityLevel::typeOnly()))];
+				return [
+					RuleErrorBuilder::message(sprintf(
+						'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.',
+						$checkType->describe(VerbosityLevel::typeOnly()),
+						$defaultType->describe(VerbosityLevel::typeOnly())
+					))->build(),
+				];
 			}
 		}
 
 		// is array
 		if (($mode & 8) === 8 && !(new UnionType([new ArrayType(new MixedType(), new StringType()), new NullType()]))->isSuperTypeOf($defaultType)->yes()) {
-			return [sprintf('Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array<string>|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()))];
+			return [
+				RuleErrorBuilder::message(sprintf(
+					'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array<string>|null, %s given.',
+					$defaultType->describe(VerbosityLevel::typeOnly())
+				))->build(),
+			];
 		}
 
 		return [];
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index 52f90241..07f35e2a 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -8,7 +8,7 @@
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\Rules\RuleError;
+use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
@@ -39,9 +39,6 @@ public function getNodeType(): string
 		return MethodCall::class;
 	}
 
-	/**
-	 * @return (string|RuleError)[] errors
-	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
 		$classReflection = $scope->getClassReflection();
@@ -78,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array
 				if ($scope->getType(Helper::createMarkerNode($node->var, $argType, $this->printer))->equals($argType)) {
 					continue;
 				}
-				$errors[] = sprintf('Command "%s" does not define argument "%s".', $name, $argName);
+				$errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define argument "%s".', $name, $argName))->build();
 			}
 		}
 
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index 00dd750e..5a8dc8e3 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -8,7 +8,7 @@
 use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Rules\Rule;
-use PHPStan\Rules\RuleError;
+use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
@@ -39,9 +39,6 @@ public function getNodeType(): string
 		return MethodCall::class;
 	}
 
-	/**
-	 * @return (string|RuleError)[] errors
-	 */
 	public function processNode(Node $node, Scope $scope): array
 	{
 		$classReflection = $scope->getClassReflection();
@@ -78,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array
 				if ($scope->getType(Helper::createMarkerNode($node->var, $optType, $this->printer))->equals($optType)) {
 					continue;
 				}
-				$errors[] = sprintf('Command "%s" does not define option "%s".', $name, $optName);
+				$errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define option "%s".', $name, $optName))->build();
 			}
 		}
 

From 14e46f20862e3772931aace725d06eb29612ea1a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 8 Jun 2023 11:12:16 +0200
Subject: [PATCH 232/297] Bump PHPStan dependencies

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 22501953..ca18861b 100644
--- a/composer.json
+++ b/composer.json
@@ -23,8 +23,8 @@
   "require-dev": {
     "nikic/php-parser": "^4.13.0",
     "php-parallel-lint/php-parallel-lint": "^1.2",
-    "phpstan/phpstan-phpunit": "^1.0",
-    "phpstan/phpstan-strict-rules": "^1.0",
+    "phpstan/phpstan-phpunit": "^1.3.11",
+    "phpstan/phpstan-strict-rules": "^1.5.1",
     "phpunit/phpunit": "^8.5.29 || ^9.5",
     "psr/container": "1.0 || 1.1.1",
     "symfony/config": "^5.4 || ^6.1",

From 3bc0b957979e7447a907bd190236fcbd90fcb95d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ga=C3=ABtan=20Verlhac?= <viviengaetan69@gmail.com>
Date: Wed, 7 Jun 2023 12:47:04 +0200
Subject: [PATCH 233/297] feat: Sync Validor stubs with SF6.1

---
 .../Component/Validator/ConstraintViolationInterface.stub    | 3 +++
 .../Validator/ConstraintViolationListInterface.stub          | 5 ++++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
index ef3312bb..e7f2b8a2 100644
--- a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
+++ b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
@@ -2,6 +2,9 @@
 
 namespace Symfony\Component\Validator;
 
+/**
+ * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1.
+ */
 interface ConstraintViolationInterface
 {
 }
diff --git a/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
index 423bdfb0..00a2a9d5 100644
--- a/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
+++ b/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
@@ -3,8 +3,11 @@
 namespace Symfony\Component\Validator;
 
 /**
+ * @extends \ArrayAccess<int, ConstraintViolationInterface>
  * @extends \Traversable<int, ConstraintViolationInterface>
+ *
+ * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1.
  */
-interface ConstraintViolationListInterface extends \Traversable
+interface ConstraintViolationListInterface extends \Traversable, \Countable, \ArrayAccess
 {
 }

From b055cab8b0dc01fb462220e3796ace29d9dab27d Mon Sep 17 00:00:00 2001
From: Christophe Coevoet <stof@notk.org>
Date: Mon, 28 Aug 2023 17:31:11 +0200
Subject: [PATCH 234/297] Add a more precise return type for ParameterBag::keys
 in stubs

---
 stubs/Symfony/Component/HttpFoundation/ParameterBag.stub | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
index 00a40741..0a6858e3 100644
--- a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
+++ b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
@@ -7,5 +7,10 @@ namespace Symfony\Component\HttpFoundation;
  */
 class ParameterBag implements \IteratorAggregate
 {
-
+    /**
+     * @return list<string>
+     */
+    public function keys(): array
+    {
+    }
 }

From 7120655127f68bd78e0b012431707d1260a460f8 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 11 Sep 2023 00:15:15 +0000
Subject: [PATCH 235/297] chore(deps): update actions/checkout action to v4

---
 .github/workflows/build.yml      | 10 +++++-----
 .github/workflows/create-tag.yml |  2 +-
 .github/workflows/release.yml    |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ba129bc9..06457c85 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,7 +25,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
@@ -49,10 +49,10 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: "Checkout build-cs"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           repository: "phpstan/build-cs"
           path: "build-cs"
@@ -99,7 +99,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
@@ -138,7 +138,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml
index 8452d986..a8535014 100644
--- a/.github/workflows/create-tag.yml
+++ b/.github/workflows/create-tag.yml
@@ -21,7 +21,7 @@ jobs:
     runs-on: "ubuntu-latest"
     steps:
       - name: "Checkout"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
           token: ${{ secrets.PHPSTAN_BOT_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 92b72547..e4a8ac62 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -14,7 +14,7 @@ jobs:
 
     steps:
       - name: "Checkout"
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Generate changelog
         id: changelog

From abc7682290c4a1acf3425566616f2fe42c5fa8d0 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 28 Sep 2023 14:20:16 +0200
Subject: [PATCH 236/297] Open 1.3.x-dev

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

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 06457c85..58ae8be8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "1.2.x"
+      - "1.3.x"
 
 jobs:
   lint:

From 67cf72e7a1f4c7563ee5c17eb5b7fe1f29ac5088 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 28 Sep 2023 14:21:03 +0200
Subject: [PATCH 237/297] Open 1.4.x-dev

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

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 58ae8be8..b4453157 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "1.3.x"
+      - "1.4.x"
 
 jobs:
   lint:

From 6a0cefe070ce1dc70561a37d4181cf8f21aee94e Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 8 Jun 2023 10:18:44 +0200
Subject: [PATCH 238/297] Require PHPStan 1.11

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index ca18861b..fc3e9368 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.9.18"
+    "phpstan/phpstan": "^1.11"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From ce3c25807d9713897605b420c6830d6a86cf56b5 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 8 Jun 2023 11:07:26 +0200
Subject: [PATCH 239/297] Error identifiers

---
 src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php | 1 +
 src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php | 4 +++-
 src/Rules/Symfony/InvalidArgumentDefaultValueRule.php      | 4 ++--
 src/Rules/Symfony/InvalidOptionDefaultValueRule.php        | 4 ++--
 src/Rules/Symfony/UndefinedArgumentRule.php                | 4 +++-
 src/Rules/Symfony/UndefinedOptionRule.php                  | 4 +++-
 6 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 5e10c2b8..996d3b77 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -71,6 +71,7 @@ public function processNode(Node $node, Scope $scope): array
 			if ($service !== null && !$service->isPublic()) {
 				return [
 					RuleErrorBuilder::message(sprintf('Service "%s" is private.', $serviceId))
+						->identifier('symfonyContainer.privateService')
 						->build(),
 				];
 			}
diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index ccc1999e..fc7d9585 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -71,7 +71,9 @@ public function processNode(Node $node, Scope $scope): array
 			$serviceIdType = $scope->getType($node->getArgs()[0]->value);
 			if ($service === null && !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType)) {
 				return [
-					RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId))->build(),
+					RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId))
+						->identifier('symfonyContainer.serviceNotFound')
+						->build(),
 				];
 			}
 		}
diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index a55881ce..153d4af5 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -63,7 +63,7 @@ public function processNode(Node $node, Scope $scope): array
 				RuleErrorBuilder::message(sprintf(
 					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.',
 					$defaultType->describe(VerbosityLevel::typeOnly())
-				))->build(),
+				))->identifier('argument.type')->build(),
 			];
 		}
 
@@ -73,7 +73,7 @@ public function processNode(Node $node, Scope $scope): array
 				RuleErrorBuilder::message(sprintf(
 					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array<int, string>|null, %s given.',
 					$defaultType->describe(VerbosityLevel::typeOnly())
-				))->build(),
+				))->identifier('argument.type')->build(),
 			];
 		}
 
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index 6964d25e..1595f4a8 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -68,7 +68,7 @@ public function processNode(Node $node, Scope $scope): array
 						'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.',
 						$checkType->describe(VerbosityLevel::typeOnly()),
 						$defaultType->describe(VerbosityLevel::typeOnly())
-					))->build(),
+					))->identifier('argument.type')->build(),
 				];
 			}
 		}
@@ -79,7 +79,7 @@ public function processNode(Node $node, Scope $scope): array
 				RuleErrorBuilder::message(sprintf(
 					'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array<string>|null, %s given.',
 					$defaultType->describe(VerbosityLevel::typeOnly())
-				))->build(),
+				))->identifier('argument.type')->build(),
 			];
 		}
 
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index 07f35e2a..cd8be894 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -75,7 +75,9 @@ public function processNode(Node $node, Scope $scope): array
 				if ($scope->getType(Helper::createMarkerNode($node->var, $argType, $this->printer))->equals($argType)) {
 					continue;
 				}
-				$errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define argument "%s".', $name, $argName))->build();
+				$errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define argument "%s".', $name, $argName))
+					->identifier('symfonyConsole.argumentNotFound')
+					->build();
 			}
 		}
 
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index 5a8dc8e3..095bd44d 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -75,7 +75,9 @@ public function processNode(Node $node, Scope $scope): array
 				if ($scope->getType(Helper::createMarkerNode($node->var, $optType, $this->printer))->equals($optType)) {
 					continue;
 				}
-				$errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define option "%s".', $name, $optName))->build();
+				$errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define option "%s".', $name, $optName))
+					->identifier('symfonyConsole.optionNotFound')
+					->build();
 			}
 		}
 

From 383855999db6a7d65d0bf580ce2762e17188c2a5 Mon Sep 17 00:00:00 2001
From: raalderink <remco.a@hotmail.com>
Date: Fri, 21 Apr 2023 13:37:56 +0200
Subject: [PATCH 240/297] Set properties autowired with @required as
 initialized

---
 composer.json                                 |  5 +-
 extension.neon                                |  7 ++
 phpstan-baseline.neon                         | 10 ++
 src/Symfony/RequiredAutowiringExtension.php   | 91 +++++++++++++++++++
 .../RequiredAutowiringExtensionTest.php       | 65 +++++++++++++
 tests/Symfony/data/required-annotations.php   | 38 ++++++++
 tests/Symfony/data/required-attributes.php    | 38 ++++++++
 tests/Symfony/required-autowiring-config.neon |  6 ++
 8 files changed, 258 insertions(+), 2 deletions(-)
 create mode 100644 src/Symfony/RequiredAutowiringExtension.php
 create mode 100644 tests/Symfony/RequiredAutowiringExtensionTest.php
 create mode 100644 tests/Symfony/data/required-annotations.php
 create mode 100644 tests/Symfony/data/required-attributes.php
 create mode 100644 tests/Symfony/required-autowiring-config.neon

diff --git a/composer.json b/composer.json
index ca18861b..b8f33962 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.9.18"
+    "phpstan/phpstan": "^1.10.36"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
@@ -35,7 +35,8 @@
     "symfony/http-foundation": "^5.4 || ^6.1",
     "symfony/messenger": "^5.4",
     "symfony/polyfill-php80": "^1.24",
-    "symfony/serializer": "^5.4"
+    "symfony/serializer": "^5.4",
+    "symfony/service-contracts": "^2.2.0"
   },
   "config": {
     "sort-packages": true
diff --git a/extension.neon b/extension.neon
index 40165b39..fdfbfdcf 100644
--- a/extension.neon
+++ b/extension.neon
@@ -329,3 +329,10 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension
 		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
+
+	# Additional constructors and initialization checks for @required autowiring
+	-
+		class: PHPStan\Symfony\RequiredAutowiringExtension
+		tags:
+			- phpstan.properties.readWriteExtension
+			- phpstan.additionalConstructorsExtension
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 4abdaa0a..0f6edd5c 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,5 +1,10 @@
 parameters:
 	ignoreErrors:
+		-
+			message: "#^Although PHPStan\\\\Reflection\\\\Php\\\\PhpPropertyReflection is covered by backward compatibility promise, this instanceof assumption might break because it's not guaranteed to always stay the same\\.$#"
+			count: 1
+			path: src/Symfony/RequiredAutowiringExtension.php
+
 		-
 			message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Console\\\\Input\\\\InputOption and 'isNegatable' will always evaluate to true\\.$#"
 			count: 1
@@ -10,6 +15,11 @@ parameters:
 			count: 1
 			path: tests/Rules/NonexistentInputBagClassTest.php
 
+		-
+			message: "#^Accessing PHPStan\\\\Rules\\\\Properties\\\\UninitializedPropertyRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
+			count: 1
+			path: tests/Symfony/RequiredAutowiringExtensionTest.php
+
 		-
 			message: "#^Accessing PHPStan\\\\Rules\\\\Comparison\\\\ImpossibleCheckTypeMethodCallRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
 			count: 1
diff --git a/src/Symfony/RequiredAutowiringExtension.php b/src/Symfony/RequiredAutowiringExtension.php
new file mode 100644
index 00000000..a3cc6a31
--- /dev/null
+++ b/src/Symfony/RequiredAutowiringExtension.php
@@ -0,0 +1,91 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Reflection\AdditionalConstructorsExtension;
+use PHPStan\Reflection\ClassReflection;
+use PHPStan\Reflection\Php\PhpPropertyReflection;
+use PHPStan\Reflection\PropertyReflection;
+use PHPStan\Rules\Properties\ReadWritePropertiesExtension;
+use PHPStan\Type\FileTypeMapper;
+use function count;
+
+class RequiredAutowiringExtension implements ReadWritePropertiesExtension, AdditionalConstructorsExtension
+{
+
+	/** @var FileTypeMapper */
+	private $fileTypeMapper;
+
+	public function __construct(FileTypeMapper $fileTypeMapper)
+	{
+		$this->fileTypeMapper = $fileTypeMapper;
+	}
+
+	public function isAlwaysRead(PropertyReflection $property, string $propertyName): bool
+	{
+		return false;
+	}
+
+	public function isAlwaysWritten(PropertyReflection $property, string $propertyName): bool
+	{
+		return false;
+	}
+
+	public function isInitialized(PropertyReflection $property, string $propertyName): bool
+	{
+		// If the property is public, check for @required on the property itself
+		if (!$property->isPublic()) {
+			return false;
+		}
+
+		if ($property->getDocComment() !== null && $this->isRequiredFromDocComment($property->getDocComment())) {
+			return true;
+		}
+
+		// Check for the attribute version
+		if ($property instanceof PhpPropertyReflection && count($property->getNativeReflection()->getAttributes('Symfony\Contracts\Service\Attribute\Required')) > 0) {
+			return true;
+		}
+
+		return false;
+	}
+
+	public function getAdditionalConstructors(ClassReflection $classReflection): array
+	{
+		$additionalConstructors = [];
+		$nativeReflection = $classReflection->getNativeReflection();
+
+		foreach ($nativeReflection->getMethods() as $method) {
+			if (!$method->isPublic()) {
+				continue;
+			}
+
+			if ($method->getDocComment() !== false && $this->isRequiredFromDocComment($method->getDocComment())) {
+				$additionalConstructors[] = $method->getName();
+			}
+
+			if (count($method->getAttributes('Symfony\Contracts\Service\Attribute\Required')) === 0) {
+				continue;
+			}
+
+			$additionalConstructors[] = $method->getName();
+		}
+
+		return $additionalConstructors;
+	}
+
+	private function isRequiredFromDocComment(string $docComment): bool
+	{
+		$phpDoc = $this->fileTypeMapper->getResolvedPhpDoc(null, null, null, null, $docComment);
+
+		foreach ($phpDoc->getPhpDocNodes() as $node) {
+			// @required tag is available, meaning this property is always initialized
+			if (count($node->getTagsByName('@required')) > 0) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/tests/Symfony/RequiredAutowiringExtensionTest.php b/tests/Symfony/RequiredAutowiringExtensionTest.php
new file mode 100644
index 00000000..93fb3822
--- /dev/null
+++ b/tests/Symfony/RequiredAutowiringExtensionTest.php
@@ -0,0 +1,65 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Reflection\AdditionalConstructorsExtension;
+use PHPStan\Rules\Properties\UninitializedPropertyRule;
+use PHPStan\Rules\Rule;
+use PHPStan\Testing\RuleTestCase;
+use Symfony\Contracts\Service\Attribute\Required;
+use function class_exists;
+
+/**
+ * @extends RuleTestCase<UninitializedPropertyRule>
+ */
+final class RequiredAutowiringExtensionTest extends RuleTestCase
+{
+
+	protected function getRule(): Rule
+	{
+		$container = self::getContainer();
+		$container->getServicesByTag(AdditionalConstructorsExtension::EXTENSION_TAG);
+
+		return $container->getByType(UninitializedPropertyRule::class);
+	}
+
+	public function testRequiredAnnotations(): void
+	{
+		$this->analyse([__DIR__ . '/data/required-annotations.php'], [
+			[
+				'Class RequiredAnnotationTest\TestAnnotations has an uninitialized property $three. Give it default value or assign it in the constructor.',
+				12,
+			],
+			[
+				'Class RequiredAnnotationTest\TestAnnotations has an uninitialized property $four. Give it default value or assign it in the constructor.',
+				14,
+			],
+		]);
+	}
+
+	public function testRequiredAttributes(): void
+	{
+		if (!class_exists(Required::class)) {
+			self::markTestSkipped('Required symfony/service-contracts@3.2.1 or higher is not installed');
+		}
+
+		$this->analyse([__DIR__ . '/data/required-attributes.php'], [
+			[
+				'Class RequiredAttributesTest\TestAttributes has an uninitialized property $three. Give it default value or assign it in the constructor.',
+				14,
+			],
+			[
+				'Class RequiredAttributesTest\TestAttributes has an uninitialized property $four. Give it default value or assign it in the constructor.',
+				16,
+			],
+		]);
+	}
+
+	public static function getAdditionalConfigFiles(): array
+	{
+		return [
+			__DIR__ . '/required-autowiring-config.neon',
+		];
+	}
+
+}
diff --git a/tests/Symfony/data/required-annotations.php b/tests/Symfony/data/required-annotations.php
new file mode 100644
index 00000000..e2085ab3
--- /dev/null
+++ b/tests/Symfony/data/required-annotations.php
@@ -0,0 +1,38 @@
+<?php // lint >= 7.4
+
+namespace RequiredAnnotationTest;
+
+class TestAnnotations
+{
+	/** @required */
+	public string $one;
+
+	private string $two;
+
+	public string $three;
+
+	private string $four;
+
+	/**
+	 * @required
+	 */
+	public function setTwo(int $two): void
+	{
+		$this->two = $two;
+	}
+
+	public function getTwo(): int
+	{
+		return $this->two;
+	}
+
+	public function setFour(int $four): void
+	{
+		$this->four = $four;
+	}
+
+	public function getFour(): int
+	{
+		return $this->four;
+	}
+}
diff --git a/tests/Symfony/data/required-attributes.php b/tests/Symfony/data/required-attributes.php
new file mode 100644
index 00000000..d847d276
--- /dev/null
+++ b/tests/Symfony/data/required-attributes.php
@@ -0,0 +1,38 @@
+<?php // lint >= 8.0
+
+namespace RequiredAttributesTest;
+
+use Symfony\Contracts\Service\Attribute\Required;
+
+class TestAttributes
+{
+	#[Required]
+	public string $one;
+
+	private string $two;
+
+	public string $three;
+
+	private string $four;
+
+	#[Required]
+	public function setTwo(int $two): void
+	{
+		$this->two = $two;
+	}
+
+	public function getTwo(): int
+	{
+		return $this->two;
+	}
+
+	public function setFour(int $four): void
+	{
+		$this->four = $four;
+	}
+
+	public function getFour(): int
+	{
+		return $this->four;
+	}
+}
diff --git a/tests/Symfony/required-autowiring-config.neon b/tests/Symfony/required-autowiring-config.neon
new file mode 100644
index 00000000..3ff4183c
--- /dev/null
+++ b/tests/Symfony/required-autowiring-config.neon
@@ -0,0 +1,6 @@
+services:
+	-
+		class: PHPStan\Symfony\RequiredAutowiringExtension
+		tags:
+		    - phpstan.properties.readWriteExtension
+		    - phpstan.additionalConstructorsExtension

From 27ff6339f83796a7e0dd963cf445cd3c456fc620 Mon Sep 17 00:00:00 2001
From: Vladislav Nikolayev <luxemate@users.noreply.github.com>
Date: Mon, 30 Oct 2023 16:52:15 +0200
Subject: [PATCH 241/297] Fix stub of `Voter::supports()` method

---
 .../Component/Security/Core/Authorization/Voter/Voter.stub       | 1 +
 1 file changed, 1 insertion(+)

diff --git a/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub
index 17485a28..c2daae3b 100644
--- a/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub
+++ b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub
@@ -13,6 +13,7 @@ abstract class Voter implements VoterInterface
     /**
      * Determines if the attribute and subject are supported by this voter.
      *
+     * @param string $attribute
      * @param mixed $subject
      *
      * @phpstan-assert-if-true TSubject $subject

From 44b68dda25f6a32f5628e7ae3c01a8ffe17171ec Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 20 Nov 2023 01:05:39 +0000
Subject: [PATCH 242/297] chore(deps): update dessant/lock-threads action to v5

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

diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml
index 4c7990df..c2b017b9 100644
--- a/.github/workflows/lock-closed-issues.yml
+++ b/.github/workflows/lock-closed-issues.yml
@@ -8,7 +8,7 @@ jobs:
   lock:
     runs-on: ubuntu-latest
     steps:
-      - uses: dessant/lock-threads@v4
+      - uses: dessant/lock-threads@v5
         with:
           github-token: ${{ github.token }}
           issue-inactive-days: '31'

From 4cc4788b6692b34948385a7638b324b1bc35377a Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 20 Nov 2023 01:05:35 +0000
Subject: [PATCH 243/297] chore(deps): update metcalfc/changelog-generator
 action to v4.2.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 e4a8ac62..2fb750a4 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.1.0
+        uses: metcalfc/changelog-generator@v4.2.0
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From 34b3c43684834f6a20aa51af8d455480d9de8b88 Mon Sep 17 00:00:00 2001
From: Markus Staab <maggus.staab@googlemail.com>
Date: Fri, 22 Dec 2023 12:22:34 +0100
Subject: [PATCH 244/297] Fix `CacheInterface::get()` return type

---
 extension.neon                                |  5 ++
 ...InterfaceGetDynamicReturnTypeExtension.php | 48 +++++++++++++++++++
 tests/Type/Symfony/data/cache.php             | 20 ++++++++
 3 files changed, 73 insertions(+)
 create mode 100644 src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php

diff --git a/extension.neon b/extension.neon
index fdfbfdcf..af69d8bf 100644
--- a/extension.neon
+++ b/extension.neon
@@ -336,3 +336,8 @@ services:
 		tags:
 			- phpstan.properties.readWriteExtension
 			- phpstan.additionalConstructorsExtension
+
+	# CacheInterface::get() return type
+	-
+		factory: PHPStan\Type\Symfony\CacheInterfaceGetDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..ce736bfe
--- /dev/null
+++ b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php
@@ -0,0 +1,48 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ParametersAcceptorSelector;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\GeneralizePrecision;
+use PHPStan\Type\Type;
+
+final class CacheInterfaceGetDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	public function getClass(): string
+	{
+		return 'Symfony\Contracts\Cache\CacheInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'get';
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
+	{
+		if (!isset($methodCall->getArgs()[1])) {
+			return null;
+		}
+
+		$callbackReturnType = $scope->getType($methodCall->getArgs()[1]->value);
+		if ($callbackReturnType->isCallable()->yes()) {
+			$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
+				$scope,
+				$methodCall->getArgs(),
+				$callbackReturnType->getCallableParametersAcceptors($scope)
+			);
+			$returnType = $parametersAcceptor->getReturnType();
+
+			// generalize template parameters
+			return $returnType->generalize(GeneralizePrecision::templateArgument());
+		}
+
+		return null;
+	}
+
+}
diff --git a/tests/Type/Symfony/data/cache.php b/tests/Type/Symfony/data/cache.php
index 6b0728d4..a8862177 100644
--- a/tests/Type/Symfony/data/cache.php
+++ b/tests/Type/Symfony/data/cache.php
@@ -12,6 +12,26 @@ function testCacheCallable(\Symfony\Contracts\Cache\CacheInterface  $cache): voi
 	assertType('string', $result);
 };
 
+/**
+ * @param callable():string $fn
+ */
+function testNonScalarCacheCallable(\Symfony\Contracts\Cache\CacheInterface $cache, callable $fn): void {
+	$result = $cache->get('foo', $fn);
+
+	assertType('string', $result);
+};
+
+
+/**
+ * @param callable():non-empty-string $fn
+ */
+function testCacheCallableReturnTypeGeneralization(\Symfony\Contracts\Cache\CacheInterface $cache, callable $fn): void {
+	$result = $cache->get('foo', $fn);
+
+	assertType('string', $result);
+};
+
+
 /**
  * @param \Symfony\Contracts\Cache\CallbackInterface<\stdClass> $cb
  */

From ef7db637be9b85fa00278fc3477ac66abe8eb7d1 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Wed, 10 Jan 2024 11:46:57 +0100
Subject: [PATCH 245/297] Infer type for builder getData

---
 extension.neon                                   |  5 +++++
 stubs/Symfony/Component/Form/AbstractType.stub   |  1 +
 .../Component/Form/FormBuilderInterface.stub     | 12 ++++++++++--
 .../Form/FormConfigBuilderInterface.stub         | 13 +++++++++++++
 .../Component/Form/FormConfigInterface.stub      | 16 ++++++++++++++++
 .../Form/FormTypeExtensionInterface.stub         |  1 +
 .../Component/Form/FormTypeInterface.stub        |  1 +
 tests/Type/Symfony/data/form_data_type.php       |  3 +++
 8 files changed, 50 insertions(+), 2 deletions(-)
 create mode 100644 stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub
 create mode 100644 stubs/Symfony/Component/Form/FormConfigInterface.stub

diff --git a/extension.neon b/extension.neon
index af69d8bf..dec41999 100644
--- a/extension.neon
+++ b/extension.neon
@@ -14,6 +14,9 @@ parameters:
 	featureToggles:
 		skipCheckGenericClasses:
 			- Symfony\Component\Form\AbstractType
+			- Symfony\Component\Form\FormBuilderInterface
+			- Symfony\Component\Form\FormConfigBuilderInterface
+			- Symfony\Component\Form\FormConfigInterface
 			- Symfony\Component\Form\FormInterface
 			- Symfony\Component\Form\FormTypeExtensionInterface
 			- Symfony\Component\Form\FormTypeInterface
@@ -47,6 +50,8 @@ parameters:
 		- stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub
 		- stubs/Symfony/Component/Form/DataTransformerInterface.stub
 		- stubs/Symfony/Component/Form/FormBuilderInterface.stub
+		- stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub
+		- stubs/Symfony/Component/Form/FormConfigInterface.stub
 		- stubs/Symfony/Component/Form/FormInterface.stub
 		- stubs/Symfony/Component/Form/FormFactoryInterface.stub
 		- stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
diff --git a/stubs/Symfony/Component/Form/AbstractType.stub b/stubs/Symfony/Component/Form/AbstractType.stub
index da2b1439..e99b746c 100644
--- a/stubs/Symfony/Component/Form/AbstractType.stub
+++ b/stubs/Symfony/Component/Form/AbstractType.stub
@@ -11,6 +11,7 @@ abstract class AbstractType implements FormTypeInterface
 {
 
 	/**
+	 * @param FormBuilderInterface<TData|null> $builder
 	 * @param array<string, mixed> $options
 	 */
 	public function buildForm(FormBuilderInterface $builder, array $options): void;
diff --git a/stubs/Symfony/Component/Form/FormBuilderInterface.stub b/stubs/Symfony/Component/Form/FormBuilderInterface.stub
index 5173bb64..fe578c50 100644
--- a/stubs/Symfony/Component/Form/FormBuilderInterface.stub
+++ b/stubs/Symfony/Component/Form/FormBuilderInterface.stub
@@ -3,9 +3,17 @@
 namespace Symfony\Component\Form;
 
 /**
- * @extends \Traversable<int, \Symfony\Component\Form\FormBuilderInterface>
+ * @template TData
+ *
+ * @extends \Traversable<int, \Symfony\Component\Form\FormBuilderInterface<mixed>>
+ * @extends FormConfigBuilderInterface<TData>
  */
-interface FormBuilderInterface extends \Traversable
+interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuilderInterface
 {
 
+    /**
+     * @return FormInterface<TData|null>
+     */
+    public function getForm(): FormInterface;
+
 }
diff --git a/stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub b/stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub
new file mode 100644
index 00000000..a167ce43
--- /dev/null
+++ b/stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub
@@ -0,0 +1,13 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+/**
+ * @template TData
+ *
+ * @extends FormConfigInterface<TData>
+ */
+interface FormConfigBuilderInterface extends FormConfigInterface
+{
+
+}
diff --git a/stubs/Symfony/Component/Form/FormConfigInterface.stub b/stubs/Symfony/Component/Form/FormConfigInterface.stub
new file mode 100644
index 00000000..942d467b
--- /dev/null
+++ b/stubs/Symfony/Component/Form/FormConfigInterface.stub
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Component\Form;
+
+/**
+ * @template TData
+ */
+interface FormConfigInterface
+{
+
+    /**
+     * @return TData
+     */
+    public function getData(): mixed;
+
+}
diff --git a/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub b/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
index 234d1b2e..a03d5e1c 100644
--- a/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
+++ b/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub
@@ -8,6 +8,7 @@ namespace Symfony\Component\Form;
 interface FormTypeExtensionInterface
 {
     /**
+     * @param FormBuilderInterface<TData|null> $builder
      * @param array<string, mixed> $options
      */
     public function buildForm(FormBuilderInterface $builder, array $options): void;
diff --git a/stubs/Symfony/Component/Form/FormTypeInterface.stub b/stubs/Symfony/Component/Form/FormTypeInterface.stub
index 2f745283..8536656a 100644
--- a/stubs/Symfony/Component/Form/FormTypeInterface.stub
+++ b/stubs/Symfony/Component/Form/FormTypeInterface.stub
@@ -8,6 +8,7 @@ namespace Symfony\Component\Form;
 interface FormTypeInterface
 {
     /**
+     * @param FormBuilderInterface<TData|null> $builder
      * @param array<string, mixed> $options
      */
     public function buildForm(FormBuilderInterface $builder, array $options): void;
diff --git a/tests/Type/Symfony/data/form_data_type.php b/tests/Type/Symfony/data/form_data_type.php
index 524a5b7c..e0cefb88 100644
--- a/tests/Type/Symfony/data/form_data_type.php
+++ b/tests/Type/Symfony/data/form_data_type.php
@@ -29,6 +29,9 @@ class DataClassType extends AbstractType
 
 	public function buildForm(FormBuilderInterface $builder, array $options): void
 	{
+		assertType('GenericFormDataType\DataClass|null', $builder->getData());
+		assertType('GenericFormDataType\DataClass|null', $builder->getForm()->getData());
+
 		$builder
 			->add('foo', NumberType::class)
 			->add('bar', TextType::class)

From b2ddd0ffa7631afed5662f3e450320cd630257d6 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 00:26:02 +0000
Subject: [PATCH 246/297] chore(deps): update cbrgm/mastodon-github-action
 action to v2

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

diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml
index 6a1c8156..1ba4fd77 100644
--- a/.github/workflows/release-toot.yml
+++ b/.github/workflows/release-toot.yml
@@ -10,7 +10,7 @@ jobs:
   toot:
     runs-on: ubuntu-latest
     steps:
-      - uses: cbrgm/mastodon-github-action@v1
+      - uses: cbrgm/mastodon-github-action@v2
         if: ${{ !github.event.repository.private }}
         with:
           # GitHub event payload

From a0e0f1f832d509f86eaf388a1881de664455b170 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 16 Feb 2024 22:38:05 +0000
Subject: [PATCH 247/297] chore(deps): update metcalfc/changelog-generator
 action to v4.3.1

---
 .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 2fb750a4..b1a669a9 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.2.0
+        uses: metcalfc/changelog-generator@v4.3.1
         with:
           myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}
 

From 67e208de049211ef1b0598be4576cb24729b20a9 Mon Sep 17 00:00:00 2001
From: Emanuele Panzeri <thepanz@gmail.com>
Date: Fri, 23 Feb 2024 16:08:10 +0100
Subject: [PATCH 248/297] Add documentation to load DI helper

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index 78f02320..25a155c1 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,10 @@ parameters:
     # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`:
     scanDirectories:
         - var/cache/dev/Symfony/Config
+    # If you're using PHP config files (including the ones under packages/*.php) for Symfony 5.3+,
+    # you need this to load the helper functions (i.e. service(), env()):
+    scanFiles:
+        - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php
 ```
 
 ## Constant hassers

From d8a0bc03a68d95288b6471c37d435647fbdaff1a Mon Sep 17 00:00:00 2001
From: Robert Meijers <symfony@robert.meijers.dev>
Date: Thu, 29 Feb 2024 14:44:49 +0100
Subject: [PATCH 249/297] Narrow type for Extension::getConfiguration if class
 exists

The base extension class automatically creates a Configuration instance
when a Configuration class exists in the namespace of the extension. But
PHPStan obviously doesn't understand this behaviour and always assumes
that `getConfiguration()` returns `ConfigurationInterface|null` meaning
that the default pattern to get and parse the configuration reports an
error.

I.e.:
```php
namespace Foo;

class SomeExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container): void
    {
        $configuration = $this->getConfiguration($configs, $container);
        $config = $this->processConfiguration($configuration, $configs);
    }
}
```
results in an error because `processConfiguration()` doesn't accept
`ConfigurationInterface|null`. But when a `Configuration` class exists
in the same namespace as the `Extension` class (so `Foo\Extension`) an
instance of it is returned.

This `DynamicReturnTypeExtension` overrides the return type of
`Extension::getConfiguration()` so it automatically narrows the return
type in case `getConfiguration()` is not overriden and a `Configuration`
class exists. So that in the given example `getConfiguration()` doesn't
return `ConfigurationInterface|null` anymore but `Foo\Configuration` and
there is no error on calling `processConfiguration()`.
---
 extension.neon                                |   5 +
 ...ionGetConfigurationReturnTypeExtension.php | 105 ++++++++++++++++++
 tests/Type/Symfony/ExtensionTest.php          |   9 ++
 .../anonymous/AnonymousExtension.php          |  17 +++
 .../IgnoreImplementedExtension.php            |  23 ++++
 .../multiple-types/MultipleTypes.php          |  18 +++
 .../Configuration.php                         |  16 +++
 ...WithConstructorOptionalParamsExtension.php |  17 +++
 .../Configuration.php                         |  16 +++
 ...WithConstructorRequiredParamsExtension.php |  17 +++
 .../Configuration.php                         |  16 +++
 ...hConfigurationWithConstructorExtension.php |  17 +++
 .../with-configuration/Configuration.php      |  12 ++
 .../WithConfigurationExtension.php            |  17 +++
 .../WithoutConfigurationExtension.php         |  17 +++
 15 files changed, 322 insertions(+)
 create mode 100644 src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php
 create mode 100644 tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php
 create mode 100644 tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php
 create mode 100644 tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/WithConfigurationWithConstructorOptionalParamsExtension.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/WithConfigurationWithConstructorRequiredParamsExtension.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor/WithConfigurationWithConstructorExtension.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration/Configuration.php
 create mode 100644 tests/Type/Symfony/data/extension/with-configuration/WithConfigurationExtension.php
 create mode 100644 tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php

diff --git a/extension.neon b/extension.neon
index dec41999..531eb7df 100644
--- a/extension.neon
+++ b/extension.neon
@@ -346,3 +346,8 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\CacheInterfaceGetDynamicReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# Extension::getConfiguration() return type
+	-
+		factory: PHPStan\Type\Symfony\ExtensionGetConfigurationReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php
new file mode 100644
index 00000000..7af50773
--- /dev/null
+++ b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php
@@ -0,0 +1,105 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Reflection\ClassReflection;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\ReflectionProvider;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\NullType;
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
+use function str_contains;
+use function strrpos;
+use function substr_replace;
+
+class ExtensionGetConfigurationReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var ReflectionProvider */
+	private $reflectionProvider;
+
+	public function __construct(ReflectionProvider $reflectionProvider)
+	{
+		$this->reflectionProvider = $reflectionProvider;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\DependencyInjection\Extension\Extension';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getConfiguration'
+			&& $methodReflection->getDeclaringClass()->getName() === 'Symfony\Component\DependencyInjection\Extension\Extension';
+	}
+
+	public function getTypeFromMethodCall(
+		MethodReflection $methodReflection,
+		MethodCall $methodCall,
+		Scope $scope
+	): ?Type
+	{
+		$types = [];
+		$extensionType = $scope->getType($methodCall->var);
+		$classes = $extensionType->getObjectClassNames();
+
+		foreach ($classes as $extensionName) {
+			if (str_contains($extensionName, "\0")) {
+				$types[] = new NullType();
+				continue;
+			}
+
+			$lastBackslash = strrpos($extensionName, '\\');
+			if ($lastBackslash === false) {
+				$types[] = new NullType();
+				continue;
+			}
+
+			$configurationName = substr_replace($extensionName, '\Configuration', $lastBackslash);
+			if (!$this->reflectionProvider->hasClass($configurationName)) {
+				$types[] = new NullType();
+				continue;
+			}
+
+			$reflection = $this->reflectionProvider->getClass($configurationName);
+			if ($this->hasRequiredConstructor($reflection)) {
+				$types[] = new NullType();
+				continue;
+			}
+
+			$types[] = new ObjectType($configurationName);
+		}
+
+		return TypeCombinator::union(...$types);
+	}
+
+	private function hasRequiredConstructor(ClassReflection $class): bool
+	{
+		if (!$class->hasConstructor()) {
+			return false;
+		}
+
+		$constructor = $class->getConstructor();
+		foreach ($constructor->getVariants() as $variant) {
+			$anyRequired = false;
+			foreach ($variant->getParameters() as $parameter) {
+				if (!$parameter->isOptional()) {
+					$anyRequired = true;
+					break;
+				}
+			}
+
+			if (!$anyRequired) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 879abb5d..73eb49b7 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -57,6 +57,15 @@ public function dataFileAsserts(): iterable
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/cache.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/form_data_type.php');
+
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration/WithConfigurationExtension.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/without-configuration/WithoutConfigurationExtension.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/anonymous/AnonymousExtension.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/ignore-implemented/IgnoreImplementedExtension.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/multiple-types/MultipleTypes.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration-with-constructor/WithConfigurationWithConstructorExtension.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration-with-constructor-optional-params/WithConfigurationWithConstructorOptionalParamsExtension.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration-with-constructor-required-params/WithConfigurationWithConstructorRequiredParamsExtension.php');
 	}
 
 	/**
diff --git a/tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php b/tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php
new file mode 100644
index 00000000..1e33cb37
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\Anonymous;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Extension\Extension;
+
+new class extends Extension
+{
+	public function load(array $configs, ContainerBuilder $container)
+	{
+		\PHPStan\Testing\assertType(
+			'null',
+			$this->getConfiguration($configs, $container)
+		);
+	}
+};
diff --git a/tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php b/tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php
new file mode 100644
index 00000000..614431f2
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\IgnoreImplemented;
+
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use \Symfony\Component\DependencyInjection\Extension\Extension;
+
+class IgnoreImplementedExtension extends Extension
+{
+	public function load(array $configs, ContainerBuilder $container): void
+	{
+		\PHPStan\Testing\assertType(
+			'Symfony\Component\Config\Definition\ConfigurationInterface|null',
+			$this->getConfiguration($configs, $container)
+		);
+	}
+
+	public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface
+	{
+		return null;
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php b/tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php
new file mode 100644
index 00000000..77c44003
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\MultipleTypes;
+
+use PHPStan\Type\Symfony\Extension\WithConfiguration\WithConfigurationExtension;
+use PHPStan\Type\Symfony\Extension\WithoutConfiguration\WithoutConfigurationExtension;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * @param WithConfigurationExtension|WithoutConfigurationExtension $extension
+ */
+function test($extension, array $configs, ContainerBuilder $container)
+{
+	\PHPStan\Testing\assertType(
+		'PHPStan\Type\Symfony\Extension\WithConfiguration\Configuration|null',
+		$extension->getConfiguration($configs, $container)
+	);
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php
new file mode 100644
index 00000000..4ff16c39
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfigurationWithConstructorOptionalParams;
+
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+class Configuration implements ConfigurationInterface
+{
+	public function __construct($foo = null)
+	{
+	}
+
+	public function getConfigTreeBuilder()
+	{
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/WithConfigurationWithConstructorOptionalParamsExtension.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/WithConfigurationWithConstructorOptionalParamsExtension.php
new file mode 100644
index 00000000..56545350
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/WithConfigurationWithConstructorOptionalParamsExtension.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfigurationWithConstructorOptionalParams;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use \Symfony\Component\DependencyInjection\Extension\Extension;
+
+class WithConfigurationWithConstructorOptionalParamsExtension extends Extension
+{
+	public function load(array $configs, ContainerBuilder $container): void
+	{
+		\PHPStan\Testing\assertType(
+			Configuration::class,
+			$this->getConfiguration($configs, $container)
+		);
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php
new file mode 100644
index 00000000..b9d5bcc1
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfigurationWithConstructorRequiredParams;
+
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+class Configuration implements ConfigurationInterface
+{
+	public function __construct($foo)
+	{
+	}
+
+	public function getConfigTreeBuilder()
+	{
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/WithConfigurationWithConstructorRequiredParamsExtension.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/WithConfigurationWithConstructorRequiredParamsExtension.php
new file mode 100644
index 00000000..ccc8b992
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/WithConfigurationWithConstructorRequiredParamsExtension.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfigurationWithConstructorRequiredParams;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use \Symfony\Component\DependencyInjection\Extension\Extension;
+
+class WithConfigurationWithConstructorRequiredParamsExtension extends Extension
+{
+	public function load(array $configs, ContainerBuilder $container): void
+	{
+		\PHPStan\Testing\assertType(
+			'null',
+			$this->getConfiguration($configs, $container)
+		);
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php
new file mode 100644
index 00000000..8eea9eb9
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfigurationWithConstructor;
+
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+class Configuration implements ConfigurationInterface
+{
+	public function __construct()
+	{
+	}
+
+	public function getConfigTreeBuilder()
+	{
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor/WithConfigurationWithConstructorExtension.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor/WithConfigurationWithConstructorExtension.php
new file mode 100644
index 00000000..7b5a2daf
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor/WithConfigurationWithConstructorExtension.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfigurationWithConstructor;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use \Symfony\Component\DependencyInjection\Extension\Extension;
+
+class WithConfigurationWithConstructorExtension extends Extension
+{
+	public function load(array $configs, ContainerBuilder $container): void
+	{
+		\PHPStan\Testing\assertType(
+			Configuration::class,
+			$this->getConfiguration($configs, $container)
+		);
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration/Configuration.php
new file mode 100644
index 00000000..4e8c51b5
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration/Configuration.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfiguration;
+
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+class Configuration implements ConfigurationInterface
+{
+	public function getConfigTreeBuilder()
+	{
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/with-configuration/WithConfigurationExtension.php b/tests/Type/Symfony/data/extension/with-configuration/WithConfigurationExtension.php
new file mode 100644
index 00000000..9b809841
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/with-configuration/WithConfigurationExtension.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithConfiguration;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use \Symfony\Component\DependencyInjection\Extension\Extension;
+
+class WithConfigurationExtension extends Extension
+{
+	public function load(array $configs, ContainerBuilder $container): void
+	{
+		\PHPStan\Testing\assertType(
+			Configuration::class,
+			$this->getConfiguration($configs, $container)
+		);
+	}
+}
diff --git a/tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php b/tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php
new file mode 100644
index 00000000..dccec3e2
--- /dev/null
+++ b/tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PHPStan\Type\Symfony\Extension\WithoutConfiguration;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use \Symfony\Component\DependencyInjection\Extension\Extension;
+
+class WithoutConfigurationExtension extends Extension
+{
+	public function load(array $configs, ContainerBuilder $container): void
+	{
+		\PHPStan\Testing\assertType(
+			'null',
+			$this->getConfiguration($configs, $container)
+		);
+	}
+}

From a32bc86da24495025d7aafd1ba62444d4a364a98 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Sat, 16 Mar 2024 17:50:20 +0100
Subject: [PATCH 250/297] Add `@param-out` for PropertyAccessor

* Add param out for propertyAccessor

* Fix stub
---
 composer.json                                 |  2 +-
 extension.neon                                |  6 +++++
 .../Exception/AccessException.stub            |  7 ++++++
 .../Exception/ExceptionInterface.stub         |  7 ++++++
 .../Exception/InvalidArgumentException.stub   |  7 ++++++
 .../Exception/RuntimeException.stub           |  7 ++++++
 .../Exception/UnexpectedTypeException.stub    |  7 ++++++
 .../PropertyAccessorInterface.stub            | 23 +++++++++++++++++++
 tests/Type/Symfony/ExtensionTest.php          |  1 +
 tests/Type/Symfony/data/property_accessor.php | 13 +++++++++++
 10 files changed, 79 insertions(+), 1 deletion(-)
 create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub
 create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub
 create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub
 create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub
 create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub
 create mode 100644 stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub
 create mode 100644 tests/Type/Symfony/data/property_accessor.php

diff --git a/composer.json b/composer.json
index b8f33962..7da1f489 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.10.36"
+    "phpstan/phpstan": "^1.10.62"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index 531eb7df..09837d28 100644
--- a/extension.neon
+++ b/extension.neon
@@ -66,6 +66,12 @@ parameters:
 		- stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub
 		- stubs/Symfony/Component/OptionsResolver/Options.stub
 		- stubs/Symfony/Component/Process/Process.stub
+		- stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub
+		- stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub
+		- stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub
+		- stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub
+		- stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub
+		- stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub
diff --git a/stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub b/stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub
new file mode 100644
index 00000000..a763b784
--- /dev/null
+++ b/stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+class AccessException extends RuntimeException
+{
+}
diff --git a/stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub b/stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub
new file mode 100644
index 00000000..5028628d
--- /dev/null
+++ b/stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub b/stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub
new file mode 100644
index 00000000..66ee13fb
--- /dev/null
+++ b/stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub b/stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub
new file mode 100644
index 00000000..50827398
--- /dev/null
+++ b/stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub b/stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub
new file mode 100644
index 00000000..22bdaf62
--- /dev/null
+++ b/stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess\Exception;
+
+class UnexpectedTypeException extends RuntimeException
+{
+}
diff --git a/stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub b/stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub
new file mode 100644
index 00000000..63a09a01
--- /dev/null
+++ b/stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub
@@ -0,0 +1,23 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess;
+
+interface PropertyAccessorInterface
+{
+
+    /**
+     * @phpstan-template T of object|array<mixed>
+     * @phpstan-param T &$objectOrArray
+     * @phpstan-param-out ($objectOrArray is object ? T : array<mixed>) $objectOrArray
+     * @phpstan-param string|PropertyPathInterface $propertyPath
+     * @phpstan-param mixed $value
+     *
+     * @return void
+     *
+     * @throws Exception\InvalidArgumentException If the property path is invalid
+     * @throws Exception\AccessException          If a property/index does not exist or is not public
+     * @throws Exception\UnexpectedTypeException  If a value within the path is neither object nor array
+     */
+    public function setValue(&$objectOrArray, $propertyPath, $value);
+
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index 73eb49b7..a076caac 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -28,6 +28,7 @@ public function dataFileAsserts(): iterable
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionCommand.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionLazyCommand.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/kernel_interface.php');
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/property_accessor.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/request_get_content.php');
 
 		$ref = new ReflectionMethod(Request::class, 'getSession');
diff --git a/tests/Type/Symfony/data/property_accessor.php b/tests/Type/Symfony/data/property_accessor.php
new file mode 100644
index 00000000..0e445684
--- /dev/null
+++ b/tests/Type/Symfony/data/property_accessor.php
@@ -0,0 +1,13 @@
+<?php declare(strict_types = 1);
+
+use function PHPStan\Testing\assertType;
+
+$propertyAccessor = new \Symfony\Component\PropertyAccess\PropertyAccessor();
+
+$array = [1 => 'ea'];
+$propertyAccessor->setValue($array, 'foo', 'bar');
+assertType('array', $array);
+
+$object = new \stdClass();
+$propertyAccessor->setValue($object, 'foo', 'bar');
+assertType('stdClass', $object);

From 99848d82a72417cc70d3d57cbae0887040ca7fbe Mon Sep 17 00:00:00 2001
From: "Alexander M. Turek" <me@derrabus.de>
Date: Thu, 11 Apr 2024 16:37:03 +0200
Subject: [PATCH 251/297] Add stub for Compound::getConstraints()

---
 extension.neon                                     |  2 ++
 .../Component/Validator/Constraints/Composite.stub |  9 +++++++++
 .../Component/Validator/Constraints/Compound.stub  | 14 ++++++++++++++
 3 files changed, 25 insertions(+)
 create mode 100644 stubs/Symfony/Component/Validator/Constraints/Composite.stub
 create mode 100644 stubs/Symfony/Component/Validator/Constraints/Compound.stub

diff --git a/extension.neon b/extension.neon
index 09837d28..1eb888ab 100644
--- a/extension.neon
+++ b/extension.neon
@@ -96,6 +96,8 @@ parameters:
 		- stubs/Symfony/Component/Serializer/Normalizer/NormalizableInterface.stub
 		- stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub
 		- stubs/Symfony/Component/Validator/Constraint.stub
+		- stubs/Symfony/Component/Validator/Constraints/Composite.stub
+		- stubs/Symfony/Component/Validator/Constraints/Compound.stub
 		- stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
 		- stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub
 		- stubs/Symfony/Contracts/Cache/CacheInterface.stub
diff --git a/stubs/Symfony/Component/Validator/Constraints/Composite.stub b/stubs/Symfony/Component/Validator/Constraints/Composite.stub
new file mode 100644
index 00000000..8344ea94
--- /dev/null
+++ b/stubs/Symfony/Component/Validator/Constraints/Composite.stub
@@ -0,0 +1,9 @@
+<?php
+
+namespace Symfony\Component\Validator\Constraints;
+
+use Symfony\Component\Validator\Constraint;
+
+abstract class Composite extends Constraint
+{
+}
diff --git a/stubs/Symfony/Component/Validator/Constraints/Compound.stub b/stubs/Symfony/Component/Validator/Constraints/Compound.stub
new file mode 100644
index 00000000..3227009b
--- /dev/null
+++ b/stubs/Symfony/Component/Validator/Constraints/Compound.stub
@@ -0,0 +1,14 @@
+<?php
+
+namespace Symfony\Component\Validator\Constraints;
+
+use Symfony\Component\Validator\Constraint;
+
+abstract class Compound extends Composite
+{
+    /**
+     * @param array<mixed> $options
+     * @return array<Constraint>
+     */
+    abstract protected function getConstraints(array $options): array;
+}

From 3fbf634b2495adaa1dc4b7300293f8abf74128d8 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Wed, 6 Mar 2024 17:17:02 +0100
Subject: [PATCH 252/297] Synchronize stub with symfony

---
 .../Component/Validator/ConstraintViolationInterface.stub     | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
index e7f2b8a2..fd1c7b9b 100644
--- a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
+++ b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub
@@ -3,7 +3,9 @@
 namespace Symfony\Component\Validator;
 
 /**
- * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1.
+ * @method Constraint|null getConstraint() Returns the constraint whose validation caused the violation. Not implementing it is deprecated since Symfony 6.3.
+ * @method mixed           getCause()      Returns the cause of the violation. Not implementing it is deprecated since Symfony 6.2.
+ * @method string          __toString()    Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1.
  */
 interface ConstraintViolationInterface
 {

From f4b9407fa3203aebafd422ae8f0eb1ef94659a80 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Sat, 13 Apr 2024 10:28:03 +0200
Subject: [PATCH 253/297] Synchronize stub with symfony

---
 stubs/Symfony/Component/Validator/Constraints/Compound.stub | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/Validator/Constraints/Compound.stub b/stubs/Symfony/Component/Validator/Constraints/Compound.stub
index 3227009b..6a36c6bf 100644
--- a/stubs/Symfony/Component/Validator/Constraints/Compound.stub
+++ b/stubs/Symfony/Component/Validator/Constraints/Compound.stub
@@ -7,7 +7,7 @@ use Symfony\Component\Validator\Constraint;
 abstract class Compound extends Composite
 {
     /**
-     * @param array<mixed> $options
+     * @param array<string, mixed> $options
      * @return array<Constraint>
      */
     abstract protected function getConstraints(array $options): array;

From 51183fefbaf4713aa81eddbd273dc59dd5e5ff71 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Sat, 20 Apr 2024 08:38:35 +0200
Subject: [PATCH 254/297] Update lock-closed-issues.yml

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

diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml
index c2b017b9..047fe906 100644
--- a/.github/workflows/lock-closed-issues.yml
+++ b/.github/workflows/lock-closed-issues.yml
@@ -2,7 +2,7 @@ name: 'Lock Issues'
 
 on:
   schedule:
-    - cron: '0 0 * * *'
+    - cron: '5 0 * * *'
 
 jobs:
   lock:

From 2c3d666889173e902714359d8216ec5627527d92 Mon Sep 17 00:00:00 2001
From: Benoit Viguier <benoit.viguier@lendable.co.uk>
Date: Wed, 29 May 2024 10:32:08 +0200
Subject: [PATCH 255/297] Support for alias to inlined service

---
 src/Symfony/XmlServiceMapFactory.php    |  9 +++++++--
 tests/Symfony/DefaultServiceMapTest.php | 11 +++++++++++
 tests/Symfony/container.xml             |  2 ++
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index ad52c373..1cae5d97 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -48,11 +48,11 @@ public function create(): ServiceMap
 			}
 
 			$service = new Service(
-				strpos((string) $attrs->id, '.') === 0 ? substr((string) $attrs->id, 1) : (string) $attrs->id,
+				$this->cleanServiceId((string) $attrs->id),
 				isset($attrs->class) ? (string) $attrs->class : null,
 				isset($attrs->public) && (string) $attrs->public === 'true',
 				isset($attrs->synthetic) && (string) $attrs->synthetic === 'true',
-				isset($attrs->alias) ? (string) $attrs->alias : null
+				isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null
 			);
 
 			if ($service->getAlias() !== null) {
@@ -79,4 +79,9 @@ public function create(): ServiceMap
 		return new DefaultServiceMap($services);
 	}
 
+	private function cleanServiceId(string $id): string
+	{
+		return strpos($id, '.') === 0 ? substr($id, 1) : $id;
+	}
+
 }
diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php
index dc8487e5..a0a27d98 100644
--- a/tests/Symfony/DefaultServiceMapTest.php
+++ b/tests/Symfony/DefaultServiceMapTest.php
@@ -113,6 +113,17 @@ static function (?Service $service): void {
 				self::assertSame('withClass', $service->getAlias());
 			},
 		];
+		yield [
+			'aliasForInlined',
+			static function (?Service $service): void {
+				self::assertNotNull($service);
+				self::assertSame('aliasForInlined', $service->getId());
+				self::assertNull($service->getClass());
+				self::assertFalse($service->isPublic());
+				self::assertFalse($service->isSynthetic());
+				self::assertSame('inlined', $service->getAlias());
+			},
+		];
 	}
 
 }
diff --git a/tests/Symfony/container.xml b/tests/Symfony/container.xml
index 4be3bd98..f456ab51 100644
--- a/tests/Symfony/container.xml
+++ b/tests/Symfony/container.xml
@@ -41,5 +41,7 @@
     <service id="public" class="Foo" public="true"></service>
     <service id="synthetic" class="Foo" synthetic="true"></service>
     <service id="alias" class="Bar" alias="withClass"></service>
+    <service id=".inlined"></service>
+    <service id="aliasForInlined" alias=".inlined"></service>
   </services>
 </container>

From af6ae0f4b91bc080265e80776af26da3e5befb28 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Thu, 30 May 2024 17:01:27 +0200
Subject: [PATCH 256/297] Update getArgument return type in interact method

---
 ...InterfaceGetArgumentDynamicReturnTypeExtension.php | 11 +++++++++++
 tests/Type/Symfony/data/ExampleBaseCommand.php        | 11 +++++++++++
 2 files changed, 22 insertions(+)

diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index 0af7c88d..a8d3eb37 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -10,11 +10,13 @@
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\IntegerType;
+use PHPStan\Type\NullType;
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeCombinator;
 use PHPStan\Type\TypeUtils;
 use function count;
+use function in_array;
 
 final class InputInterfaceGetArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
@@ -76,6 +78,15 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			}
 		}
 
+		$method = $scope->getFunction();
+		if (
+			$method instanceof MethodReflection
+			&& $method->getName() === 'interact'
+			&& in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true)
+		) {
+			$argTypes[] = new NullType();
+		}
+
 		return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : null;
 	}
 
diff --git a/tests/Type/Symfony/data/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php
index 0ab3a322..6c1f9422 100644
--- a/tests/Type/Symfony/data/ExampleBaseCommand.php
+++ b/tests/Type/Symfony/data/ExampleBaseCommand.php
@@ -17,6 +17,17 @@ protected function configure(): void
 		$this->addArgument('base');
 	}
 
+	protected function interact(InputInterface $input, OutputInterface $output): int
+	{
+		assertType('string|null', $input->getArgument('base'));
+		assertType('string|null', $input->getArgument('aaa'));
+		assertType('string|null', $input->getArgument('bbb'));
+		assertType('array<int, string>|string|null', $input->getArgument('diff'));
+		assertType('array<int, string>|null', $input->getArgument('arr'));
+		assertType('string|null', $input->getArgument('both'));
+		assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question'));
+	}
+
 	protected function execute(InputInterface $input, OutputInterface $output): int
 	{
 		assertType('string|null', $input->getArgument('base'));

From bca27f1701fc1a297749e6c2a1e3da4462c1a6af Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Thu, 6 Jun 2024 15:55:20 +0200
Subject: [PATCH 257/297] Fix interact method inference

---
 ...nterfaceGetArgumentDynamicReturnTypeExtension.php | 12 ++++++++++--
 tests/Type/Symfony/data/ExampleBaseCommand.php       | 12 ++++++++----
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index a8d3eb37..860317f6 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -57,6 +57,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 		$argName = $argStrings[0]->getValue();
 
 		$argTypes = [];
+		$canBeNullInInteract = false;
 		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
 			try {
 				$command->mergeApplicationDefinition();
@@ -70,6 +71,8 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 					$argType = new StringType();
 					if (!$argument->isRequired()) {
 						$argType = TypeCombinator::union($argType, $scope->getTypeFromValue($argument->getDefault()));
+					} else {
+						$canBeNullInInteract = true;
 					}
 				}
 				$argTypes[] = $argType;
@@ -78,16 +81,21 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			}
 		}
 
+		if (count($argTypes) === 0) {
+			return null;
+		}
+
 		$method = $scope->getFunction();
 		if (
-			$method instanceof MethodReflection
+			$canBeNullInInteract
+			&& $method instanceof MethodReflection
 			&& $method->getName() === 'interact'
 			&& in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true)
 		) {
 			$argTypes[] = new NullType();
 		}
 
-		return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : null;
+		return TypeCombinator::union(...$argTypes);
 	}
 
 }
diff --git a/tests/Type/Symfony/data/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php
index 6c1f9422..d2357089 100644
--- a/tests/Type/Symfony/data/ExampleBaseCommand.php
+++ b/tests/Type/Symfony/data/ExampleBaseCommand.php
@@ -3,6 +3,7 @@
 namespace PHPStan\Type\Symfony;
 
 use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use function PHPStan\Testing\assertType;
@@ -14,16 +15,18 @@ protected function configure(): void
 	{
 		parent::configure();
 
+		$this->addArgument('required', InputArgument::REQUIRED);
 		$this->addArgument('base');
 	}
 
 	protected function interact(InputInterface $input, OutputInterface $output): int
 	{
 		assertType('string|null', $input->getArgument('base'));
-		assertType('string|null', $input->getArgument('aaa'));
-		assertType('string|null', $input->getArgument('bbb'));
-		assertType('array<int, string>|string|null', $input->getArgument('diff'));
-		assertType('array<int, string>|null', $input->getArgument('arr'));
+		assertType('string', $input->getArgument('aaa'));
+		assertType('string', $input->getArgument('bbb'));
+		assertType('string|null', $input->getArgument('required'));
+		assertType('array<int, string>|string', $input->getArgument('diff'));
+		assertType('array<int, string>', $input->getArgument('arr'));
 		assertType('string|null', $input->getArgument('both'));
 		assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question'));
 	}
@@ -33,6 +36,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
 		assertType('string|null', $input->getArgument('base'));
 		assertType('string', $input->getArgument('aaa'));
 		assertType('string', $input->getArgument('bbb'));
+		assertType('string', $input->getArgument('required'));
 		assertType('array<int, string>|string', $input->getArgument('diff'));
 		assertType('array<int, string>', $input->getArgument('arr'));
 		assertType('string|null', $input->getArgument('both'));

From 1bd7c339f622dfb5a1a97dcaf1a862734eabfa1d Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Wed, 19 Jun 2024 21:29:16 +0200
Subject: [PATCH 258/297] Also support command argument and initialize method

---
 ...eGetArgumentDynamicReturnTypeExtension.php |  2 +-
 ...eHasArgumentDynamicReturnTypeExtension.php | 12 ++++++++++
 .../Type/Symfony/data/ExampleBaseCommand.php  | 23 ++++++++++++++++++-
 3 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index 860317f6..5eb4cfe9 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -89,7 +89,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 		if (
 			$canBeNullInInteract
 			&& $method instanceof MethodReflection
-			&& $method->getName() === 'interact'
+			&& ($method->getName() === 'interact' || $method->getName() === 'initialize')
 			&& in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true)
 		) {
 			$argTypes[] = new NullType();
diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
index 4ce49c4a..2ca6b4a1 100644
--- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
@@ -13,6 +13,7 @@
 use PHPStan\Type\TypeUtils;
 use function array_unique;
 use function count;
+use function in_array;
 
 final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
@@ -52,6 +53,17 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 		}
 		$argName = $argStrings[0]->getValue();
 
+		if ($argName === 'command') {
+			$method = $scope->getFunction();
+			if (
+				$method instanceof MethodReflection
+				&& ($method->getName() === 'interact' || $method->getName() === 'initialize')
+				&& in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true)
+			) {
+				return null;
+			}
+		}
+
 		$returnTypes = [];
 		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
 			try {
diff --git a/tests/Type/Symfony/data/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php
index d2357089..0376429f 100644
--- a/tests/Type/Symfony/data/ExampleBaseCommand.php
+++ b/tests/Type/Symfony/data/ExampleBaseCommand.php
@@ -19,8 +19,26 @@ protected function configure(): void
 		$this->addArgument('base');
 	}
 
-	protected function interact(InputInterface $input, OutputInterface $output): int
+	protected function initialize(InputInterface $input, OutputInterface $output): void
 	{
+		assertType('bool', $input->hasArgument('command'));
+		assertType('string|null', $input->getArgument('command'));
+
+		assertType('string|null', $input->getArgument('base'));
+		assertType('string', $input->getArgument('aaa'));
+		assertType('string', $input->getArgument('bbb'));
+		assertType('string|null', $input->getArgument('required'));
+		assertType('array<int, string>|string', $input->getArgument('diff'));
+		assertType('array<int, string>', $input->getArgument('arr'));
+		assertType('string|null', $input->getArgument('both'));
+		assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question'));
+	}
+
+	protected function interact(InputInterface $input, OutputInterface $output): void
+	{
+		assertType('bool', $input->hasArgument('command'));
+		assertType('string|null', $input->getArgument('command'));
+
 		assertType('string|null', $input->getArgument('base'));
 		assertType('string', $input->getArgument('aaa'));
 		assertType('string', $input->getArgument('bbb'));
@@ -33,6 +51,9 @@ protected function interact(InputInterface $input, OutputInterface $output): int
 
 	protected function execute(InputInterface $input, OutputInterface $output): int
 	{
+		assertType('true', $input->hasArgument('command'));
+		assertType('string', $input->getArgument('command'));
+
 		assertType('string|null', $input->getArgument('base'));
 		assertType('string', $input->getArgument('aaa'));
 		assertType('string', $input->getArgument('bbb'));

From e909a075d69e0d4db262ac3407350ae2c6b6ab5f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 16 Jul 2024 13:33:56 +0200
Subject: [PATCH 259/297] DiagnoseExtension

---
 composer.json                              |  2 +-
 extension.neon                             |  4 +++
 src/Symfony/ConsoleApplicationResolver.php |  5 ++++
 src/Symfony/SymfonyDiagnoseExtension.php   | 29 ++++++++++++++++++++++
 4 files changed, 39 insertions(+), 1 deletion(-)
 create mode 100644 src/Symfony/SymfonyDiagnoseExtension.php

diff --git a/composer.json b/composer.json
index 2269d9a4..f132544d 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.11"
+    "phpstan/phpstan": "^1.11.7"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index 1eb888ab..e58d668f 100644
--- a/extension.neon
+++ b/extension.neon
@@ -322,6 +322,10 @@ services:
 		class: PHPStan\Symfony\PasswordAuthenticatedUserStubFilesExtension
 		tags:
 			- phpstan.stubFilesExtension
+	-
+		class: PHPStan\Symfony\SymfonyDiagnoseExtension
+		tags:
+			- phpstan.diagnoseExtension
 
 	# FormInterface::getErrors() return type
 	-
diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 51bd7960..52f5f4f0 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -27,6 +27,11 @@ public function __construct(Configuration $configuration)
 		$this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader();
 	}
 
+	public function hasConsoleApplicationLoader(): bool
+	{
+		return $this->consoleApplicationLoader !== null;
+	}
+
 	private function getConsoleApplication(): ?Application
 	{
 		if ($this->consoleApplicationLoader === null) {
diff --git a/src/Symfony/SymfonyDiagnoseExtension.php b/src/Symfony/SymfonyDiagnoseExtension.php
new file mode 100644
index 00000000..afd566dc
--- /dev/null
+++ b/src/Symfony/SymfonyDiagnoseExtension.php
@@ -0,0 +1,29 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Command\Output;
+use PHPStan\Diagnose\DiagnoseExtension;
+use function sprintf;
+
+class SymfonyDiagnoseExtension implements DiagnoseExtension
+{
+
+	/** @var ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+	}
+
+	public function print(Output $output): void
+	{
+		$output->writeLineFormatted(sprintf(
+			'<info>Symfony\'s consoleApplicationLoader:</info> %s',
+			$this->consoleApplicationResolver->hasConsoleApplicationLoader() ? 'In use' : 'No'
+		));
+		$output->writeLineFormatted('');
+	}
+
+}

From ee88a01bc48f608143d3376802ec952270737cb8 Mon Sep 17 00:00:00 2001
From: Jan Nedbal <janedbal@gmail.com>
Date: Thu, 18 Jul 2024 12:19:10 +0200
Subject: [PATCH 260/297] Fix internal error when DIC param map has > 256 items

---
 .../ParameterDynamicReturnTypeExtension.php   |   5 +-
 tests/Type/Symfony/container.xml              | 259 ++++++++++++++++++
 .../data/ExampleAbstractController.php        |   1 +
 3 files changed, 264 insertions(+), 1 deletion(-)

diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 82d3599e..82535133 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -13,6 +13,7 @@
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\BooleanType;
 use PHPStan\Type\Constant\ConstantArrayType;
+use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\ConstantType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
@@ -168,7 +169,9 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type
 					$valueTypes[] = $this->generalizeTypeFromValue($scope, $element);
 				}
 
-				return new ConstantArrayType($keyTypes, $valueTypes);
+				return ConstantArrayTypeBuilder::createFromConstantArray(
+					new ConstantArrayType($keyTypes, $valueTypes)
+				)->getArray();
 			}
 
 			return new ArrayType(
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index e1ef6d89..224c72db 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -82,6 +82,265 @@
             <parameter key="b">value of b</parameter>
             <parameter key="c">value of c</parameter>
         </parameter>
+        <parameter key="app.hugemap" type="collection">
+            <parameter key="k1">v1</parameter>
+            <parameter key="k2">v2</parameter>
+            <parameter key="k3">v3</parameter>
+            <parameter key="k4">v4</parameter>
+            <parameter key="k5">v5</parameter>
+            <parameter key="k6">v6</parameter>
+            <parameter key="k7">v7</parameter>
+            <parameter key="k8">v8</parameter>
+            <parameter key="k9">v9</parameter>
+            <parameter key="k10">v10</parameter>
+            <parameter key="k11">v11</parameter>
+            <parameter key="k12">v12</parameter>
+            <parameter key="k13">v13</parameter>
+            <parameter key="k14">v14</parameter>
+            <parameter key="k15">v15</parameter>
+            <parameter key="k16">v16</parameter>
+            <parameter key="k17">v17</parameter>
+            <parameter key="k18">v18</parameter>
+            <parameter key="k19">v19</parameter>
+            <parameter key="k20">v20</parameter>
+            <parameter key="k21">v21</parameter>
+            <parameter key="k22">v22</parameter>
+            <parameter key="k23">v23</parameter>
+            <parameter key="k24">v24</parameter>
+            <parameter key="k25">v25</parameter>
+            <parameter key="k26">v26</parameter>
+            <parameter key="k27">v27</parameter>
+            <parameter key="k28">v28</parameter>
+            <parameter key="k29">v29</parameter>
+            <parameter key="k30">v30</parameter>
+            <parameter key="k31">v31</parameter>
+            <parameter key="k32">v32</parameter>
+            <parameter key="k33">v33</parameter>
+            <parameter key="k34">v34</parameter>
+            <parameter key="k35">v35</parameter>
+            <parameter key="k36">v36</parameter>
+            <parameter key="k37">v37</parameter>
+            <parameter key="k38">v38</parameter>
+            <parameter key="k39">v39</parameter>
+            <parameter key="k40">v40</parameter>
+            <parameter key="k41">v41</parameter>
+            <parameter key="k42">v42</parameter>
+            <parameter key="k43">v43</parameter>
+            <parameter key="k44">v44</parameter>
+            <parameter key="k45">v45</parameter>
+            <parameter key="k46">v46</parameter>
+            <parameter key="k47">v47</parameter>
+            <parameter key="k48">v48</parameter>
+            <parameter key="k49">v49</parameter>
+            <parameter key="k50">v50</parameter>
+            <parameter key="k51">v51</parameter>
+            <parameter key="k52">v52</parameter>
+            <parameter key="k53">v53</parameter>
+            <parameter key="k54">v54</parameter>
+            <parameter key="k55">v55</parameter>
+            <parameter key="k56">v56</parameter>
+            <parameter key="k57">v57</parameter>
+            <parameter key="k58">v58</parameter>
+            <parameter key="k59">v59</parameter>
+            <parameter key="k60">v60</parameter>
+            <parameter key="k61">v61</parameter>
+            <parameter key="k62">v62</parameter>
+            <parameter key="k63">v63</parameter>
+            <parameter key="k64">v64</parameter>
+            <parameter key="k65">v65</parameter>
+            <parameter key="k66">v66</parameter>
+            <parameter key="k67">v67</parameter>
+            <parameter key="k68">v68</parameter>
+            <parameter key="k69">v69</parameter>
+            <parameter key="k70">v70</parameter>
+            <parameter key="k71">v71</parameter>
+            <parameter key="k72">v72</parameter>
+            <parameter key="k73">v73</parameter>
+            <parameter key="k74">v74</parameter>
+            <parameter key="k75">v75</parameter>
+            <parameter key="k76">v76</parameter>
+            <parameter key="k77">v77</parameter>
+            <parameter key="k78">v78</parameter>
+            <parameter key="k79">v79</parameter>
+            <parameter key="k80">v80</parameter>
+            <parameter key="k81">v81</parameter>
+            <parameter key="k82">v82</parameter>
+            <parameter key="k83">v83</parameter>
+            <parameter key="k84">v84</parameter>
+            <parameter key="k85">v85</parameter>
+            <parameter key="k86">v86</parameter>
+            <parameter key="k87">v87</parameter>
+            <parameter key="k88">v88</parameter>
+            <parameter key="k89">v89</parameter>
+            <parameter key="k90">v90</parameter>
+            <parameter key="k91">v91</parameter>
+            <parameter key="k92">v92</parameter>
+            <parameter key="k93">v93</parameter>
+            <parameter key="k94">v94</parameter>
+            <parameter key="k95">v95</parameter>
+            <parameter key="k96">v96</parameter>
+            <parameter key="k97">v97</parameter>
+            <parameter key="k98">v98</parameter>
+            <parameter key="k99">v99</parameter>
+            <parameter key="k100">v100</parameter>
+            <parameter key="k101">v101</parameter>
+            <parameter key="k102">v102</parameter>
+            <parameter key="k103">v103</parameter>
+            <parameter key="k104">v104</parameter>
+            <parameter key="k105">v105</parameter>
+            <parameter key="k106">v106</parameter>
+            <parameter key="k107">v107</parameter>
+            <parameter key="k108">v108</parameter>
+            <parameter key="k109">v109</parameter>
+            <parameter key="k110">v110</parameter>
+            <parameter key="k111">v111</parameter>
+            <parameter key="k112">v112</parameter>
+            <parameter key="k113">v113</parameter>
+            <parameter key="k114">v114</parameter>
+            <parameter key="k115">v115</parameter>
+            <parameter key="k116">v116</parameter>
+            <parameter key="k117">v117</parameter>
+            <parameter key="k118">v118</parameter>
+            <parameter key="k119">v119</parameter>
+            <parameter key="k120">v120</parameter>
+            <parameter key="k121">v121</parameter>
+            <parameter key="k122">v122</parameter>
+            <parameter key="k123">v123</parameter>
+            <parameter key="k124">v124</parameter>
+            <parameter key="k125">v125</parameter>
+            <parameter key="k126">v126</parameter>
+            <parameter key="k127">v127</parameter>
+            <parameter key="k128">v128</parameter>
+            <parameter key="k129">v129</parameter>
+            <parameter key="k130">v130</parameter>
+            <parameter key="k131">v131</parameter>
+            <parameter key="k132">v132</parameter>
+            <parameter key="k133">v133</parameter>
+            <parameter key="k134">v134</parameter>
+            <parameter key="k135">v135</parameter>
+            <parameter key="k136">v136</parameter>
+            <parameter key="k137">v137</parameter>
+            <parameter key="k138">v138</parameter>
+            <parameter key="k139">v139</parameter>
+            <parameter key="k140">v140</parameter>
+            <parameter key="k141">v141</parameter>
+            <parameter key="k142">v142</parameter>
+            <parameter key="k143">v143</parameter>
+            <parameter key="k144">v144</parameter>
+            <parameter key="k145">v145</parameter>
+            <parameter key="k146">v146</parameter>
+            <parameter key="k147">v147</parameter>
+            <parameter key="k148">v148</parameter>
+            <parameter key="k149">v149</parameter>
+            <parameter key="k150">v150</parameter>
+            <parameter key="k151">v151</parameter>
+            <parameter key="k152">v152</parameter>
+            <parameter key="k153">v153</parameter>
+            <parameter key="k154">v154</parameter>
+            <parameter key="k155">v155</parameter>
+            <parameter key="k156">v156</parameter>
+            <parameter key="k157">v157</parameter>
+            <parameter key="k158">v158</parameter>
+            <parameter key="k159">v159</parameter>
+            <parameter key="k160">v160</parameter>
+            <parameter key="k161">v161</parameter>
+            <parameter key="k162">v162</parameter>
+            <parameter key="k163">v163</parameter>
+            <parameter key="k164">v164</parameter>
+            <parameter key="k165">v165</parameter>
+            <parameter key="k166">v166</parameter>
+            <parameter key="k167">v167</parameter>
+            <parameter key="k168">v168</parameter>
+            <parameter key="k169">v169</parameter>
+            <parameter key="k170">v170</parameter>
+            <parameter key="k171">v171</parameter>
+            <parameter key="k172">v172</parameter>
+            <parameter key="k173">v173</parameter>
+            <parameter key="k174">v174</parameter>
+            <parameter key="k175">v175</parameter>
+            <parameter key="k176">v176</parameter>
+            <parameter key="k177">v177</parameter>
+            <parameter key="k178">v178</parameter>
+            <parameter key="k179">v179</parameter>
+            <parameter key="k180">v180</parameter>
+            <parameter key="k181">v181</parameter>
+            <parameter key="k182">v182</parameter>
+            <parameter key="k183">v183</parameter>
+            <parameter key="k184">v184</parameter>
+            <parameter key="k185">v185</parameter>
+            <parameter key="k186">v186</parameter>
+            <parameter key="k187">v187</parameter>
+            <parameter key="k188">v188</parameter>
+            <parameter key="k189">v189</parameter>
+            <parameter key="k190">v190</parameter>
+            <parameter key="k191">v191</parameter>
+            <parameter key="k192">v192</parameter>
+            <parameter key="k193">v193</parameter>
+            <parameter key="k194">v194</parameter>
+            <parameter key="k195">v195</parameter>
+            <parameter key="k196">v196</parameter>
+            <parameter key="k197">v197</parameter>
+            <parameter key="k198">v198</parameter>
+            <parameter key="k199">v199</parameter>
+            <parameter key="k200">v200</parameter>
+            <parameter key="k201">v201</parameter>
+            <parameter key="k202">v202</parameter>
+            <parameter key="k203">v203</parameter>
+            <parameter key="k204">v204</parameter>
+            <parameter key="k205">v205</parameter>
+            <parameter key="k206">v206</parameter>
+            <parameter key="k207">v207</parameter>
+            <parameter key="k208">v208</parameter>
+            <parameter key="k209">v209</parameter>
+            <parameter key="k210">v210</parameter>
+            <parameter key="k211">v211</parameter>
+            <parameter key="k212">v212</parameter>
+            <parameter key="k213">v213</parameter>
+            <parameter key="k214">v214</parameter>
+            <parameter key="k215">v215</parameter>
+            <parameter key="k216">v216</parameter>
+            <parameter key="k217">v217</parameter>
+            <parameter key="k218">v218</parameter>
+            <parameter key="k219">v219</parameter>
+            <parameter key="k220">v220</parameter>
+            <parameter key="k221">v221</parameter>
+            <parameter key="k222">v222</parameter>
+            <parameter key="k223">v223</parameter>
+            <parameter key="k224">v224</parameter>
+            <parameter key="k225">v225</parameter>
+            <parameter key="k226">v226</parameter>
+            <parameter key="k227">v227</parameter>
+            <parameter key="k228">v228</parameter>
+            <parameter key="k229">v229</parameter>
+            <parameter key="k230">v230</parameter>
+            <parameter key="k231">v231</parameter>
+            <parameter key="k232">v232</parameter>
+            <parameter key="k233">v233</parameter>
+            <parameter key="k234">v234</parameter>
+            <parameter key="k235">v235</parameter>
+            <parameter key="k236">v236</parameter>
+            <parameter key="k237">v237</parameter>
+            <parameter key="k238">v238</parameter>
+            <parameter key="k239">v239</parameter>
+            <parameter key="k240">v240</parameter>
+            <parameter key="k241">v241</parameter>
+            <parameter key="k242">v242</parameter>
+            <parameter key="k243">v243</parameter>
+            <parameter key="k244">v244</parameter>
+            <parameter key="k245">v245</parameter>
+            <parameter key="k246">v246</parameter>
+            <parameter key="k247">v247</parameter>
+            <parameter key="k248">v248</parameter>
+            <parameter key="k249">v249</parameter>
+            <parameter key="k250">v250</parameter>
+            <parameter key="k251">v251</parameter>
+            <parameter key="k252">v252</parameter>
+            <parameter key="k253">v253</parameter>
+            <parameter key="k254">v254</parameter>
+            <parameter key="k255">v255</parameter>
+            <parameter key="k256">v256</parameter>
+            <parameter key="k257">v257</parameter>
+        </parameter>
         <parameter key="app.binary" type="binary">VGhpcyBpcyBhIEJlbGwgY2hhciAH</parameter>
         <parameter key="app.constant">Y-m-d\TH:i:sP</parameter>
 		<parameter key="test_collection" type="collection"/>
diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php
index d9857a53..53b38066 100644
--- a/tests/Type/Symfony/data/ExampleAbstractController.php
+++ b/tests/Type/Symfony/data/ExampleAbstractController.php
@@ -80,6 +80,7 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
 		assertType("array{a: string, b: string, c: string}", $container->getParameter('app.map'));
 		assertType("array{a: string, b: string, c: string}", $parameterBag->get('app.map'));
 		assertType("array{a: string, b: string, c: string}", $this->getParameter('app.map'));
+		assertType("non-falsy-string", implode(',', $this->getParameter('app.hugemap')));
 		assertType("string", $container->getParameter('app.binary'));
 		assertType("string", $parameterBag->get('app.binary'));
 		assertType("string", $this->getParameter('app.binary'));

From 14eec8c011b856eee4d744a2a3f709db1e1858bd Mon Sep 17 00:00:00 2001
From: Zachary Lund <git@lundscape.com>
Date: Mon, 12 Aug 2024 11:03:46 -0500
Subject: [PATCH 261/297] Add stub for AbstractController::createForm()

---
 extension.neon                                |  2 ++
 .../Controller/AbstractController.stub        | 24 +++++++++++++++++++
 .../Service/ServiceSubscriberInterface.stub   |  7 ++++++
 tests/Type/Symfony/data/form_data_type.php    | 18 ++++++++++++++
 4 files changed, 51 insertions(+)
 create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub
 create mode 100644 stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub

diff --git a/extension.neon b/extension.neon
index e58d668f..512f9908 100644
--- a/extension.neon
+++ b/extension.neon
@@ -27,6 +27,7 @@ parameters:
 		- stubs/Psr/Cache/CacheException.stub
 		- stubs/Psr/Cache/CacheItemInterface.stub
 		- stubs/Psr/Cache/InvalidArgumentException.stub
+		- stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
@@ -103,6 +104,7 @@ parameters:
 		- stubs/Symfony/Contracts/Cache/CacheInterface.stub
 		- stubs/Symfony/Contracts/Cache/CallbackInterface.stub
 		- stubs/Symfony/Contracts/Cache/ItemInterface.stub
+		- stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub
 		- stubs/Twig/Node/Node.stub
 
 parametersSchema:
diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub b/stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub
new file mode 100644
index 00000000..075dce6d
--- /dev/null
+++ b/stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub
@@ -0,0 +1,24 @@
+<?php
+
+namespace Symfony\Bundle\FrameworkBundle\Controller;
+
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\FormTypeInterface;
+use Symfony\Contracts\Service\ServiceSubscriberInterface;
+
+abstract class AbstractController implements ServiceSubscriberInterface
+{
+	/**
+	 * @template TFormType of FormTypeInterface<TData>
+	 * @template TData
+	 *
+	 * @param class-string<TFormType> $type
+	 * @param TData                   $data
+	 * @param array<string, mixed>    $options
+	 *
+	 * @phpstan-return ($data is null ? FormInterface<null|TData> : FormInterface<TData>)
+	 */
+	protected function createForm(string $type, $data = null, array $options = []): FormInterface
+	{
+	}
+}
diff --git a/stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub b/stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub
new file mode 100644
index 00000000..8860e239
--- /dev/null
+++ b/stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Contracts\Service;
+
+interface ServiceSubscriberInterface
+{
+}
diff --git a/tests/Type/Symfony/data/form_data_type.php b/tests/Type/Symfony/data/form_data_type.php
index e0cefb88..34a673a4 100644
--- a/tests/Type/Symfony/data/form_data_type.php
+++ b/tests/Type/Symfony/data/form_data_type.php
@@ -2,6 +2,7 @@
 
 namespace GenericFormDataType;
 
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 use Symfony\Component\Form\AbstractType;
 use Symfony\Component\Form\Extension\Core\Type\NumberType;
 use Symfony\Component\Form\Extension\Core\Type\TextType;
@@ -73,3 +74,20 @@ public function doSomethingNullable(): void
 	}
 
 }
+
+class FormController extends AbstractController
+{
+
+	public function doSomething(): void
+	{
+		$form = $this->createForm(DataClassType::class, new DataClass());
+		assertType('GenericFormDataType\DataClass', $form->getData());
+	}
+
+	public function doSomethingNullable(): void
+	{
+		$form = $this->createForm(DataClassType::class);
+		assertType('GenericFormDataType\DataClass|null', $form->getData());
+	}
+
+}

From 3cf113fa3e190ab9ab47b0aed46f32a36dfc2c7a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 5 Sep 2024 13:39:28 +0200
Subject: [PATCH 262/297] Fix stubs

---
 .../Component/EventDispatcher/EventDispatcherInterface.stub   | 2 +-
 stubs/Symfony/Component/HttpFoundation/Cookie.stub            | 4 ++--
 .../Serializer/Exception/ExtraAttributesException.stub        | 2 +-
 stubs/Symfony/Contracts/Cache/CacheInterface.stub             | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
index e4ad8fc4..a58e43ca 100644
--- a/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
+++ b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub
@@ -10,5 +10,5 @@ interface EventDispatcherInterface
      *
      * @return TEvent
      */
-    public function dispatch(object $event, string $eventName = null): object;
+    public function dispatch(object $event, ?string $eventName = null): object;
 }
diff --git a/stubs/Symfony/Component/HttpFoundation/Cookie.stub b/stubs/Symfony/Component/HttpFoundation/Cookie.stub
index 40ee45ab..cfb45fa3 100644
--- a/stubs/Symfony/Component/HttpFoundation/Cookie.stub
+++ b/stubs/Symfony/Component/HttpFoundation/Cookie.stub
@@ -21,7 +21,7 @@ class Cookie
      *
      * @throws \InvalidArgumentException
      */
-    public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null);
+    public function __construct(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, ?string $sameSite = null);
 
     /**
      * @param string                        $name     The name of the cookie
@@ -36,7 +36,7 @@ class Cookie
      *
      * @throws \InvalidArgumentException
      */
-    public function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null): self;
+    public function create(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, ?string $sameSite = null): self;
 
     /**
      * @return self::SAMESITE_*|null
diff --git a/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub b/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub
index 18621931..b23f0d19 100644
--- a/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub
+++ b/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub
@@ -7,7 +7,7 @@ class ExtraAttributesException extends RuntimeException
     /**
      * @param string[] $extraAttributes
      */
-    public function __construct(array $extraAttributes, \Throwable $previous = null)
+    public function __construct(array $extraAttributes, ?\Throwable $previous = null)
     {
     }
 
diff --git a/stubs/Symfony/Contracts/Cache/CacheInterface.stub b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
index ff3027b1..a361ead4 100644
--- a/stubs/Symfony/Contracts/Cache/CacheInterface.stub
+++ b/stubs/Symfony/Contracts/Cache/CacheInterface.stub
@@ -15,5 +15,5 @@ interface CacheInterface
      *
      * @throws InvalidArgumentException
      */
-    public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);
+    public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null);
 }

From 42797536c3de1531769a725bfbb404f220ae0516 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 5 Sep 2024 18:13:37 +0200
Subject: [PATCH 263/297] Test newer PHP versions

---
 .github/workflows/build.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b4453157..c4f7c922 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,6 +22,8 @@ jobs:
           - "8.0"
           - "8.1"
           - "8.2"
+          - "8.3"
+          - "8.4"
 
     steps:
       - name: "Checkout"
@@ -93,6 +95,8 @@ jobs:
           - "8.0"
           - "8.1"
           - "8.2"
+          - "8.3"
+          - "8.4"
         dependencies:
           - "lowest"
           - "highest"
@@ -132,6 +136,8 @@ jobs:
           - "8.0"
           - "8.1"
           - "8.2"
+          - "8.3"
+          - "8.4"
         dependencies:
           - "lowest"
           - "highest"

From 30d088616836b108fbfc634211c2feae130ae42e Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 5 Sep 2024 18:13:54 +0200
Subject: [PATCH 264/297] Pin build-cs

---
 .github/workflows/build.yml | 1 +
 Makefile                    | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c4f7c922..ad0554fb 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -58,6 +58,7 @@ jobs:
         with:
           repository: "phpstan/build-cs"
           path: "build-cs"
+          ref: "1.x"
 
       - name: "Install PHP"
         uses: "shivammathur/setup-php@v2"
diff --git a/Makefile b/Makefile
index ecd8cfb2..b01b1537 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,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/main
+	git -C build-cs fetch origin && git -C build-cs reset --hard origin/1.x
 	composer install --working-dir build-cs
 
 .PHONY: cs

From 51ab2438fb2695467cf96b58d2f8f28d4dd1e3e9 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 5 Sep 2024 18:15:09 +0200
Subject: [PATCH 265/297] Require PHPStan 1.12

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index f132544d..0d5cd41c 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.2 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.11.7"
+    "phpstan/phpstan": "^1.12"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"

From 2c53b3802664ade95c0b12735b7b923ba52a545c Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 6 Sep 2024 14:20:56 +0200
Subject: [PATCH 266/297] Open 2.0.x

---
 .github/workflows/build.yml |  2 +-
 composer.json               | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ad0554fb..df52d534 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
   pull_request:
   push:
     branches:
-      - "1.4.x"
+      - "2.0.x"
 
 jobs:
   lint:
diff --git a/composer.json b/composer.json
index 0d5cd41c..ffb9f3b8 100644
--- a/composer.json
+++ b/composer.json
@@ -13,9 +13,9 @@
     }
   ],
   "require": {
-    "php": "^7.2 || ^8.0",
+    "php": "^7.4 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^1.12"
+    "phpstan/phpstan": "^2.0"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
@@ -23,9 +23,9 @@
   "require-dev": {
     "nikic/php-parser": "^4.13.0",
     "php-parallel-lint/php-parallel-lint": "^1.2",
-    "phpstan/phpstan-phpunit": "^1.3.11",
-    "phpstan/phpstan-strict-rules": "^1.5.1",
-    "phpunit/phpunit": "^8.5.29 || ^9.5",
+    "phpstan/phpstan-phpunit": "^2.0",
+    "phpstan/phpstan-strict-rules": "^2.0",
+    "phpunit/phpunit": "^9.6",
     "psr/container": "1.0 || 1.1.1",
     "symfony/config": "^5.4 || ^6.1",
     "symfony/console": "^5.4 || ^6.1",

From 2f736a628af8d5059cf0fc831eb96771a9669d86 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 6 Sep 2024 14:21:26 +0200
Subject: [PATCH 267/297] Stop testing PHP 7.2 and 7.3

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

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index df52d534..8d659b18 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"
@@ -90,8 +88,6 @@ jobs:
       fail-fast: false
       matrix:
         php-version:
-          - "7.2"
-          - "7.3"
           - "7.4"
           - "8.0"
           - "8.1"
@@ -131,8 +127,6 @@ jobs:
       fail-fast: false
       matrix:
         php-version:
-          - "7.2"
-          - "7.3"
           - "7.4"
           - "8.0"
           - "8.1"

From 90e89765c80dd91483968dca9c3f6e124784e59f Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 6 Sep 2024 14:21:44 +0200
Subject: [PATCH 268/297] Update build-cs

---
 .github/workflows/build.yml                   |  2 +-
 Makefile                                      |  2 +-
 .../ContainerInterfacePrivateServiceRule.php  |  3 +-
 .../ContainerInterfaceUnknownServiceRule.php  |  6 ++--
 .../InvalidArgumentDefaultValueRule.php       |  4 +--
 .../Symfony/InvalidOptionDefaultValueRule.php |  4 +--
 src/Rules/Symfony/UndefinedArgumentRule.php   |  6 ++--
 src/Rules/Symfony/UndefinedOptionRule.php     |  6 ++--
 src/Symfony/Configuration.php                 |  2 +-
 src/Symfony/ConsoleApplicationResolver.php    |  6 ++--
 src/Symfony/DefaultParameterMap.php           |  6 ++--
 src/Symfony/DefaultServiceMap.php             |  2 +-
 src/Symfony/InputBagStubFilesExtension.php    |  3 +-
 src/Symfony/Parameter.php                     |  3 +-
 ...ordAuthenticatedUserStubFilesExtension.php |  3 +-
 src/Symfony/RequiredAutowiringExtension.php   |  3 +-
 src/Symfony/Service.php                       | 15 +++------
 src/Symfony/SymfonyDiagnoseExtension.php      |  5 ++-
 src/Symfony/XmlParameterMapFactory.php        |  5 ++-
 src/Symfony/XmlServiceMapFactory.php          |  7 ++--
 .../ArgumentTypeSpecifyingExtension.php       |  8 ++---
 ...InterfaceGetDynamicReturnTypeExtension.php |  2 +-
 ...andGetHelperDynamicReturnTypeExtension.php |  3 +-
 ...ionPrototypeDynamicReturnTypeExtension.php |  2 +-
 ...ParentObjectDynamicReturnTypeExtension.php |  5 ++-
 ...ReturnParentDynamicReturnTypeExtension.php |  5 ++-
 ...rGetRootNodeDynamicReturnTypeExtension.php |  2 +-
 .../Config/ValueObject/ParentObjectType.php   |  3 +-
 .../Config/ValueObject/TreeBuilderType.php    |  3 +-
 .../Symfony/EnvelopeReturnTypeExtension.php   |  2 +-
 ...ionGetConfigurationReturnTypeExtension.php |  3 +-
 src/Type/Symfony/Helper.php                   |  2 +-
 .../InputBagTypeSpecifyingExtension.php       |  5 ++-
 ...eGetArgumentDynamicReturnTypeExtension.php |  3 +-
 ...aceGetOptionDynamicReturnTypeExtension.php |  6 ++--
 ...ceGetOptionsDynamicReturnTypeExtension.php |  6 ++--
 ...eHasArgumentDynamicReturnTypeExtension.php |  3 +-
 ...aceHasOptionDynamicReturnTypeExtension.php |  3 +-
 .../Symfony/OptionTypeSpecifyingExtension.php |  8 ++---
 .../ParameterDynamicReturnTypeExtension.php   | 32 ++++++-------------
 .../RequestTypeSpecifyingExtension.php        |  5 ++-
 ...nseHeaderBagDynamicReturnTypeExtension.php |  6 ++--
 .../SerializerDynamicReturnTypeExtension.php  |  6 ++--
 .../ServiceDynamicReturnTypeExtension.php     | 15 +++------
 .../ServiceTypeSpecifyingExtension.php        | 11 +++----
 ...nerInterfacePrivateServiceRuleFakeTest.php |  8 ++---
 ...ntainerInterfacePrivateServiceRuleTest.php |  6 ++--
 ...nerInterfaceUnknownServiceRuleFakeTest.php |  4 +--
 ...ntainerInterfaceUnknownServiceRuleTest.php |  6 ++--
 .../Symfony/ExampleServiceSubscriber.php      |  3 +-
 .../InvalidArgumentDefaultValueRuleTest.php   |  2 +-
 .../InvalidOptionDefaultValueRuleTest.php     |  2 +-
 .../Symfony/UndefinedArgumentRuleTest.php     |  2 +-
 .../Rules/Symfony/UndefinedOptionRuleTest.php |  2 +-
 .../Symfony/console_application_loader.php    |  4 +--
 55 files changed, 107 insertions(+), 174 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8d659b18..88543fb5 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 b01b1537..1ee557df 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,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/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 996d3b77..5d935859 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -19,8 +19,7 @@
 final class ContainerInterfacePrivateServiceRule implements Rule
 {
 
-	/** @var ServiceMap */
-	private $serviceMap;
+	private ServiceMap $serviceMap;
 
 	public function __construct(ServiceMap $symfonyServiceMap)
 	{
diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index fc7d9585..ec1a70e0 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -19,11 +19,9 @@
 final class ContainerInterfaceUnknownServiceRule implements Rule
 {
 
-	/** @var ServiceMap */
-	private $serviceMap;
+	private ServiceMap $serviceMap;
 
-	/** @var Standard */
-	private $printer;
+	private Standard $printer;
 
 	public function __construct(ServiceMap $symfonyServiceMap, Standard $printer)
 	{
diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index 153d4af5..27643a7d 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -62,7 +62,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [
 				RuleErrorBuilder::message(sprintf(
 					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.',
-					$defaultType->describe(VerbosityLevel::typeOnly())
+					$defaultType->describe(VerbosityLevel::typeOnly()),
 				))->identifier('argument.type')->build(),
 			];
 		}
@@ -72,7 +72,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [
 				RuleErrorBuilder::message(sprintf(
 					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array<int, string>|null, %s given.',
-					$defaultType->describe(VerbosityLevel::typeOnly())
+					$defaultType->describe(VerbosityLevel::typeOnly()),
 				))->identifier('argument.type')->build(),
 			];
 		}
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index 1595f4a8..152514f1 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -67,7 +67,7 @@ public function processNode(Node $node, Scope $scope): array
 					RuleErrorBuilder::message(sprintf(
 						'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.',
 						$checkType->describe(VerbosityLevel::typeOnly()),
-						$defaultType->describe(VerbosityLevel::typeOnly())
+						$defaultType->describe(VerbosityLevel::typeOnly()),
 					))->identifier('argument.type')->build(),
 				];
 			}
@@ -78,7 +78,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [
 				RuleErrorBuilder::message(sprintf(
 					'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array<string>|null, %s given.',
-					$defaultType->describe(VerbosityLevel::typeOnly())
+					$defaultType->describe(VerbosityLevel::typeOnly()),
 				))->identifier('argument.type')->build(),
 			];
 		}
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index cd8be894..ff997be5 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -22,11 +22,9 @@
 final class UndefinedArgumentRule implements Rule
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
-	/** @var Standard */
-	private $printer;
+	private Standard $printer;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
 	{
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index 095bd44d..ae96bfa9 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -22,11 +22,9 @@
 final class UndefinedOptionRule implements Rule
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
-	/** @var Standard */
-	private $printer;
+	private Standard $printer;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
 	{
diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php
index 4c1f1a31..e603b333 100644
--- a/src/Symfony/Configuration.php
+++ b/src/Symfony/Configuration.php
@@ -6,7 +6,7 @@ final class Configuration
 {
 
 	/** @var array<string, mixed> */
-	private $parameters;
+	private array $parameters;
 
 	/**
 	 * @param array<string, mixed> $parameters
diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index 52f5f4f0..a29740b7 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -16,11 +16,9 @@
 final class ConsoleApplicationResolver
 {
 
-	/** @var string|null */
-	private $consoleApplicationLoader;
+	private ?string $consoleApplicationLoader = null;
 
-	/** @var Application|null */
-	private $consoleApplication;
+	private ?Application $consoleApplication = null;
 
 	public function __construct(Configuration $configuration)
 	{
diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php
index 26317468..8d0af459 100644
--- a/src/Symfony/DefaultParameterMap.php
+++ b/src/Symfony/DefaultParameterMap.php
@@ -12,7 +12,7 @@ final class DefaultParameterMap implements ParameterMap
 {
 
 	/** @var ParameterDefinition[] */
-	private $parameters;
+	private array $parameters;
 
 	/**
 	 * @param ParameterDefinition[] $parameters
@@ -39,9 +39,7 @@ public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
 	{
 		$strings = TypeUtils::getConstantStrings($scope->getType($node));
 
-		return array_map(static function (Type $type) {
-			return $type->getValue();
-		}, $strings);
+		return array_map(static fn (Type $type) => $type->getValue(), $strings);
 	}
 
 }
diff --git a/src/Symfony/DefaultServiceMap.php b/src/Symfony/DefaultServiceMap.php
index cb8259d2..edf4dfd6 100644
--- a/src/Symfony/DefaultServiceMap.php
+++ b/src/Symfony/DefaultServiceMap.php
@@ -11,7 +11,7 @@ final class DefaultServiceMap implements ServiceMap
 {
 
 	/** @var ServiceDefinition[] */
-	private $services;
+	private array $services;
 
 	/**
 	 * @param ServiceDefinition[] $services
diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php
index 140dae99..6ce36f4b 100644
--- a/src/Symfony/InputBagStubFilesExtension.php
+++ b/src/Symfony/InputBagStubFilesExtension.php
@@ -9,8 +9,7 @@
 class InputBagStubFilesExtension implements StubFilesExtension
 {
 
-	/** @var Reflector */
-	private $reflector;
+	private Reflector $reflector;
 
 	public function __construct(
 		Reflector $reflector
diff --git a/src/Symfony/Parameter.php b/src/Symfony/Parameter.php
index 8ff3f7a8..53b53265 100644
--- a/src/Symfony/Parameter.php
+++ b/src/Symfony/Parameter.php
@@ -5,8 +5,7 @@
 final class Parameter implements ParameterDefinition
 {
 
-	/** @var string */
-	private $key;
+	private string $key;
 
 	/** @var array<mixed>|bool|float|int|string */
 	private $value;
diff --git a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
index 8f8c4782..42bbf9e7 100644
--- a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
+++ b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
@@ -9,8 +9,7 @@
 class PasswordAuthenticatedUserStubFilesExtension implements StubFilesExtension
 {
 
-	/** @var Reflector */
-	private $reflector;
+	private Reflector $reflector;
 
 	public function __construct(
 		Reflector $reflector
diff --git a/src/Symfony/RequiredAutowiringExtension.php b/src/Symfony/RequiredAutowiringExtension.php
index a3cc6a31..7d8d195d 100644
--- a/src/Symfony/RequiredAutowiringExtension.php
+++ b/src/Symfony/RequiredAutowiringExtension.php
@@ -13,8 +13,7 @@
 class RequiredAutowiringExtension implements ReadWritePropertiesExtension, AdditionalConstructorsExtension
 {
 
-	/** @var FileTypeMapper */
-	private $fileTypeMapper;
+	private FileTypeMapper $fileTypeMapper;
 
 	public function __construct(FileTypeMapper $fileTypeMapper)
 	{
diff --git a/src/Symfony/Service.php b/src/Symfony/Service.php
index c31324f5..881787f5 100644
--- a/src/Symfony/Service.php
+++ b/src/Symfony/Service.php
@@ -5,20 +5,15 @@
 final class Service implements ServiceDefinition
 {
 
-	/** @var string */
-	private $id;
+	private string $id;
 
-	/** @var string|null */
-	private $class;
+	private ?string $class = null;
 
-	/** @var bool */
-	private $public;
+	private bool $public;
 
-	/** @var bool */
-	private $synthetic;
+	private bool $synthetic;
 
-	/** @var string|null */
-	private $alias;
+	private ?string $alias = null;
 
 	public function __construct(
 		string $id,
diff --git a/src/Symfony/SymfonyDiagnoseExtension.php b/src/Symfony/SymfonyDiagnoseExtension.php
index afd566dc..38b19754 100644
--- a/src/Symfony/SymfonyDiagnoseExtension.php
+++ b/src/Symfony/SymfonyDiagnoseExtension.php
@@ -9,8 +9,7 @@
 class SymfonyDiagnoseExtension implements DiagnoseExtension
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
 	{
@@ -21,7 +20,7 @@ public function print(Output $output): void
 	{
 		$output->writeLineFormatted(sprintf(
 			'<info>Symfony\'s consoleApplicationLoader:</info> %s',
-			$this->consoleApplicationResolver->hasConsoleApplicationLoader() ? 'In use' : 'No'
+			$this->consoleApplicationResolver->hasConsoleApplicationLoader() ? 'In use' : 'No',
 		));
 		$output->writeLineFormatted('');
 	}
diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php
index 1467d050..afb58ba8 100644
--- a/src/Symfony/XmlParameterMapFactory.php
+++ b/src/Symfony/XmlParameterMapFactory.php
@@ -15,8 +15,7 @@
 final class XmlParameterMapFactory implements ParameterMapFactory
 {
 
-	/** @var string|null */
-	private $containerXml;
+	private ?string $containerXml = null;
 
 	public function __construct(Configuration $configuration)
 	{
@@ -47,7 +46,7 @@ public function create(): ParameterMap
 
 			$parameter = new Parameter(
 				(string) $attrs->key,
-				$this->getNodeValue($def)
+				$this->getNodeValue($def),
 			);
 
 			$parameters[$parameter->getKey()] = $parameter;
diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 1cae5d97..8d2ba919 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -12,8 +12,7 @@
 final class XmlServiceMapFactory implements ServiceMapFactory
 {
 
-	/** @var string|null */
-	private $containerXml;
+	private ?string $containerXml = null;
 
 	public function __construct(Configuration $configuration)
 	{
@@ -52,7 +51,7 @@ public function create(): ServiceMap
 				isset($attrs->class) ? (string) $attrs->class : null,
 				isset($attrs->public) && (string) $attrs->public === 'true',
 				isset($attrs->synthetic) && (string) $attrs->synthetic === 'true',
-				isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null
+				isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null,
 			);
 
 			if ($service->getAlias() !== null) {
@@ -72,7 +71,7 @@ public function create(): ServiceMap
 				$services[$alias]->getClass(),
 				$service->isPublic(),
 				$service->isSynthetic(),
-				$alias
+				$alias,
 			);
 		}
 
diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
index b57f3c35..f9aafb23 100644
--- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
@@ -15,11 +15,9 @@
 final class ArgumentTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	/** @var Standard */
-	private $printer;
+	private Standard $printer;
 
-	/** @var TypeSpecifier */
-	private $typeSpecifier;
+	private TypeSpecifier $typeSpecifier;
 
 	public function __construct(Standard $printer)
 	{
@@ -45,7 +43,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 		return $this->typeSpecifier->create(
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
-			$context
+			$context,
 		);
 	}
 
diff --git a/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php
index ce736bfe..0862ce61 100644
--- a/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php
@@ -34,7 +34,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
 				$scope,
 				$methodCall->getArgs(),
-				$callbackReturnType->getCallableParametersAcceptors($scope)
+				$callbackReturnType->getCallableParametersAcceptors($scope),
 			);
 			$returnType = $parametersAcceptor->getReturnType();
 
diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
index 3b38258c..c59ba0dd 100644
--- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
@@ -18,8 +18,7 @@
 final class CommandGetHelperDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
 	{
diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
index 828b000e..da0acd65 100644
--- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
@@ -72,7 +72,7 @@ public function getTypeFromMethodCall(
 
 		return new ParentObjectType(
 			$defaultType->describe(VerbosityLevel::typeOnly()),
-			$calledOnType
+			$calledOnType,
 		);
 	}
 
diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
index 6ef28bbd..c1750d00 100644
--- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
@@ -15,11 +15,10 @@
 final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var string */
-	private $className;
+	private string $className;
 
 	/** @var string[] */
-	private $methods;
+	private array $methods;
 
 	/**
 	 * @param string[] $methods
diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
index 4babdcd7..32b9ecd8 100644
--- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
@@ -14,11 +14,10 @@
 final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var string */
-	private $className;
+	private string $className;
 
 	/** @var string[] */
-	private $methods;
+	private array $methods;
 
 	/**
 	 * @param string[] $methods
diff --git a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
index 4b7c1b3a..b25ceaf4 100644
--- a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
@@ -37,7 +37,7 @@ public function getTypeFromMethodCall(
 		if ($calledOnType instanceof TreeBuilderType) {
 			return new ParentObjectType(
 				$calledOnType->getRootNodeClassName(),
-				$calledOnType
+				$calledOnType,
 			);
 		}
 
diff --git a/src/Type/Symfony/Config/ValueObject/ParentObjectType.php b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
index 56aec24f..19baf926 100644
--- a/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
+++ b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php
@@ -9,8 +9,7 @@
 class ParentObjectType extends ObjectType
 {
 
-	/** @var Type */
-	private $parent;
+	private Type $parent;
 
 	public function __construct(string $className, Type $parent)
 	{
diff --git a/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
index 71bc4fc2..93c713f1 100644
--- a/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
+++ b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php
@@ -7,8 +7,7 @@
 class TreeBuilderType extends ObjectType
 {
 
-	/** @var string */
-	private $rootNodeClassName;
+	private string $rootNodeClassName;
 
 	public function __construct(string $className, string $rootNodeClassName)
 	{
diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
index 2e01dec7..23b077f2 100644
--- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php
+++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
@@ -37,7 +37,7 @@ public function getTypeFromMethodCall(
 		if (count($methodCall->getArgs()) === 0) {
 			return new ArrayType(
 				new GenericClassStringType(new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')),
-				AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')))
+				AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))),
 			);
 		}
 
diff --git a/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php
index 7af50773..d975d9d1 100644
--- a/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php
+++ b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php
@@ -19,8 +19,7 @@
 class ExtensionGetConfigurationReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var ReflectionProvider */
-	private $reflectionProvider;
+	private ReflectionProvider $reflectionProvider;
 
 	public function __construct(ReflectionProvider $reflectionProvider)
 	{
diff --git a/src/Type/Symfony/Helper.php b/src/Type/Symfony/Helper.php
index 099b64c2..4aad820c 100644
--- a/src/Type/Symfony/Helper.php
+++ b/src/Type/Symfony/Helper.php
@@ -17,7 +17,7 @@ public static function createMarkerNode(Expr $expr, Type $type, PrettyPrinterAbs
 		return new Expr\Variable(md5(sprintf(
 			'%s::%s',
 			$printer->prettyPrintExpr($expr),
-			$type->describe(VerbosityLevel::precise())
+			$type->describe(VerbosityLevel::precise()),
 		)));
 	}
 
diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
index d3ad578c..a11540bf 100644
--- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
@@ -20,8 +20,7 @@ final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExten
 	private const HAS_METHOD_NAME = 'has';
 	private const GET_METHOD_NAME = 'get';
 
-	/** @var TypeSpecifier */
-	private $typeSpecifier;
+	private TypeSpecifier $typeSpecifier;
 
 	public function getClass(): string
 	{
@@ -38,7 +37,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 		return $this->typeSpecifier->create(
 			new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()),
 			new NullType(),
-			$context->negate()
+			$context->negate(),
 		);
 	}
 
diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index 5eb4cfe9..71e71748 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -21,8 +21,7 @@
 final class InputInterfaceGetArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
 	{
diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
index d765f65b..223b77cf 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -16,11 +16,9 @@
 final class InputInterfaceGetOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
-	/** @var GetOptionTypeHelper */
-	private $getOptionTypeHelper;
+	private GetOptionTypeHelper $getOptionTypeHelper;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, GetOptionTypeHelper $getOptionTypeHelper)
 	{
diff --git a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
index 515c44d0..3105621d 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
@@ -17,11 +17,9 @@
 final class InputInterfaceGetOptionsDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
-	/** @var GetOptionTypeHelper */
-	private $getOptionTypeHelper;
+	private GetOptionTypeHelper $getOptionTypeHelper;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, GetOptionTypeHelper $getOptionTypeHelper)
 	{
diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
index 2ca6b4a1..91611838 100644
--- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
@@ -18,8 +18,7 @@
 final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
 	{
diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
index 3be621f8..c25e646a 100644
--- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
@@ -17,8 +17,7 @@
 final class InputInterfaceHasOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var ConsoleApplicationResolver */
-	private $consoleApplicationResolver;
+	private ConsoleApplicationResolver $consoleApplicationResolver;
 
 	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
 	{
diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
index 98e0e3fd..c8f49bb8 100644
--- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
@@ -15,11 +15,9 @@
 final class OptionTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	/** @var Standard */
-	private $printer;
+	private Standard $printer;
 
-	/** @var TypeSpecifier */
-	private $typeSpecifier;
+	private TypeSpecifier $typeSpecifier;
 
 	public function __construct(Standard $printer)
 	{
@@ -45,7 +43,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 		return $this->typeSpecifier->create(
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
-			$context
+			$context,
 		);
 	}
 
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 82535133..7b70b443 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -44,23 +44,17 @@
 final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var string */
-	private $className;
+	private string $className;
 
-	/** @var string|null */
-	private $methodGet;
+	private ?string $methodGet = null;
 
-	/** @var string|null */
-	private $methodHas;
+	private ?string $methodHas = null;
 
-	/** @var bool */
-	private $constantHassers;
+	private bool $constantHassers;
 
-	/** @var ParameterMap */
-	private $parameterMap;
+	private ParameterMap $parameterMap;
 
-	/** @var TypeStringResolver */
-	private $typeStringResolver;
+	private TypeStringResolver $typeStringResolver;
 
 	public function __construct(
 		string $className,
@@ -86,9 +80,7 @@ public function getClass(): string
 
 	public function isMethodSupported(MethodReflection $methodReflection): bool
 	{
-		$methods = array_filter([$this->methodGet, $this->methodHas], static function (?string $method): bool {
-			return $method !== null;
-		});
+		$methods = array_filter([$this->methodGet, $this->methodHas], static fn (?string $method): bool => $method !== null);
 
 		return in_array($methodReflection->getName(), $methods, true);
 	}
@@ -170,17 +162,13 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type
 				}
 
 				return ConstantArrayTypeBuilder::createFromConstantArray(
-					new ConstantArrayType($keyTypes, $valueTypes)
+					new ConstantArrayType($keyTypes, $valueTypes),
 				)->getArray();
 			}
 
 			return new ArrayType(
-				TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
-					return $this->generalizeTypeFromValue($scope, $item);
-				}, array_keys($value))),
-				TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
-					return $this->generalizeTypeFromValue($scope, $item);
-				}, array_values($value)))
+				TypeCombinator::union(...array_map(fn ($item): Type => $this->generalizeTypeFromValue($scope, $item), array_keys($value))),
+				TypeCombinator::union(...array_map(fn ($item): Type => $this->generalizeTypeFromValue($scope, $item), array_values($value))),
 			);
 		}
 
diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
index ffb2e8f3..793e928f 100644
--- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
@@ -20,8 +20,7 @@ final class RequestTypeSpecifyingExtension implements MethodTypeSpecifyingExtens
 	private const HAS_METHOD_NAME = 'hasSession';
 	private const GET_METHOD_NAME = 'getSession';
 
-	/** @var TypeSpecifier */
-	private $typeSpecifier;
+	private TypeSpecifier $typeSpecifier;
 
 	public function getClass(): string
 	{
@@ -45,7 +44,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 		return $this->typeSpecifier->create(
 			new MethodCall($node->var, self::GET_METHOD_NAME),
 			TypeCombinator::removeNull($returnType),
-			$context
+			$context,
 		);
 	}
 
diff --git a/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php
index a659c684..5a95bf98 100644
--- a/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php
@@ -52,9 +52,9 @@ public function getTypeFromMethodCall(
 						new StringType(),
 						new ArrayType(
 							new StringType(),
-							new ObjectType(Cookie::class)
-						)
-					)
+							new ObjectType(Cookie::class),
+						),
+					),
 				);
 			}
 		}
diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
index d3ea959d..2b689fbb 100755
--- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
@@ -17,11 +17,9 @@
 class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var string */
-	private $class;
+	private string $class;
 
-	/** @var string */
-	private $method;
+	private string $method;
 
 	public function __construct(string $class, string $method)
 	{
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index b0101cf4..c8c49a89 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -23,20 +23,15 @@
 final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
-	/** @var string */
-	private $className;
+	private string $className;
 
-	/** @var bool */
-	private $constantHassers;
+	private bool $constantHassers;
 
-	/** @var ServiceMap */
-	private $serviceMap;
+	private ServiceMap $serviceMap;
 
-	/** @var ParameterMap */
-	private $parameterMap;
+	private ParameterMap $parameterMap;
 
-	/** @var ParameterBag|null */
-	private $parameterBag;
+	private ?ParameterBag $parameterBag = null;
 
 	public function __construct(
 		string $className,
diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
index b52c1531..524e6132 100644
--- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
@@ -15,14 +15,11 @@
 final class ServiceTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	/** @var string */
-	private $className;
+	private string $className;
 
-	/** @var Standard */
-	private $printer;
+	private Standard $printer;
 
-	/** @var TypeSpecifier */
-	private $typeSpecifier;
+	private TypeSpecifier $typeSpecifier;
 
 	public function __construct(string $className, Standard $printer)
 	{
@@ -49,7 +46,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 		return $this->typeSpecifier->create(
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
-			$context
+			$context,
 		);
 	}
 
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
index 4551accd..e4cb4bea 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -29,7 +29,7 @@ public function testGetPrivateService(): void
 			[
 				__DIR__ . '/ExampleController.php',
 			],
-			[]
+			[],
 		);
 	}
 
@@ -42,7 +42,7 @@ public function testGetPrivateServiceInAbstractController(): void
 			[
 				__DIR__ . '/ExampleAbstractController.php',
 			],
-			[]
+			[],
 		);
 	}
 
@@ -62,7 +62,7 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 				__DIR__ . '/ExampleLegacyServiceSubscriberFromAbstractController.php',
 				__DIR__ . '/ExampleLegacyServiceSubscriberFromLegacyController.php',
 			],
-			[]
+			[],
 		);
 	}
 
@@ -82,7 +82,7 @@ public function testGetPrivateServiceInServiceSubscriber(): void
 				__DIR__ . '/ExampleServiceSubscriberFromAbstractController.php',
 				__DIR__ . '/ExampleServiceSubscriberFromLegacyController.php',
 			],
-			[]
+			[],
 		);
 	}
 
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index 51513b09..8c5efe8d 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -34,7 +34,7 @@ public function testGetPrivateService(): void
 					'Service "private" is private.',
 					13,
 				],
-			]
+			],
 		);
 	}
 
@@ -54,7 +54,7 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 				__DIR__ . '/ExampleLegacyServiceSubscriberFromAbstractController.php',
 				__DIR__ . '/ExampleLegacyServiceSubscriberFromLegacyController.php',
 			],
-			[]
+			[],
 		);
 	}
 
@@ -74,7 +74,7 @@ public function testGetPrivateServiceInServiceSubscriber(): void
 				__DIR__ . '/ExampleServiceSubscriberFromAbstractController.php',
 				__DIR__ . '/ExampleServiceSubscriberFromLegacyController.php',
 			],
-			[]
+			[],
 		);
 	}
 
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index f975a179..f2f74e55 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -41,7 +41,7 @@ public function testGetPrivateService(): void
 			[
 				__DIR__ . '/ExampleController.php',
 			],
-			[]
+			[],
 		);
 	}
 
@@ -55,7 +55,7 @@ public function testGetPrivateServiceInAbstractController(): void
 			[
 				__DIR__ . '/ExampleAbstractController.php',
 			],
-			[]
+			[],
 		);
 	}
 
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 4bd233e9..323065b0 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -35,7 +35,7 @@ public function testGetPrivateService(): void
 					'Service "unknown" is not registered in the container.',
 					25,
 				],
-			]
+			],
 		);
 	}
 
@@ -54,7 +54,7 @@ public function testGetPrivateServiceInAbstractController(): void
 					'Service "unknown" is not registered in the container.',
 					25,
 				],
-			]
+			],
 		);
 	}
 
@@ -68,7 +68,7 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void
 			[
 				__DIR__ . '/ExampleServiceSubscriber.php',
 			],
-			[]
+			[],
 		);
 	}
 
diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php
index 9005cf60..ec9c966d 100644
--- a/tests/Rules/Symfony/ExampleServiceSubscriber.php
+++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php
@@ -9,8 +9,7 @@
 final class ExampleServiceSubscriber implements ServiceSubscriberInterface
 {
 
-	/** @var ContainerInterface */
-	private $locator;
+	private ContainerInterface $locator;
 
 	public function __construct(ContainerInterface $locator)
 	{
diff --git a/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
index 5e4f2ee6..bc6a6563 100644
--- a/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
+++ b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
@@ -35,7 +35,7 @@ public function testGetArgument(): void
 					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array<int, string>|null, array<string, string> given.',
 					25,
 				],
-			]
+			],
 		);
 	}
 
diff --git a/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php b/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php
index 3e5e03d4..2dcbbcd1 100644
--- a/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php
+++ b/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php
@@ -27,7 +27,7 @@ public function testGetArgument(): void
 					'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array<string>|null, array<int, int> given.',
 					29,
 				],
-			]
+			],
 		);
 	}
 
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
index 95f4b733..6d25bf4a 100644
--- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -30,7 +30,7 @@ public function testGetArgument(): void
 					'Command "example-rule" does not define argument "undefined".',
 					42,
 				],
-			]
+			],
 		);
 	}
 
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
index b3cbe0ef..50d38a83 100644
--- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -30,7 +30,7 @@ public function testGetArgument(): void
 					'Command "example-rule" does not define option "bbb".',
 					49,
 				],
-			]
+			],
 		);
 	}
 
diff --git a/tests/Type/Symfony/console_application_loader.php b/tests/Type/Symfony/console_application_loader.php
index b6755aea..fb5459b5 100644
--- a/tests/Type/Symfony/console_application_loader.php
+++ b/tests/Type/Symfony/console_application_loader.php
@@ -15,9 +15,7 @@
 $application->add(new ExampleOptionCommand());
 
 if (class_exists(LazyCommand::class)) {
-	$application->add(new LazyCommand('lazy-example-option', [], '', false, static function () {
-		return new ExampleOptionLazyCommand();
-	}));
+	$application->add(new LazyCommand('lazy-example-option', [], '', false, static fn () => new ExampleOptionLazyCommand()));
 } else {
 	$application->add(new ExampleOptionLazyCommand());
 }

From c88f96e66158021ee84b65f2d50a07002bd93645 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 6 Sep 2024 14:42:57 +0200
Subject: [PATCH 269/297] Drop dependency on nikic/php-parser

---
 composer.json | 1 -
 1 file changed, 1 deletion(-)

diff --git a/composer.json b/composer.json
index ffb9f3b8..6cf39b10 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,6 @@
     "symfony/framework-bundle": "<3.0"
   },
   "require-dev": {
-    "nikic/php-parser": "^4.13.0",
     "php-parallel-lint/php-parallel-lint": "^1.2",
     "phpstan/phpstan-phpunit": "^2.0",
     "phpstan/phpstan-strict-rules": "^2.0",

From 23a776d0326e4f2c0cf2bbd6f6b28bc32cc45b46 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 13 Sep 2024 14:54:12 +0200
Subject: [PATCH 270/297] Fixes after TypeSpecifier BC break

---
 src/Type/Symfony/ArgumentTypeSpecifyingExtension.php | 1 +
 src/Type/Symfony/InputBagTypeSpecifyingExtension.php | 1 +
 src/Type/Symfony/OptionTypeSpecifyingExtension.php   | 1 +
 src/Type/Symfony/RequestTypeSpecifyingExtension.php  | 1 +
 src/Type/Symfony/ServiceTypeSpecifyingExtension.php  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
index f9aafb23..019890c8 100644
--- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
@@ -44,6 +44,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
 			$context,
+			$scope,
 		);
 	}
 
diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
index a11540bf..72e3bc3f 100644
--- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
@@ -38,6 +38,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 			new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()),
 			new NullType(),
 			$context->negate(),
+			$scope,
 		);
 	}
 
diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
index c8f49bb8..e8530417 100644
--- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
@@ -44,6 +44,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
 			$context,
+			$scope,
 		);
 	}
 
diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
index 793e928f..40d38493 100644
--- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php
@@ -45,6 +45,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 			new MethodCall($node->var, self::GET_METHOD_NAME),
 			TypeCombinator::removeNull($returnType),
 			$context,
+			$scope,
 		);
 	}
 
diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
index 524e6132..86dc4efc 100644
--- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
@@ -47,6 +47,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod
 			Helper::createMarkerNode($node->var, $argType, $this->printer),
 			$argType,
 			$context,
+			$scope,
 		);
 	}
 

From bd9efb75ec99a20298ff04c167e9c2f599ae734d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 24 Sep 2024 20:53:44 +0200
Subject: [PATCH 271/297] Fixes after PHPStan update

---
 src/Type/Symfony/EnvelopeReturnTypeExtension.php | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
index 23b077f2..06e08772 100644
--- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php
+++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php
@@ -37,13 +37,13 @@ public function getTypeFromMethodCall(
 		if (count($methodCall->getArgs()) === 0) {
 			return new ArrayType(
 				new GenericClassStringType(new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')),
-				AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))),
+				TypeCombinator::intersect(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')), new AccessoryArrayListType()),
 			);
 		}
 
 		$argType = $scope->getType($methodCall->getArgs()[0]->value);
 		if (count($argType->getConstantStrings()) === 0) {
-			return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')));
+			return TypeCombinator::intersect(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')), new AccessoryArrayListType());
 		}
 
 		$objectTypes = [];
@@ -51,7 +51,7 @@ public function getTypeFromMethodCall(
 			$objectTypes[] = new ObjectType($constantString->getValue());
 		}
 
-		return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), TypeCombinator::union(...$objectTypes)));
+		return TypeCombinator::intersect(new ArrayType(new IntegerType(), TypeCombinator::union(...$objectTypes)), new AccessoryArrayListType());
 	}
 
 }

From 3eb61a05bf872bfb8a5663b75df5a1a22270086b Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 26 Sep 2024 15:01:53 +0200
Subject: [PATCH 272/297] Fixes after PHPStan update

---
 ...ionPrototypeDynamicReturnTypeExtension.php |  6 +++++-
 ...ParentObjectDynamicReturnTypeExtension.php |  6 +++++-
 ...ReturnParentDynamicReturnTypeExtension.php |  5 ++---
 ...rGetRootNodeDynamicReturnTypeExtension.php |  8 ++------
 .../HeaderBagDynamicReturnTypeExtension.php   |  5 ++---
 .../InputBagDynamicReturnTypeExtension.php    | 12 ++++++++----
 ...nelInterfaceDynamicReturnTypeExtension.php |  5 ++---
 .../ParameterDynamicReturnTypeExtension.php   | 12 +++++-------
 .../RequestDynamicReturnTypeExtension.php     |  5 ++---
 .../ServiceDynamicReturnTypeExtension.php     | 19 ++++++++-----------
 10 files changed, 41 insertions(+), 42 deletions(-)

diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
index da0acd65..73af2d77 100644
--- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
@@ -55,7 +55,11 @@ public function getTypeFromMethodCall(
 	{
 		$calledOnType = $scope->getType($methodCall->var);
 
-		$defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		$defaultType = ParametersAcceptorSelector::selectFromArgs(
+			$scope,
+			$methodCall->getArgs(),
+			$methodReflection->getVariants(),
+		)->getReturnType();
 
 		if ($methodReflection->getName() === 'prototype') {
 			if (!isset($methodCall->getArgs()[0])) {
diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
index c1750d00..9264d17b 100644
--- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
@@ -47,7 +47,11 @@ public function getTypeFromMethodCall(
 	{
 		$calledOnType = $scope->getType($methodCall->var);
 
-		$defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		$defaultType = ParametersAcceptorSelector::selectFromArgs(
+			$scope,
+			$methodCall->getArgs(),
+			$methodReflection->getVariants(),
+		)->getReturnType();
 
 		return new ParentObjectType($defaultType->describe(VerbosityLevel::typeOnly()), $calledOnType);
 	}
diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
index 32b9ecd8..d3351327 100644
--- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
@@ -5,7 +5,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
 use PHPStan\Type\Type;
@@ -42,14 +41,14 @@ public function getTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
 		$calledOnType = $scope->getType($methodCall->var);
 		if ($calledOnType instanceof ParentObjectType) {
 			return $calledOnType->getParent();
 		}
 
-		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		return null;
 	}
 
 }
diff --git a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
index b25ceaf4..2be2b574 100644
--- a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php
@@ -5,7 +5,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
 use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType;
@@ -28,12 +27,9 @@ public function getTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
 		$calledOnType = $scope->getType($methodCall->var);
-
-		$defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
-
 		if ($calledOnType instanceof TreeBuilderType) {
 			return new ParentObjectType(
 				$calledOnType->getRootNodeClassName(),
@@ -41,7 +37,7 @@ public function getTypeFromMethodCall(
 			);
 		}
 
-		return $defaultType;
+		return null;
 	}
 
 }
diff --git a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
index 5b05a711..4bf5b8d0 100644
--- a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
@@ -5,7 +5,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
@@ -32,7 +31,7 @@ public function getTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
 		$firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true);
 		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
@@ -48,7 +47,7 @@ public function getTypeFromMethodCall(
 			return new ArrayType(new IntegerType(), new StringType());
 		}
 
-		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		return null;
 	}
 
 }
diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
index af35a548..75e6d0bc 100644
--- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php
@@ -37,7 +37,7 @@ public function getTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
 		if ($methodReflection->getName() === 'get') {
 			return $this->getGetTypeFromMethodCall($methodReflection, $methodCall, $scope);
@@ -54,17 +54,21 @@ private function getGetTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
 		if (isset($methodCall->getArgs()[1])) {
 			$argType = $scope->getType($methodCall->getArgs()[1]->value);
 			$isNull = (new NullType())->isSuperTypeOf($argType);
 			if ($isNull->no()) {
-				return TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType());
+				return TypeCombinator::removeNull(ParametersAcceptorSelector::selectFromArgs(
+					$scope,
+					$methodCall->getArgs(),
+					$methodReflection->getVariants(),
+				)->getReturnType());
 			}
 		}
 
-		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		return null;
 	}
 
 	private function getAllTypeFromMethodCall(
diff --git a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
index 5a2adae5..a0cb1c8a 100644
--- a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
@@ -5,7 +5,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
@@ -30,7 +29,7 @@ public function getTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
 		$firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true);
 		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
@@ -44,7 +43,7 @@ public function getTypeFromMethodCall(
 			return new ArrayType(new IntegerType(), new StringType());
 		}
 
-		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		return null;
 	}
 
 }
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 7b70b443..ad62f127 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -6,7 +6,6 @@
 use PHPStan\Analyser\Scope;
 use PHPStan\PhpDoc\TypeStringResolver;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ParameterMap;
@@ -85,7 +84,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return in_array($methodReflection->getName(), $methods, true);
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
 		switch ($methodReflection->getName()) {
 			case $this->methodGet:
@@ -206,16 +205,15 @@ private function getHasTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
-		$defaultReturnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
 		if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope);
 		if ($parameterKeys === []) {
-			return $defaultReturnType;
+			return null;
 		}
 
 		$has = null;
@@ -228,7 +226,7 @@ private function getHasTypeFromMethodCall(
 				($has === true && $parameter === null)
 				|| ($has === false && $parameter !== null)
 			) {
-				return $defaultReturnType;
+				return null;
 			}
 		}
 
diff --git a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
index 284ddf01..6dcc1bcf 100644
--- a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
@@ -5,7 +5,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\ResourceType;
@@ -29,7 +28,7 @@ public function getTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
 		if (!isset($methodCall->getArgs()[0])) {
 			return new StringType();
@@ -46,7 +45,7 @@ public function getTypeFromMethodCall(
 			return new StringType();
 		}
 
-		return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
+		return null;
 	}
 
 }
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index c8c49a89..af35b368 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -5,7 +5,6 @@
 use PhpParser\Node\Expr\MethodCall;
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
-use PHPStan\Reflection\ParametersAcceptorSelector;
 use PHPStan\ShouldNotHappenException;
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ParameterMap;
@@ -56,7 +55,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
 		return in_array($methodReflection->getName(), ['get', 'has'], true);
 	}
 
-	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
 	{
 		switch ($methodReflection->getName()) {
 			case 'get':
@@ -71,16 +70,15 @@ private function getGetTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
-		$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
 		if (!isset($methodCall->getArgs()[0])) {
-			return $returnType;
+			return null;
 		}
 
 		$parameterBag = $this->tryGetParameterBag();
 		if ($parameterBag === null) {
-			return $returnType;
+			return null;
 		}
 
 		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope);
@@ -91,7 +89,7 @@ private function getGetTypeFromMethodCall(
 			}
 		}
 
-		return $returnType;
+		return null;
 	}
 
 	private function tryGetParameterBag(): ?ParameterBag
@@ -122,11 +120,10 @@ private function getHasTypeFromMethodCall(
 		MethodReflection $methodReflection,
 		MethodCall $methodCall,
 		Scope $scope
-	): Type
+	): ?Type
 	{
-		$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
 		if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) {
-			return $returnType;
+			return null;
 		}
 
 		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope);
@@ -135,7 +132,7 @@ private function getHasTypeFromMethodCall(
 			return new ConstantBooleanType($service !== null && $service->isPublic());
 		}
 
-		return $returnType;
+		return null;
 	}
 
 	private function determineServiceClass(ParameterBag $parameterBag, ServiceDefinition $service): ?string

From f7d5782044bedf93aeb3f38e09c91148ee90e5a1 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <vincentlanglet@hotmail.fr>
Date: Thu, 26 Sep 2024 11:24:58 +0200
Subject: [PATCH 273/297] Fix InputBagTypeSpecifyingExtension

---
 src/Type/Symfony/InputBagTypeSpecifyingExtension.php | 2 +-
 tests/Type/Symfony/data/input_bag.php                | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
index d3ad578c..3a4e54fc 100644
--- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php
@@ -30,7 +30,7 @@ public function getClass(): string
 
 	public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
 	{
-		return $methodReflection->getName() === self::HAS_METHOD_NAME && !$context->null();
+		return $methodReflection->getName() === self::HAS_METHOD_NAME && $context->false();
 	}
 
 	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php
index efd08461..744a7765 100644
--- a/tests/Type/Symfony/data/input_bag.php
+++ b/tests/Type/Symfony/data/input_bag.php
@@ -7,7 +7,8 @@
 assertType('bool|float|int|string|null', $bag->get('foo'));
 
 if ($bag->has('foo')) {
-	assertType('bool|float|int|string', $bag->get('foo'));
+	// Because `has` rely on `array_key_exists` we can still have set the NULL value.
+	assertType('bool|float|int|string|null', $bag->get('foo'));
 	assertType('bool|float|int|string|null', $bag->get('bar'));
 } else {
 	assertType('null', $bag->get('foo'));

From 7e5633f28f2f62bde4d48c7739bc4f277ca90238 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 30 Sep 2024 17:09:04 +0200
Subject: [PATCH 274/297] [BCB] Remove legacy config options with `_` in their
 name

---
 extension.neon                         | 6 ------
 src/Symfony/Configuration.php          | 6 +++---
 tests/Symfony/config.neon              | 3 ---
 tests/Type/Symfony/extension-test.neon | 4 ++--
 4 files changed, 5 insertions(+), 14 deletions(-)
 delete mode 100644 tests/Symfony/config.neon

diff --git a/extension.neon b/extension.neon
index 512f9908..0d74d8b3 100644
--- a/extension.neon
+++ b/extension.neon
@@ -5,11 +5,8 @@ parameters:
 		uncheckedExceptionClasses:
 			- 'Symfony\Component\Console\Exception\InvalidArgumentException'
 	symfony:
-		container_xml_path: null
 		containerXmlPath: null
-		constant_hassers: true
 		constantHassers: true
-		console_application_loader: null
 		consoleApplicationLoader: null
 	featureToggles:
 		skipCheckGenericClasses:
@@ -109,11 +106,8 @@ parameters:
 
 parametersSchema:
 	symfony: structure([
-		container_xml_path: schema(string(), nullable())
 		containerXmlPath: schema(string(), nullable())
-		constant_hassers: bool()
 		constantHassers: bool()
-		console_application_loader: schema(string(), nullable())
 		consoleApplicationLoader: schema(string(), nullable())
 	])
 
diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php
index e603b333..acdd8508 100644
--- a/src/Symfony/Configuration.php
+++ b/src/Symfony/Configuration.php
@@ -18,17 +18,17 @@ public function __construct(array $parameters)
 
 	public function getContainerXmlPath(): ?string
 	{
-		return $this->parameters['containerXmlPath'] ?? $this->parameters['container_xml_path'] ?? null;
+		return $this->parameters['containerXmlPath'];
 	}
 
 	public function hasConstantHassers(): bool
 	{
-		return $this->parameters['constantHassers'] ?? $this->parameters['constant_hassers'] ?? true;
+		return $this->parameters['constantHassers'];
 	}
 
 	public function getConsoleApplicationLoader(): ?string
 	{
-		return $this->parameters['consoleApplicationLoader'] ?? $this->parameters['console_application_loader'] ?? null;
+		return $this->parameters['consoleApplicationLoader'];
 	}
 
 }
diff --git a/tests/Symfony/config.neon b/tests/Symfony/config.neon
deleted file mode 100644
index e4f42c64..00000000
--- a/tests/Symfony/config.neon
+++ /dev/null
@@ -1,3 +0,0 @@
-parameters:
-	symfony:
-		container_xml_path: container.xml
diff --git a/tests/Type/Symfony/extension-test.neon b/tests/Type/Symfony/extension-test.neon
index f7dc1353..0f1d9522 100644
--- a/tests/Type/Symfony/extension-test.neon
+++ b/tests/Type/Symfony/extension-test.neon
@@ -1,4 +1,4 @@
 parameters:
 	symfony:
-		console_application_loader: console_application_loader.php
-		container_xml_path: container.xml
+		consoleApplicationLoader: console_application_loader.php
+		containerXmlPath: container.xml

From a0572ad0ffd8bd4eff6cc5e41a3a3d951880ad80 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 30 Sep 2024 17:17:17 +0200
Subject: [PATCH 275/297] Fixes after PHPStan update

---
 src/Rules/Symfony/InvalidArgumentDefaultValueRule.php          | 3 +--
 src/Rules/Symfony/InvalidOptionDefaultValueRule.php            | 3 +--
 src/Rules/Symfony/UndefinedArgumentRule.php                    | 3 +--
 src/Rules/Symfony/UndefinedOptionRule.php                      | 3 +--
 src/Symfony/DefaultParameterMap.php                            | 3 +--
 src/Symfony/DefaultServiceMap.php                              | 3 +--
 .../Symfony/CommandGetHelperDynamicReturnTypeExtension.php     | 3 +--
 .../ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php | 3 +--
 .../Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php   | 3 +--
 .../InputInterfaceGetArgumentDynamicReturnTypeExtension.php    | 3 +--
 .../InputInterfaceGetOptionDynamicReturnTypeExtension.php      | 3 +--
 .../InputInterfaceHasArgumentDynamicReturnTypeExtension.php    | 3 +--
 .../InputInterfaceHasOptionDynamicReturnTypeExtension.php      | 3 +--
 src/Type/Symfony/ParameterDynamicReturnTypeExtension.php       | 3 +--
 14 files changed, 14 insertions(+), 28 deletions(-)

diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
index 27643a7d..5435d4c9 100644
--- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -13,7 +13,6 @@
 use PHPStan\Type\NullType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\StringType;
-use PHPStan\Type\TypeUtils;
 use PHPStan\Type\UnionType;
 use PHPStan\Type\VerbosityLevel;
 use function count;
@@ -46,7 +45,7 @@ public function processNode(Node $node, Scope $scope): array
 		if ($modeType->isNull()->yes()) {
 			$modeType = new ConstantIntegerType(2); // InputArgument::OPTIONAL
 		}
-		$modeTypes = TypeUtils::getConstantScalars($modeType);
+		$modeTypes = $modeType->getConstantScalarTypes();
 		if (count($modeTypes) !== 1) {
 			return [];
 		}
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
index 152514f1..2e3dc0e9 100644
--- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -15,7 +15,6 @@
 use PHPStan\Type\NullType;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\StringType;
-use PHPStan\Type\TypeUtils;
 use PHPStan\Type\UnionType;
 use PHPStan\Type\VerbosityLevel;
 use function count;
@@ -48,7 +47,7 @@ public function processNode(Node $node, Scope $scope): array
 		if ($modeType->isNull()->yes()) {
 			$modeType = new ConstantIntegerType(1); // InputOption::VALUE_NONE
 		}
-		$modeTypes = TypeUtils::getConstantScalars($modeType);
+		$modeTypes = $modeType->getConstantScalarTypes();
 		if (count($modeTypes) !== 1) {
 			return [];
 		}
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index ff997be5..bccbb515 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -12,7 +12,6 @@
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
-use PHPStan\Type\TypeUtils;
 use function count;
 use function sprintf;
 
@@ -58,7 +57,7 @@ public function processNode(Node $node, Scope $scope): array
 		}
 
 		$argType = $scope->getType($node->getArgs()[0]->value);
-		$argStrings = TypeUtils::getConstantStrings($argType);
+		$argStrings = $argType->getConstantStrings();
 		if (count($argStrings) !== 1) {
 			return [];
 		}
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index ae96bfa9..f9f88a30 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -12,7 +12,6 @@
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Symfony\Helper;
-use PHPStan\Type\TypeUtils;
 use function count;
 use function sprintf;
 
@@ -58,7 +57,7 @@ public function processNode(Node $node, Scope $scope): array
 		}
 
 		$optType = $scope->getType($node->getArgs()[0]->value);
-		$optStrings = TypeUtils::getConstantStrings($optType);
+		$optStrings = $optType->getConstantStrings();
 		if (count($optStrings) !== 1) {
 			return [];
 		}
diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php
index 8d0af459..3149fd7d 100644
--- a/src/Symfony/DefaultParameterMap.php
+++ b/src/Symfony/DefaultParameterMap.php
@@ -5,7 +5,6 @@
 use PhpParser\Node\Expr;
 use PHPStan\Analyser\Scope;
 use PHPStan\Type\Type;
-use PHPStan\Type\TypeUtils;
 use function array_map;
 
 final class DefaultParameterMap implements ParameterMap
@@ -37,7 +36,7 @@ public function getParameter(string $key): ?ParameterDefinition
 
 	public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
 	{
-		$strings = TypeUtils::getConstantStrings($scope->getType($node));
+		$strings = $scope->getType($node)->getConstantStrings();
 
 		return array_map(static fn (Type $type) => $type->getValue(), $strings);
 	}
diff --git a/src/Symfony/DefaultServiceMap.php b/src/Symfony/DefaultServiceMap.php
index edf4dfd6..5d3bccd0 100644
--- a/src/Symfony/DefaultServiceMap.php
+++ b/src/Symfony/DefaultServiceMap.php
@@ -4,7 +4,6 @@
 
 use PhpParser\Node\Expr;
 use PHPStan\Analyser\Scope;
-use PHPStan\Type\TypeUtils;
 use function count;
 
 final class DefaultServiceMap implements ServiceMap
@@ -36,7 +35,7 @@ public function getService(string $id): ?ServiceDefinition
 
 	public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string
 	{
-		$strings = TypeUtils::getConstantStrings($scope->getType($node));
+		$strings = $scope->getType($node)->getConstantStrings();
 		return count($strings) === 1 ? $strings[0]->getValue() : null;
 	}
 
diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
index c59ba0dd..fba70cfd 100644
--- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
@@ -10,7 +10,6 @@
 use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeCombinator;
-use PHPStan\Type\TypeUtils;
 use Throwable;
 use function count;
 use function get_class;
@@ -46,7 +45,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return null;
 		}
 
-		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
+		$argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings();
 		if (count($argStrings) !== 1) {
 			return null;
 		}
diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
index 73af2d77..1dae22e2 100644
--- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php
@@ -9,7 +9,6 @@
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType;
 use PHPStan\Type\Type;
-use PHPStan\Type\TypeUtils;
 use PHPStan\Type\VerbosityLevel;
 use function count;
 use function in_array;
@@ -66,7 +65,7 @@ public function getTypeFromMethodCall(
 				return $defaultType;
 			}
 
-			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
+			$argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings();
 			if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) {
 				$type = $argStrings[0]->getValue();
 
diff --git a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
index 8cf3170a..4f266c50 100644
--- a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php
@@ -10,7 +10,6 @@
 use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
 use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType;
 use PHPStan\Type\Type;
-use PHPStan\Type\TypeUtils;
 use function count;
 
 final class TreeBuilderDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
@@ -47,7 +46,7 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
 		$type = 'array';
 
 		if (isset($methodCall->getArgs()[1])) {
-			$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[1]->value));
+			$argStrings = $scope->getType($methodCall->getArgs()[1]->value)->getConstantStrings();
 			if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) {
 				$type = $argStrings[0]->getValue();
 			}
diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
index 71e71748..88bd7b0e 100644
--- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -14,7 +14,6 @@
 use PHPStan\Type\StringType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeCombinator;
-use PHPStan\Type\TypeUtils;
 use function count;
 use function in_array;
 
@@ -49,7 +48,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return null;
 		}
 
-		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
+		$argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings();
 		if (count($argStrings) !== 1) {
 			return null;
 		}
diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
index 223b77cf..6d0346cf 100644
--- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -10,7 +10,6 @@
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeCombinator;
-use PHPStan\Type\TypeUtils;
 use function count;
 
 final class InputInterfaceGetOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -47,7 +46,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return null;
 		}
 
-		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
+		$optStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings();
 		if (count($optStrings) !== 1) {
 			return null;
 		}
diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
index 91611838..34bffcea 100644
--- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
@@ -10,7 +10,6 @@
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Type;
-use PHPStan\Type\TypeUtils;
 use function array_unique;
 use function count;
 use function in_array;
@@ -46,7 +45,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return null;
 		}
 
-		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
+		$argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings();
 		if (count($argStrings) !== 1) {
 			return null;
 		}
diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
index c25e646a..e4f8b5b1 100644
--- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
@@ -10,7 +10,6 @@
 use PHPStan\Type\Constant\ConstantBooleanType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\Type;
-use PHPStan\Type\TypeUtils;
 use function array_unique;
 use function count;
 
@@ -45,7 +44,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
 			return null;
 		}
 
-		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value));
+		$optStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings();
 		if (count($optStrings) !== 1) {
 			return null;
 		}
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index ad62f127..cd87ff6f 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -14,7 +14,6 @@
 use PHPStan\Type\Constant\ConstantArrayType;
 use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
 use PHPStan\Type\Constant\ConstantBooleanType;
-use PHPStan\Type\ConstantType;
 use PHPStan\Type\DynamicMethodReturnTypeExtension;
 use PHPStan\Type\FloatType;
 use PHPStan\Type\GeneralizePrecision;
@@ -194,7 +193,7 @@ private function generalizeType(Type $type): Type
 				}
 				return new ArrayType($this->generalizeType($type->getKeyType()), $this->generalizeType($type->getItemType()));
 			}
-			if ($type instanceof ConstantType) {
+			if ($type->isConstantValue()->yes()) {
 				return $type->generalize(GeneralizePrecision::lessSpecific());
 			}
 			return $traverse($type);

From d1e08acebbde9d8f1af925fd247742f40448e32b Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Fri, 4 Oct 2024 13:52:52 +0200
Subject: [PATCH 276/297] Fixes after PHPStan update

---
 .../Symfony/ContainerInterfaceUnknownServiceRule.php   |  6 +++---
 src/Rules/Symfony/UndefinedArgumentRule.php            |  6 +++---
 src/Rules/Symfony/UndefinedOptionRule.php              |  6 +++---
 src/Type/Symfony/ArgumentTypeSpecifyingExtension.php   |  6 +++---
 .../PassParentObjectDynamicReturnTypeExtension.php     |  2 ++
 .../Config/ReturnParentDynamicReturnTypeExtension.php  |  2 ++
 src/Type/Symfony/OptionTypeSpecifyingExtension.php     |  6 +++---
 .../Symfony/ParameterDynamicReturnTypeExtension.php    |  4 ++++
 .../Symfony/SerializerDynamicReturnTypeExtension.php   |  4 ++++
 src/Type/Symfony/ServiceDynamicReturnTypeExtension.php |  4 ++++
 src/Type/Symfony/ServiceTypeSpecifyingExtension.php    | 10 +++++++---
 .../ContainerInterfaceUnknownServiceRuleFakeTest.php   |  7 ++++---
 .../ContainerInterfaceUnknownServiceRuleTest.php       |  4 ++--
 tests/Rules/Symfony/UndefinedArgumentRuleTest.php      |  4 ++--
 tests/Rules/Symfony/UndefinedOptionRuleTest.php        |  4 ++--
 15 files changed, 48 insertions(+), 27 deletions(-)

diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
index ec1a70e0..23444b6b 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -4,8 +4,8 @@
 
 use PhpParser\Node;
 use PhpParser\Node\Expr\MethodCall;
-use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
 use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Symfony\ServiceMap;
@@ -21,9 +21,9 @@ final class ContainerInterfaceUnknownServiceRule implements Rule
 
 	private ServiceMap $serviceMap;
 
-	private Standard $printer;
+	private Printer $printer;
 
-	public function __construct(ServiceMap $symfonyServiceMap, Standard $printer)
+	public function __construct(ServiceMap $symfonyServiceMap, Printer $printer)
 	{
 		$this->serviceMap = $symfonyServiceMap;
 		$this->printer = $printer;
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
index bccbb515..ee36a23c 100644
--- a/src/Rules/Symfony/UndefinedArgumentRule.php
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -5,8 +5,8 @@
 use InvalidArgumentException;
 use PhpParser\Node;
 use PhpParser\Node\Expr\MethodCall;
-use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
 use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Symfony\ConsoleApplicationResolver;
@@ -23,9 +23,9 @@ final class UndefinedArgumentRule implements Rule
 
 	private ConsoleApplicationResolver $consoleApplicationResolver;
 
-	private Standard $printer;
+	private Printer $printer;
 
-	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Printer $printer)
 	{
 		$this->consoleApplicationResolver = $consoleApplicationResolver;
 		$this->printer = $printer;
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
index f9f88a30..39a6a4ac 100644
--- a/src/Rules/Symfony/UndefinedOptionRule.php
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -5,8 +5,8 @@
 use InvalidArgumentException;
 use PhpParser\Node;
 use PhpParser\Node\Expr\MethodCall;
-use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
 use PHPStan\Rules\RuleErrorBuilder;
 use PHPStan\Symfony\ConsoleApplicationResolver;
@@ -23,9 +23,9 @@ final class UndefinedOptionRule implements Rule
 
 	private ConsoleApplicationResolver $consoleApplicationResolver;
 
-	private Standard $printer;
+	private Printer $printer;
 
-	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Printer $printer)
 	{
 		$this->consoleApplicationResolver = $consoleApplicationResolver;
 		$this->printer = $printer;
diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
index 019890c8..5c21f021 100644
--- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
@@ -3,23 +3,23 @@
 namespace PHPStan\Type\Symfony;
 
 use PhpParser\Node\Expr\MethodCall;
-use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Analyser\SpecifiedTypes;
 use PHPStan\Analyser\TypeSpecifier;
 use PHPStan\Analyser\TypeSpecifierAwareExtension;
 use PHPStan\Analyser\TypeSpecifierContext;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
 
 final class ArgumentTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	private Standard $printer;
+	private Printer $printer;
 
 	private TypeSpecifier $typeSpecifier;
 
-	public function __construct(Standard $printer)
+	public function __construct(Printer $printer)
 	{
 		$this->printer = $printer;
 	}
diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
index 9264d17b..800d9dbc 100644
--- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php
@@ -15,12 +15,14 @@
 final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
+	/** @var class-string */
 	private string $className;
 
 	/** @var string[] */
 	private array $methods;
 
 	/**
+	 * @param class-string $className
 	 * @param string[] $methods
 	 */
 	public function __construct(string $className, array $methods)
diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
index d3351327..034d5d80 100644
--- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php
@@ -13,12 +13,14 @@
 final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
+	/** @var class-string */
 	private string $className;
 
 	/** @var string[] */
 	private array $methods;
 
 	/**
+	 * @param class-string $className
 	 * @param string[] $methods
 	 */
 	public function __construct(string $className, array $methods)
diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
index e8530417..8cd9dbd5 100644
--- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
@@ -3,23 +3,23 @@
 namespace PHPStan\Type\Symfony;
 
 use PhpParser\Node\Expr\MethodCall;
-use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Analyser\SpecifiedTypes;
 use PHPStan\Analyser\TypeSpecifier;
 use PHPStan\Analyser\TypeSpecifierAwareExtension;
 use PHPStan\Analyser\TypeSpecifierContext;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
 
 final class OptionTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
-	private Standard $printer;
+	private Printer $printer;
 
 	private TypeSpecifier $typeSpecifier;
 
-	public function __construct(Standard $printer)
+	public function __construct(Printer $printer)
 	{
 		$this->printer = $printer;
 	}
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index cd87ff6f..5f0fb9bd 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -42,6 +42,7 @@
 final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
+	/** @var class-string */
 	private string $className;
 
 	private ?string $methodGet = null;
@@ -54,6 +55,9 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy
 
 	private TypeStringResolver $typeStringResolver;
 
+	/**
+	 * @param class-string $className
+	 */
 	public function __construct(
 		string $className,
 		?string $methodGet,
diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
index 2b689fbb..84f256e4 100755
--- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php
@@ -17,10 +17,14 @@
 class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
+	/** @var class-string */
 	private string $class;
 
 	private string $method;
 
+	/**
+	 * @param class-string $class
+	 */
 	public function __construct(string $class, string $method)
 	{
 		$this->class = $class;
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index af35b368..13097469 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -22,6 +22,7 @@
 final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
 {
 
+	/** @var class-string */
 	private string $className;
 
 	private bool $constantHassers;
@@ -32,6 +33,9 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
 
 	private ?ParameterBag $parameterBag = null;
 
+	/**
+	 * @param class-string $className
+	 */
 	public function __construct(
 		string $className,
 		Configuration $configuration,
diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
index 86dc4efc..dd767ccb 100644
--- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
+++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php
@@ -3,25 +3,29 @@
 namespace PHPStan\Type\Symfony;
 
 use PhpParser\Node\Expr\MethodCall;
-use PhpParser\PrettyPrinter\Standard;
 use PHPStan\Analyser\Scope;
 use PHPStan\Analyser\SpecifiedTypes;
 use PHPStan\Analyser\TypeSpecifier;
 use PHPStan\Analyser\TypeSpecifierAwareExtension;
 use PHPStan\Analyser\TypeSpecifierContext;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
 
 final class ServiceTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
 {
 
+	/** @var class-string */
 	private string $className;
 
-	private Standard $printer;
+	private Printer $printer;
 
 	private TypeSpecifier $typeSpecifier;
 
-	public function __construct(string $className, Standard $printer)
+	/**
+	 * @param class-string $className
+	 */
+	public function __construct(string $className, Printer $printer)
 	{
 		$this->className = $className;
 		$this->printer = $printer;
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index f2f74e55..065fa067 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -2,13 +2,14 @@
 
 namespace PHPStan\Rules\Symfony;
 
-use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
 use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 use function class_exists;
 
 /**
@@ -19,7 +20,7 @@ final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), new Standard());
+		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), self::getContainer()->getByType(Printer::class));
 	}
 
 	/**
@@ -28,7 +29,7 @@ protected function getRule(): Rule
 	protected function getMethodTypeSpecifyingExtensions(): array
 	{
 		return [
-			new ServiceTypeSpecifyingExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', new Standard()),
+			new ServiceTypeSpecifyingExtension(AbstractController::class, self::getContainer()->getByType(Printer::class)),
 		];
 	}
 
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index 323065b0..aa7683e2 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -2,7 +2,7 @@
 
 namespace PHPStan\Rules\Symfony;
 
-use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
@@ -18,7 +18,7 @@ final class ContainerInterfaceUnknownServiceRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), new Standard());
+		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), self::getContainer()->getByType(Printer::class));
 	}
 
 	public function testGetPrivateService(): void
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
index 6d25bf4a..8b1b869c 100644
--- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -2,7 +2,7 @@
 
 namespace PHPStan\Rules\Symfony;
 
-use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ConsoleApplicationResolver;
@@ -16,7 +16,7 @@ final class UndefinedArgumentRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard());
+		return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class));
 	}
 
 	public function testGetArgument(): void
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
index 50d38a83..d376fa4b 100644
--- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -2,7 +2,7 @@
 
 namespace PHPStan\Rules\Symfony;
 
-use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
 use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ConsoleApplicationResolver;
@@ -16,7 +16,7 @@ final class UndefinedOptionRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard());
+		return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class));
 	}
 
 	public function testGetArgument(): void

From 237165eae2ddd114605f54a9f23d1171b4b3b55d Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 7 Oct 2024 11:17:28 +0200
Subject: [PATCH 277/297] Fixes after PHPStan update

---
 .../Symfony/ContainerInterfacePrivateServiceRule.php      | 4 ++--
 .../Form/FormInterfaceDynamicReturnTypeExtension.php      | 8 ++++----
 src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php  | 4 ++--
 .../Symfony/KernelInterfaceDynamicReturnTypeExtension.php | 4 ++--
 src/Type/Symfony/RequestDynamicReturnTypeExtension.php    | 4 ++--
 5 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 5d935859..96f1efea 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -82,13 +82,13 @@ public function processNode(Node $node, Scope $scope): array
 	private function isServiceSubscriber(Type $containerType, Scope $scope): TrinaryLogic
 	{
 		$serviceSubscriberInterfaceType = new ObjectType('Symfony\Contracts\Service\ServiceSubscriberInterface');
-		$isContainerServiceSubscriber = $serviceSubscriberInterfaceType->isSuperTypeOf($containerType);
+		$isContainerServiceSubscriber = $serviceSubscriberInterfaceType->isSuperTypeOf($containerType)->result;
 		$classReflection = $scope->getClassReflection();
 		if ($classReflection === null) {
 			return $isContainerServiceSubscriber;
 		}
 		$containedClassType = new ObjectType($classReflection->getName());
-		return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType));
+		return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)->result);
 	}
 
 }
diff --git a/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php
index bdbc95ee..f80ddeb9 100644
--- a/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php
@@ -41,10 +41,10 @@ public function getTypeFromMethodCall(
 		$firstArgType = $scope->getType($methodCall->getArgs()[0]->value);
 		$secondArgType = $scope->getType($methodCall->getArgs()[1]->value);
 
-		$firstIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
-		$firstIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType);
-		$secondIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($secondArgType);
-		$secondIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($secondArgType);
+		$firstIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType)->result;
+		$firstIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType)->result;
+		$secondIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($secondArgType)->result;
+		$secondIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($secondArgType)->result;
 
 		$firstCompareType = $firstIsTrueType->compareTo($firstIsFalseType);
 		$secondCompareType = $secondIsTrueType->compareTo($secondIsFalseType);
diff --git a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
index 4bf5b8d0..f50dc4e8 100644
--- a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php
@@ -34,8 +34,8 @@ public function getTypeFromMethodCall(
 	): ?Type
 	{
 		$firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true);
-		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
-		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType);
+		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType)->result;
+		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType)->result;
 		$compareTypes = $isTrueType->compareTo($isFalseType);
 
 		if ($compareTypes === $isTrueType) {
diff --git a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
index a0cb1c8a..810de2f6 100644
--- a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php
@@ -32,8 +32,8 @@ public function getTypeFromMethodCall(
 	): ?Type
 	{
 		$firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true);
-		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType);
-		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType);
+		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType)->result;
+		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType)->result;
 		$compareTypes = $isTrueType->compareTo($isFalseType);
 
 		if ($compareTypes === $isTrueType) {
diff --git a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
index 6dcc1bcf..fd0a3a00 100644
--- a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php
@@ -35,8 +35,8 @@ public function getTypeFromMethodCall(
 		}
 
 		$argType = $scope->getType($methodCall->getArgs()[0]->value);
-		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($argType);
-		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($argType);
+		$isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($argType)->result;
+		$isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($argType)->result;
 		$compareTypes = $isTrueType->compareTo($isFalseType);
 		if ($compareTypes === $isTrueType) {
 			return new ResourceType();

From bb3e671a0cfdc3ae20eb6b74400ad8b9e1ee4815 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Wed, 9 Oct 2024 16:41:39 +0200
Subject: [PATCH 278/297] Cleanup `skipCheckGenericClasses`

---
 extension.neon | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/extension.neon b/extension.neon
index 0d74d8b3..91e9c220 100644
--- a/extension.neon
+++ b/extension.neon
@@ -8,18 +8,6 @@ parameters:
 		containerXmlPath: null
 		constantHassers: true
 		consoleApplicationLoader: null
-	featureToggles:
-		skipCheckGenericClasses:
-			- Symfony\Component\Form\AbstractType
-			- Symfony\Component\Form\FormBuilderInterface
-			- Symfony\Component\Form\FormConfigBuilderInterface
-			- Symfony\Component\Form\FormConfigInterface
-			- Symfony\Component\Form\FormInterface
-			- Symfony\Component\Form\FormTypeExtensionInterface
-			- Symfony\Component\Form\FormTypeInterface
-			- Symfony\Component\OptionsResolver\Options
-			- Symfony\Component\Security\Core\Authorization\Voter\Voter
-			- Symfony\Component\Security\Core\User\PasswordUpgraderInterface
 	stubFiles:
 		- stubs/Psr/Cache/CacheException.stub
 		- stubs/Psr/Cache/CacheItemInterface.stub

From 282a6982af299730790f0583f9662b8dede12ed7 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Mon, 14 Oct 2024 05:22:40 +0200
Subject: [PATCH 279/297] Fix after PHPStan update

---
 tests/Type/Symfony/data/input_bag.php         | 4 ++--
 tests/Type/Symfony/data/property_accessor.php | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php
index 744a7765..77b58821 100644
--- a/tests/Type/Symfony/data/input_bag.php
+++ b/tests/Type/Symfony/data/input_bag.php
@@ -18,5 +18,5 @@
 assertType('bool|float|int|string|null', $bag->get('foo', null));
 assertType('bool|float|int|string', $bag->get('foo', ''));
 assertType('bool|float|int|string', $bag->get('foo', 'baz'));
-assertType('array<string, array|bool|float|int|string>', $bag->all());
-assertType('array', $bag->all('bar'));
+assertType('array<string, array<mixed>|bool|float|int|string>', $bag->all());
+assertType('array<mixed>', $bag->all('bar'));
diff --git a/tests/Type/Symfony/data/property_accessor.php b/tests/Type/Symfony/data/property_accessor.php
index 0e445684..8d5e95f0 100644
--- a/tests/Type/Symfony/data/property_accessor.php
+++ b/tests/Type/Symfony/data/property_accessor.php
@@ -6,7 +6,7 @@
 
 $array = [1 => 'ea'];
 $propertyAccessor->setValue($array, 'foo', 'bar');
-assertType('array', $array);
+assertType('array<mixed>', $array);
 
 $object = new \stdClass();
 $propertyAccessor->setValue($object, 'foo', 'bar');

From 270c2ee1478d1f8dc5121f539e890017bd64b04c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Wed, 30 Oct 2024 13:01:49 +0100
Subject: [PATCH 280/297] Update Process.stub

---
 extension.neon                                    |  1 +
 .../Process/Exception/LogicException.stub         |  8 ++++++++
 stubs/Symfony/Component/Process/Process.stub      | 15 +++++++++++++++
 3 files changed, 24 insertions(+)
 create mode 100644 stubs/Symfony/Component/Process/Exception/LogicException.stub

diff --git a/extension.neon b/extension.neon
index 512f9908..4868bc2d 100644
--- a/extension.neon
+++ b/extension.neon
@@ -66,6 +66,7 @@ parameters:
 		- stubs/Symfony/Component/Messenger/Envelope.stub
 		- stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub
 		- stubs/Symfony/Component/OptionsResolver/Options.stub
+		- stubs/Symfony/Component/Process/Exception/LogicException.stub
 		- stubs/Symfony/Component/Process/Process.stub
 		- stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub
 		- stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub
diff --git a/stubs/Symfony/Component/Process/Exception/LogicException.stub b/stubs/Symfony/Component/Process/Exception/LogicException.stub
new file mode 100644
index 00000000..cb781d6a
--- /dev/null
+++ b/stubs/Symfony/Component/Process/Exception/LogicException.stub
@@ -0,0 +1,8 @@
+<?php
+
+namespace Symfony\Component\Process\Exception;
+
+class LogicException extends \Exception
+{
+
+}
diff --git a/stubs/Symfony/Component/Process/Process.stub b/stubs/Symfony/Component/Process/Process.stub
index 771dc666..b7bff236 100644
--- a/stubs/Symfony/Component/Process/Process.stub
+++ b/stubs/Symfony/Component/Process/Process.stub
@@ -2,10 +2,25 @@
 
 namespace Symfony\Component\Process;
 
+use Symfony\Component\Process\Exception\LogicException;
+
 /**
  * @implements \IteratorAggregate<string, string>
  */
 class Process implements \IteratorAggregate
 {
 
+    /**
+     * @param int $flags
+     *
+     * @return \Generator<string, string, mixed, mixed>
+     *
+     * @throws LogicException in case the output has been disabled
+     * @throws LogicException In case the process is not started
+     */
+    public function getIterator(int $flags = 0): \Generator
+    {
+
+    }
+
 }

From c7b7e7f520893621558bfbfdb2694d4364565c1d Mon Sep 17 00:00:00 2001
From: Michael Telgmann <m.telgmann@shopware.com>
Date: Wed, 6 Nov 2024 11:08:10 +0100
Subject: [PATCH 281/297] feat: Add @api annotation to interfaces

The following interfaces are now part of the public API and can be safely relied on.

- \PHPStan\Symfony\ParameterDefinition
- \PHPStan\Symfony\ParameterMap
- \PHPStan\Symfony\ServiceDefinition
- \PHPStan\Symfony\ServiceMap
---
 src/Symfony/ParameterDefinition.php | 3 +++
 src/Symfony/ParameterMap.php        | 3 +++
 src/Symfony/ServiceDefinition.php   | 3 +++
 src/Symfony/ServiceMap.php          | 3 +++
 4 files changed, 12 insertions(+)

diff --git a/src/Symfony/ParameterDefinition.php b/src/Symfony/ParameterDefinition.php
index e1aa2eaa..1da7723b 100644
--- a/src/Symfony/ParameterDefinition.php
+++ b/src/Symfony/ParameterDefinition.php
@@ -2,6 +2,9 @@
 
 namespace PHPStan\Symfony;
 
+/**
+ * @api
+ */
 interface ParameterDefinition
 {
 
diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php
index ff0f5224..0c551635 100644
--- a/src/Symfony/ParameterMap.php
+++ b/src/Symfony/ParameterMap.php
@@ -5,6 +5,9 @@
 use PhpParser\Node\Expr;
 use PHPStan\Analyser\Scope;
 
+/**
+ * @api
+ */
 interface ParameterMap
 {
 
diff --git a/src/Symfony/ServiceDefinition.php b/src/Symfony/ServiceDefinition.php
index c7cdcd18..6df34cba 100644
--- a/src/Symfony/ServiceDefinition.php
+++ b/src/Symfony/ServiceDefinition.php
@@ -2,6 +2,9 @@
 
 namespace PHPStan\Symfony;
 
+/**
+ * @api
+ */
 interface ServiceDefinition
 {
 
diff --git a/src/Symfony/ServiceMap.php b/src/Symfony/ServiceMap.php
index 6665ede0..bbd2d8a3 100644
--- a/src/Symfony/ServiceMap.php
+++ b/src/Symfony/ServiceMap.php
@@ -5,6 +5,9 @@
 use PhpParser\Node\Expr;
 use PHPStan\Analyser\Scope;
 
+/**
+ * @api
+ */
 interface ServiceMap
 {
 

From dd1aaa7f85f9916222a2ce7e4d21072fe03958f4 Mon Sep 17 00:00:00 2001
From: bnowak <barteknowak90@gmail.com>
Date: Sat, 4 Jan 2025 14:55:31 +0100
Subject: [PATCH 282/297] Support for Messenger HandleTrait return types

---
 extension.neon                                |  12 ++
 src/Symfony/MessageMap.php                    |  24 +++
 src/Symfony/MessageMapFactory.php             | 154 ++++++++++++++++++
 src/Symfony/Service.php                       |  13 +-
 src/Symfony/ServiceDefinition.php             |   3 +
 src/Symfony/ServiceTag.php                    |  31 ++++
 src/Symfony/ServiceTagDefinition.php          |  13 ++
 src/Symfony/XmlServiceMapFactory.php          |  12 +-
 ...essengerHandleTraitReturnTypeExtension.php |  91 +++++++++++
 tests/Type/Symfony/ExtensionTest.php          |   1 +
 tests/Type/Symfony/container.xml              |  18 ++
 .../Symfony/data/messenger_handle_trait.php   | 113 +++++++++++++
 12 files changed, 483 insertions(+), 2 deletions(-)
 create mode 100644 src/Symfony/MessageMap.php
 create mode 100644 src/Symfony/MessageMapFactory.php
 create mode 100644 src/Symfony/ServiceTag.php
 create mode 100644 src/Symfony/ServiceTagDefinition.php
 create mode 100644 src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php
 create mode 100644 tests/Type/Symfony/data/messenger_handle_trait.php

diff --git a/extension.neon b/extension.neon
index 4868bc2d..cbdfd73d 100644
--- a/extension.neon
+++ b/extension.neon
@@ -140,6 +140,13 @@ services:
 	-
 		factory: @symfony.parameterMapFactory::create()
 
+	# message map
+	symfony.messageMapFactory:
+		class: PHPStan\Symfony\MessageMapFactory
+		factory: PHPStan\Symfony\MessageMapFactory
+	-
+		factory: @symfony.messageMapFactory::create()
+
 	# ControllerTrait::get()/has() return type
 	-
 		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface)
@@ -203,6 +210,11 @@ services:
 		factory: PHPStan\Type\Symfony\EnvelopeReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
+	# Messenger HandleTrait::handle() return type
+	-
+		class: PHPStan\Type\Symfony\MessengerHandleTraitReturnTypeExtension
+		tags: [phpstan.broker.expressionTypeResolverExtension]
+
 	# InputInterface::getArgument() return type
 	-
 		factory: PHPStan\Type\Symfony\InputInterfaceGetArgumentDynamicReturnTypeExtension
diff --git a/src/Symfony/MessageMap.php b/src/Symfony/MessageMap.php
new file mode 100644
index 00000000..7523742c
--- /dev/null
+++ b/src/Symfony/MessageMap.php
@@ -0,0 +1,24 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Type\Type;
+
+final class MessageMap
+{
+
+	/** @var array<string, Type> */
+	private $messageMap;
+
+	/** @param array<string, Type> $messageMap */
+	public function __construct(array $messageMap)
+	{
+		$this->messageMap = $messageMap;
+	}
+
+	public function getTypeForClass(string $class): ?Type
+	{
+		return $this->messageMap[$class] ?? null;
+	}
+
+}
diff --git a/src/Symfony/MessageMapFactory.php b/src/Symfony/MessageMapFactory.php
new file mode 100644
index 00000000..5c9ef152
--- /dev/null
+++ b/src/Symfony/MessageMapFactory.php
@@ -0,0 +1,154 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Reflection\ClassReflection;
+use PHPStan\Reflection\ReflectionProvider;
+use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
+use function class_exists;
+use function count;
+use function is_array;
+use function is_int;
+use function is_string;
+
+final class MessageMapFactory
+{
+
+	private const MESSENGER_HANDLER_TAG = 'messenger.message_handler';
+	private const DEFAULT_HANDLER_METHOD = '__invoke';
+
+	/** @var ReflectionProvider */
+	private $reflectionProvider;
+
+	/** @var ServiceMap */
+	private $serviceMap;
+
+	public function __construct(ServiceMap $symfonyServiceMap, ReflectionProvider $reflectionProvider)
+	{
+		$this->serviceMap = $symfonyServiceMap;
+		$this->reflectionProvider = $reflectionProvider;
+	}
+
+	public function create(): MessageMap
+	{
+		$returnTypesMap = [];
+
+		foreach ($this->serviceMap->getServices() as $service) {
+			$serviceClass = $service->getClass();
+
+			if ($serviceClass === null) {
+				continue;
+			}
+
+			foreach ($service->getTags() as $tag) {
+				if ($tag->getName() !== self::MESSENGER_HANDLER_TAG) {
+					continue;
+				}
+
+				if (!$this->reflectionProvider->hasClass($serviceClass)) {
+					continue;
+				}
+
+				$reflectionClass = $this->reflectionProvider->getClass($serviceClass);
+
+				/** @var array{handles?: class-string, method?: string} $tagAttributes */
+				$tagAttributes = $tag->getAttributes();
+
+				if (isset($tagAttributes['handles'])) {
+					$handles = [$tagAttributes['handles'] => ['method' => $tagAttributes['method'] ?? self::DEFAULT_HANDLER_METHOD]];
+				} else {
+					$handles = $this->guessHandledMessages($reflectionClass);
+				}
+
+				foreach ($handles as $messageClassName => $options) {
+					$methodName = $options['method'] ?? self::DEFAULT_HANDLER_METHOD;
+
+					if (!$reflectionClass->hasNativeMethod($methodName)) {
+						continue;
+					}
+
+					$methodReflection = $reflectionClass->getNativeMethod($methodName);
+
+					foreach ($methodReflection->getVariants() as $variant) {
+						$returnTypesMap[$messageClassName][] = $variant->getReturnType();
+					}
+				}
+			}
+		}
+
+		$messageMap = [];
+		foreach ($returnTypesMap as $messageClassName => $returnTypes) {
+			if (count($returnTypes) !== 1) {
+				continue;
+			}
+
+			$messageMap[$messageClassName] = $returnTypes[0];
+		}
+
+		return new MessageMap($messageMap);
+	}
+
+	/** @return iterable<string, array<string, string>> */
+	private function guessHandledMessages(ClassReflection $reflectionClass): iterable
+	{
+		if ($reflectionClass->implementsInterface(MessageSubscriberInterface::class)) {
+			$className = $reflectionClass->getName();
+
+			foreach ($className::getHandledMessages() as $index => $value) {
+				$containOptions = self::containOptions($index, $value);
+				if ($containOptions === true) {
+					yield $index => $value;
+				} elseif ($containOptions === false) {
+					yield $value => ['method' => self::DEFAULT_HANDLER_METHOD];
+				}
+			}
+
+			return;
+		}
+
+		if (!$reflectionClass->hasNativeMethod(self::DEFAULT_HANDLER_METHOD)) {
+			return;
+		}
+
+		$methodReflection = $reflectionClass->getNativeMethod(self::DEFAULT_HANDLER_METHOD);
+
+		$variants = $methodReflection->getVariants();
+		if (count($variants) !== 1) {
+			return;
+		}
+
+		$parameters = $variants[0]->getParameters();
+
+		if (count($parameters) !== 1) {
+			return;
+		}
+
+		$classNames = $parameters[0]->getType()->getObjectClassNames();
+
+		if (count($classNames) !== 1) {
+			return;
+		}
+
+		yield $classNames[0] => ['method' => self::DEFAULT_HANDLER_METHOD];
+	}
+
+	/**
+	 * @param mixed $index
+	 * @param mixed $value
+	 * @phpstan-assert-if-true =class-string $index
+	 * @phpstan-assert-if-true =array<string, mixed> $value
+	 * @phpstan-assert-if-false =int $index
+	 * @phpstan-assert-if-false =class-string $value
+	 */
+	private static function containOptions($index, $value): ?bool
+	{
+		if (is_string($index) && class_exists($index) && is_array($value)) {
+			return true;
+		} elseif (is_int($index) && is_string($value) && class_exists($value)) {
+			return false;
+		}
+
+		return null;
+	}
+
+}
diff --git a/src/Symfony/Service.php b/src/Symfony/Service.php
index c31324f5..1cc465ac 100644
--- a/src/Symfony/Service.php
+++ b/src/Symfony/Service.php
@@ -20,12 +20,17 @@ final class Service implements ServiceDefinition
 	/** @var string|null */
 	private $alias;
 
+	/** @var ServiceTag[] */
+	private $tags;
+
+	/** @param ServiceTag[] $tags */
 	public function __construct(
 		string $id,
 		?string $class,
 		bool $public,
 		bool $synthetic,
-		?string $alias
+		?string $alias,
+		array $tags = []
 	)
 	{
 		$this->id = $id;
@@ -33,6 +38,7 @@ public function __construct(
 		$this->public = $public;
 		$this->synthetic = $synthetic;
 		$this->alias = $alias;
+		$this->tags = $tags;
 	}
 
 	public function getId(): string
@@ -60,4 +66,9 @@ public function getAlias(): ?string
 		return $this->alias;
 	}
 
+	public function getTags(): array
+	{
+		return $this->tags;
+	}
+
 }
diff --git a/src/Symfony/ServiceDefinition.php b/src/Symfony/ServiceDefinition.php
index 6df34cba..3862fa8d 100644
--- a/src/Symfony/ServiceDefinition.php
+++ b/src/Symfony/ServiceDefinition.php
@@ -18,4 +18,7 @@ public function isSynthetic(): bool;
 
 	public function getAlias(): ?string;
 
+	/** @return ServiceTag[] */
+	public function getTags(): array;
+
 }
diff --git a/src/Symfony/ServiceTag.php b/src/Symfony/ServiceTag.php
new file mode 100644
index 00000000..a8437fd1
--- /dev/null
+++ b/src/Symfony/ServiceTag.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+final class ServiceTag implements ServiceTagDefinition
+{
+
+	/** @var string */
+	private $name;
+
+	/** @var array<string, string> */
+	private $attributes;
+
+	/** @param array<string, string> $attributes */
+	public function __construct(string $name, array $attributes = [])
+	{
+		$this->name = $name;
+		$this->attributes = $attributes;
+	}
+
+	public function getName(): string
+	{
+		return $this->name;
+	}
+
+	public function getAttributes(): array
+	{
+		return $this->attributes;
+	}
+
+}
diff --git a/src/Symfony/ServiceTagDefinition.php b/src/Symfony/ServiceTagDefinition.php
new file mode 100644
index 00000000..b0f66d9c
--- /dev/null
+++ b/src/Symfony/ServiceTagDefinition.php
@@ -0,0 +1,13 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+interface ServiceTagDefinition
+{
+
+	public function getName(): string;
+
+	/** @return array<string, string> */
+	public function getAttributes(): array;
+
+}
diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 1cae5d97..0c44207e 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -47,12 +47,22 @@ public function create(): ServiceMap
 				continue;
 			}
 
+			$serviceTags = [];
+			foreach ($def->tag as $tag) {
+				$tagAttrs = ((array) $tag->attributes())['@attributes'] ?? [];
+				$tagName = $tagAttrs['name'];
+				unset($tagAttrs['name']);
+
+				$serviceTags[] = new ServiceTag($tagName, $tagAttrs);
+			}
+
 			$service = new Service(
 				$this->cleanServiceId((string) $attrs->id),
 				isset($attrs->class) ? (string) $attrs->class : null,
 				isset($attrs->public) && (string) $attrs->public === 'true',
 				isset($attrs->synthetic) && (string) $attrs->synthetic === 'true',
-				isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null
+				isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null,
+				$serviceTags
 			);
 
 			if ($service->getAlias() !== null) {
diff --git a/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php
new file mode 100644
index 00000000..a5dce362
--- /dev/null
+++ b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php
@@ -0,0 +1,91 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use PhpParser\Node\Expr;
+use PhpParser\Node\Expr\MethodCall;
+use PhpParser\Node\Identifier;
+use PHPStan\Analyser\Scope;
+use PHPStan\Symfony\MessageMap;
+use PHPStan\Symfony\MessageMapFactory;
+use PHPStan\Type\ExpressionTypeResolverExtension;
+use PHPStan\Type\Type;
+use function count;
+use function is_null;
+
+final class MessengerHandleTraitReturnTypeExtension implements ExpressionTypeResolverExtension
+{
+
+	private const TRAIT_NAME = 'Symfony\Component\Messenger\HandleTrait';
+	private const TRAIT_METHOD_NAME = 'handle';
+
+	/** @var MessageMapFactory */
+	private $messageMapFactory;
+
+	/** @var MessageMap|null */
+	private $messageMap;
+
+	public function __construct(MessageMapFactory $symfonyMessageMapFactory)
+	{
+		$this->messageMapFactory = $symfonyMessageMapFactory;
+	}
+
+	public function getType(Expr $expr, Scope $scope): ?Type
+	{
+		if ($this->isSupported($expr, $scope)) {
+			$args = $expr->getArgs();
+			if (count($args) !== 1) {
+				return null;
+			}
+
+			$arg = $args[0]->value;
+			$argClassNames = $scope->getType($arg)->getObjectClassNames();
+
+			if (count($argClassNames) === 1) {
+				$messageMap = $this->getMessageMap();
+				$returnType = $messageMap->getTypeForClass($argClassNames[0]);
+
+				if (!is_null($returnType)) {
+					return $returnType;
+				}
+			}
+		}
+
+		return null;
+	}
+
+	private function getMessageMap(): MessageMap
+	{
+		if ($this->messageMap === null) {
+			$this->messageMap = $this->messageMapFactory->create();
+		}
+
+		return $this->messageMap;
+	}
+
+	/**
+	 * @phpstan-assert-if-true =MethodCall $expr
+	 */
+	private function isSupported(Expr $expr, Scope $scope): bool
+	{
+		if (!($expr instanceof MethodCall) || !($expr->name instanceof Identifier) || $expr->name->name !== self::TRAIT_METHOD_NAME) {
+			return false;
+		}
+
+		if (!$scope->isInClass()) {
+			return false;
+		}
+
+		$reflectionClass = $scope->getClassReflection()->getNativeReflection();
+
+		if (!$reflectionClass->hasMethod(self::TRAIT_METHOD_NAME)) {
+			return false;
+		}
+
+		$methodReflection = $reflectionClass->getMethod(self::TRAIT_METHOD_NAME);
+		$declaringClassReflection = $methodReflection->getBetterReflection()->getDeclaringClass();
+
+		return $declaringClassReflection->getName() === self::TRAIT_NAME;
+	}
+
+}
diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php
index a076caac..40420be0 100644
--- a/tests/Type/Symfony/ExtensionTest.php
+++ b/tests/Type/Symfony/ExtensionTest.php
@@ -14,6 +14,7 @@ class ExtensionTest extends TypeInferenceTestCase
 	/** @return mixed[] */
 	public function dataFileAsserts(): iterable
 	{
+		yield from $this->gatherAssertTypes(__DIR__ . '/data/messenger_handle_trait.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php');
 		yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php');
diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml
index 224c72db..16d4b7fe 100644
--- a/tests/Type/Symfony/container.xml
+++ b/tests/Type/Symfony/container.xml
@@ -354,5 +354,23 @@
         <service id="parameterised_foo" class="%app.class%"></service>
         <service id="parameterised_bar" class="%app.class%\Bar"></service>
         <service id="synthetic" class="Synthetic" public="true" synthetic="true" />
+        <service id="regular_query_handler" class="MessengerHandleTrait\RegularQueryHandler">
+            <tag name="messenger.message_handler"/>
+        </service>
+        <service id="multi_query_handler" class="MessengerHandleTrait\MultiQueryHandler">
+            <tag name="messenger.message_handler"/>
+        </service>
+        <service id="tagged_handler" class="MessengerHandleTrait\TaggedHandler">
+            <tag name="messenger.message_handler" handles="MessengerHandleTrait\TaggedQuery" method="handle"/>
+        </service>
+        <service id="multi_handles_for_in_the_same_handler" class="MessengerHandleTrait\MultiHandlesForInTheSameHandler">
+            <tag name="messenger.message_handler"/>
+        </service>
+        <service id="multi_handlers_for_the_same_message_handler_1" class="MessengerHandleTrait\MultiHandlersForTheSameMessageHandler1">
+            <tag name="messenger.message_handler"/>
+        </service>
+        <service id="multi_handlers_for_the_same_message_handler_2" class="MessengerHandleTrait\MultiHandlersForTheSameMessageHandler2">
+            <tag name="messenger.message_handler"/>
+        </service>
     </services>
 </container>
diff --git a/tests/Type/Symfony/data/messenger_handle_trait.php b/tests/Type/Symfony/data/messenger_handle_trait.php
new file mode 100644
index 00000000..7a86d482
--- /dev/null
+++ b/tests/Type/Symfony/data/messenger_handle_trait.php
@@ -0,0 +1,113 @@
+<?php declare(strict_types = 1);
+
+namespace MessengerHandleTrait;
+
+use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
+use Symfony\Component\Messenger\HandleTrait;
+use function PHPStan\Testing\assertType;
+
+class RegularQuery {}
+class RegularQueryResult {}
+class RegularQueryHandler
+{
+    public function __invoke(RegularQuery $query): RegularQueryResult
+    {
+        return new RegularQueryResult();
+    }
+}
+
+class BooleanQuery {}
+class StringQuery {}
+class IntQuery {}
+class FloatQuery {}
+class MultiQueryHandler implements MessageSubscriberInterface
+{
+    public static function getHandledMessages(): iterable
+    {
+        yield BooleanQuery::class;
+        yield IntQuery::class => ['method' => 'handleInt'];
+        yield FloatQuery::class => ['method' => 'handleFloat'];
+        yield StringQuery::class => ['method' => 'handleString'];
+    }
+
+    public function __invoke(BooleanQuery $query): bool
+    {
+        return true;
+    }
+
+    public function handleInt(IntQuery $query): int
+    {
+        return 0;
+    }
+
+    public function handleFloat(FloatQuery $query): float
+    {
+        return 0.0;
+    }
+
+    public function handleString(StringQuery $query): string
+    {
+        return 'string result';
+    }
+}
+
+class TaggedQuery {}
+class TaggedResult {}
+class TaggedHandler
+{
+    public function handle(TaggedQuery $query): TaggedResult
+    {
+        return new TaggedResult();
+    }
+}
+
+class MultiHandlesForInTheSameHandlerQuery {}
+class MultiHandlesForInTheSameHandler implements MessageSubscriberInterface
+{
+    public static function getHandledMessages(): iterable
+    {
+        yield MultiHandlesForInTheSameHandlerQuery::class;
+        yield MultiHandlesForInTheSameHandlerQuery::class => ['priority' => '0'];
+    }
+
+    public function __invoke(MultiHandlesForInTheSameHandlerQuery $query): bool
+    {
+        return true;
+    }
+}
+
+class MultiHandlersForTheSameMessageQuery {}
+class MultiHandlersForTheSameMessageHandler1
+{
+    public function __invoke(MultiHandlersForTheSameMessageQuery $query): bool
+    {
+        return true;
+    }
+}
+class MultiHandlersForTheSameMessageHandler2
+{
+    public function __invoke(MultiHandlersForTheSameMessageQuery $query): bool
+    {
+        return false;
+    }
+}
+
+class HandleTraitClass {
+    use HandleTrait;
+
+    public function __invoke()
+    {
+        assertType(RegularQueryResult::class, $this->handle(new RegularQuery()));
+
+        assertType('bool', $this->handle(new BooleanQuery()));
+        assertType('int', $this->handle(new IntQuery()));
+        assertType('float', $this->handle(new FloatQuery()));
+        assertType('string', $this->handle(new StringQuery()));
+
+        assertType(TaggedResult::class, $this->handle(new TaggedQuery()));
+
+        // HandleTrait will throw exception in fact due to multiple handle methods/handlers per single query
+        assertType('mixed', $this->handle(new MultiHandlesForInTheSameHandlerQuery()));
+        assertType('mixed', $this->handle(new MultiHandlersForTheSameMessageQuery()));
+    }
+}

From c08cd8e54a08d651bc402d304cfa161c3c3766c4 Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 4 Jan 2025 14:58:15 +0100
Subject: [PATCH 283/297] Fix CS after merge

---
 src/Symfony/MessageMap.php                                  | 2 +-
 src/Symfony/MessageMapFactory.php                           | 6 ++----
 src/Symfony/Service.php                                     | 2 +-
 src/Symfony/ServiceTag.php                                  | 5 ++---
 .../Symfony/MessengerHandleTraitReturnTypeExtension.php     | 6 ++----
 5 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/src/Symfony/MessageMap.php b/src/Symfony/MessageMap.php
index 7523742c..97bb8734 100644
--- a/src/Symfony/MessageMap.php
+++ b/src/Symfony/MessageMap.php
@@ -8,7 +8,7 @@ final class MessageMap
 {
 
 	/** @var array<string, Type> */
-	private $messageMap;
+	private array $messageMap;
 
 	/** @param array<string, Type> $messageMap */
 	public function __construct(array $messageMap)
diff --git a/src/Symfony/MessageMapFactory.php b/src/Symfony/MessageMapFactory.php
index 5c9ef152..3d7663ca 100644
--- a/src/Symfony/MessageMapFactory.php
+++ b/src/Symfony/MessageMapFactory.php
@@ -17,11 +17,9 @@ final class MessageMapFactory
 	private const MESSENGER_HANDLER_TAG = 'messenger.message_handler';
 	private const DEFAULT_HANDLER_METHOD = '__invoke';
 
-	/** @var ReflectionProvider */
-	private $reflectionProvider;
+	private ReflectionProvider $reflectionProvider;
 
-	/** @var ServiceMap */
-	private $serviceMap;
+	private ServiceMap $serviceMap;
 
 	public function __construct(ServiceMap $symfonyServiceMap, ReflectionProvider $reflectionProvider)
 	{
diff --git a/src/Symfony/Service.php b/src/Symfony/Service.php
index 9897f45d..44c0d1d7 100644
--- a/src/Symfony/Service.php
+++ b/src/Symfony/Service.php
@@ -16,7 +16,7 @@ final class Service implements ServiceDefinition
 	private ?string $alias = null;
 
 	/** @var ServiceTag[] */
-	private $tags;
+	private array $tags;
 
 	/** @param ServiceTag[] $tags */
 	public function __construct(
diff --git a/src/Symfony/ServiceTag.php b/src/Symfony/ServiceTag.php
index a8437fd1..3b22ee34 100644
--- a/src/Symfony/ServiceTag.php
+++ b/src/Symfony/ServiceTag.php
@@ -5,11 +5,10 @@
 final class ServiceTag implements ServiceTagDefinition
 {
 
-	/** @var string */
-	private $name;
+	private string $name;
 
 	/** @var array<string, string> */
-	private $attributes;
+	private array $attributes;
 
 	/** @param array<string, string> $attributes */
 	public function __construct(string $name, array $attributes = [])
diff --git a/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php
index a5dce362..2c7b1fbe 100644
--- a/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php
+++ b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php
@@ -19,11 +19,9 @@ final class MessengerHandleTraitReturnTypeExtension implements ExpressionTypeRes
 	private const TRAIT_NAME = 'Symfony\Component\Messenger\HandleTrait';
 	private const TRAIT_METHOD_NAME = 'handle';
 
-	/** @var MessageMapFactory */
-	private $messageMapFactory;
+	private MessageMapFactory $messageMapFactory;
 
-	/** @var MessageMap|null */
-	private $messageMap;
+	private ?MessageMap $messageMap = null;
 
 	public function __construct(MessageMapFactory $symfonyMessageMapFactory)
 	{

From 7417f3a9f6e362b2e5737b5ba553e8c6a9638c1a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Tue, 21 Jan 2025 15:59:07 +0100
Subject: [PATCH 284/297] Configuration class no longer needed, pass arguments
 directly to services

---
 extension.neon                                | 25 ++++++++------
 src/Symfony/Configuration.php                 | 34 -------------------
 src/Symfony/ConsoleApplicationResolver.php    |  4 +--
 src/Symfony/XmlParameterMapFactory.php        |  4 +--
 src/Symfony/XmlServiceMapFactory.php          |  4 +--
 .../ParameterDynamicReturnTypeExtension.php   |  5 ++-
 .../ServiceDynamicReturnTypeExtension.php     |  5 ++-
 ...nerInterfacePrivateServiceRuleFakeTest.php |  3 +-
 ...ntainerInterfacePrivateServiceRuleTest.php |  3 +-
 ...nerInterfaceUnknownServiceRuleFakeTest.php |  3 +-
 ...ntainerInterfaceUnknownServiceRuleTest.php |  3 +-
 .../Symfony/UndefinedArgumentRuleTest.php     |  3 +-
 .../Rules/Symfony/UndefinedOptionRuleTest.php |  3 +-
 tests/Symfony/DefaultParameterMapTest.php     |  4 +--
 tests/Symfony/DefaultServiceMapTest.php       |  4 +--
 15 files changed, 34 insertions(+), 73 deletions(-)
 delete mode 100644 src/Symfony/Configuration.php

diff --git a/extension.neon b/extension.neon
index 26090319..06580626 100644
--- a/extension.neon
+++ b/extension.neon
@@ -101,17 +101,18 @@ parametersSchema:
 	])
 
 services:
-	-
-		factory: PHPStan\Symfony\Configuration(%symfony%)
-
 	# console resolver
 	-
 		factory: PHPStan\Symfony\ConsoleApplicationResolver
+		arguments:
+			consoleApplicationLoader: %symfony.consoleApplicationLoader%
 
 	# service map
 	symfony.serviceMapFactory:
 		class: PHPStan\Symfony\ServiceMapFactory
 		factory: PHPStan\Symfony\XmlServiceMapFactory
+		arguments:
+			containerXmlPath: %symfony.containerXmlPath%
 	-
 		factory: @symfony.serviceMapFactory::create()
 
@@ -119,6 +120,8 @@ services:
 	symfony.parameterMapFactory:
 		class: PHPStan\Symfony\ParameterMapFactory
 		factory: PHPStan\Symfony\XmlParameterMapFactory
+		arguments:
+			containerXmlPath: %symfony.containerXmlPath%
 	-
 		factory: @symfony.parameterMapFactory::create()
 
@@ -131,16 +134,16 @@ services:
 
 	# ControllerTrait::get()/has() return type
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface, %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController)
+		factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
 	# ControllerTrait::has() type specification
@@ -296,20 +299,20 @@ services:
 
 	# ParameterBagInterface::get()/has() return type
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has')
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
 	# ContainerInterface::getParameter()/hasParameter() return type
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter')
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter', %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 
 	# (Abstract)Controller::getParameter() return type
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null)
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
-		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null)
+		factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constantHassers%)
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
 	-
 		class: PHPStan\Symfony\InputBagStubFilesExtension
diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php
deleted file mode 100644
index acdd8508..00000000
--- a/src/Symfony/Configuration.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Symfony;
-
-final class Configuration
-{
-
-	/** @var array<string, mixed> */
-	private array $parameters;
-
-	/**
-	 * @param array<string, mixed> $parameters
-	 */
-	public function __construct(array $parameters)
-	{
-		$this->parameters = $parameters;
-	}
-
-	public function getContainerXmlPath(): ?string
-	{
-		return $this->parameters['containerXmlPath'];
-	}
-
-	public function hasConstantHassers(): bool
-	{
-		return $this->parameters['constantHassers'];
-	}
-
-	public function getConsoleApplicationLoader(): ?string
-	{
-		return $this->parameters['consoleApplicationLoader'];
-	}
-
-}
diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
index a29740b7..13b24d26 100644
--- a/src/Symfony/ConsoleApplicationResolver.php
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -20,9 +20,9 @@ final class ConsoleApplicationResolver
 
 	private ?Application $consoleApplication = null;
 
-	public function __construct(Configuration $configuration)
+	public function __construct(?string $consoleApplicationLoader)
 	{
-		$this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader();
+		$this->consoleApplicationLoader = $consoleApplicationLoader;
 	}
 
 	public function hasConsoleApplicationLoader(): bool
diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php
index afb58ba8..b893308f 100644
--- a/src/Symfony/XmlParameterMapFactory.php
+++ b/src/Symfony/XmlParameterMapFactory.php
@@ -17,9 +17,9 @@ final class XmlParameterMapFactory implements ParameterMapFactory
 
 	private ?string $containerXml = null;
 
-	public function __construct(Configuration $configuration)
+	public function __construct(?string $containerXmlPath)
 	{
-		$this->containerXml = $configuration->getContainerXmlPath();
+		$this->containerXml = $containerXmlPath;
 	}
 
 	public function create(): ParameterMap
diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 684b64d6..734c22c7 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -14,9 +14,9 @@ final class XmlServiceMapFactory implements ServiceMapFactory
 
 	private ?string $containerXml = null;
 
-	public function __construct(Configuration $configuration)
+	public function __construct(?string $containerXmlPath)
 	{
-		$this->containerXml = $configuration->getContainerXmlPath();
+		$this->containerXml = $containerXmlPath;
 	}
 
 	public function create(): ServiceMap
diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
index 5f0fb9bd..687b0c33 100644
--- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
@@ -7,7 +7,6 @@
 use PHPStan\PhpDoc\TypeStringResolver;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\ShouldNotHappenException;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ParameterMap;
 use PHPStan\Type\ArrayType;
 use PHPStan\Type\BooleanType;
@@ -62,7 +61,7 @@ public function __construct(
 		string $className,
 		?string $methodGet,
 		?string $methodHas,
-		Configuration $configuration,
+		bool $constantHassers,
 		ParameterMap $symfonyParameterMap,
 		TypeStringResolver $typeStringResolver
 	)
@@ -70,7 +69,7 @@ public function __construct(
 		$this->className = $className;
 		$this->methodGet = $methodGet;
 		$this->methodHas = $methodHas;
-		$this->constantHassers = $configuration->hasConstantHassers();
+		$this->constantHassers = $constantHassers;
 		$this->parameterMap = $symfonyParameterMap;
 		$this->typeStringResolver = $typeStringResolver;
 	}
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index 13097469..0667d30c 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -6,7 +6,6 @@
 use PHPStan\Analyser\Scope;
 use PHPStan\Reflection\MethodReflection;
 use PHPStan\ShouldNotHappenException;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ParameterMap;
 use PHPStan\Symfony\ServiceDefinition;
 use PHPStan\Symfony\ServiceMap;
@@ -38,13 +37,13 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
 	 */
 	public function __construct(
 		string $className,
-		Configuration $configuration,
+		bool $constantHassers,
 		ServiceMap $symfonyServiceMap,
 		ParameterMap $symfonyParameterMap
 	)
 	{
 		$this->className = $className;
-		$this->constantHassers = $configuration->hasConstantHassers();
+		$this->constantHassers = $constantHassers;
 		$this->serviceMap = $symfonyServiceMap;
 		$this->parameterMap = $symfonyParameterMap;
 	}
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
index e4cb4bea..bbecb2e8 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -3,7 +3,6 @@
 namespace PHPStan\Rules\Symfony;
 
 use PHPStan\Rules\Rule;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use function class_exists;
@@ -17,7 +16,7 @@ final class ContainerInterfacePrivateServiceRuleFakeTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration([])))->create());
+		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(null))->create());
 	}
 
 	public function testGetPrivateService(): void
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index 8c5efe8d..dfa3d2b7 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -3,7 +3,6 @@
 namespace PHPStan\Rules\Symfony;
 
 use PHPStan\Rules\Rule;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use function class_exists;
@@ -17,7 +16,7 @@ final class ContainerInterfacePrivateServiceRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create());
+		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create());
 	}
 
 	public function testGetPrivateService(): void
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
index 065fa067..8d70f1c3 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -4,7 +4,6 @@
 
 use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use PHPStan\Type\MethodTypeSpecifyingExtension;
@@ -20,7 +19,7 @@ final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), self::getContainer()->getByType(Printer::class));
+		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(null))->create(), self::getContainer()->getByType(Printer::class));
 	}
 
 	/**
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
index aa7683e2..c975750f 100644
--- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php
@@ -4,7 +4,6 @@
 
 use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\XmlServiceMapFactory;
 use PHPStan\Testing\RuleTestCase;
 use function class_exists;
@@ -18,7 +17,7 @@ final class ContainerInterfaceUnknownServiceRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), self::getContainer()->getByType(Printer::class));
+		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create(), self::getContainer()->getByType(Printer::class));
 	}
 
 	public function testGetPrivateService(): void
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
index 8b1b869c..d9970ef6 100644
--- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -4,7 +4,6 @@
 
 use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Testing\RuleTestCase;
 
@@ -16,7 +15,7 @@ final class UndefinedArgumentRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class));
+		return new UndefinedArgumentRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), self::getContainer()->getByType(Printer::class));
 	}
 
 	public function testGetArgument(): void
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
index d376fa4b..7f759213 100644
--- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -4,7 +4,6 @@
 
 use PHPStan\Node\Printer\Printer;
 use PHPStan\Rules\Rule;
-use PHPStan\Symfony\Configuration;
 use PHPStan\Symfony\ConsoleApplicationResolver;
 use PHPStan\Testing\RuleTestCase;
 
@@ -16,7 +15,7 @@ final class UndefinedOptionRuleTest extends RuleTestCase
 
 	protected function getRule(): Rule
 	{
-		return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class));
+		return new UndefinedOptionRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), self::getContainer()->getByType(Printer::class));
 	}
 
 	public function testGetArgument(): void
diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php
index 8e2e5b58..018a68a9 100644
--- a/tests/Symfony/DefaultParameterMapTest.php
+++ b/tests/Symfony/DefaultParameterMapTest.php
@@ -13,13 +13,13 @@ final class DefaultParameterMapTest extends TestCase
 	 */
 	public function testGetParameter(string $key, callable $validator): void
 	{
-		$factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml']));
+		$factory = new XmlParameterMapFactory(__DIR__ . '/container.xml');
 		$validator($factory->create()->getParameter($key));
 	}
 
 	public function testGetParameterEscapedPath(): void
 	{
-		$factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml']));
+		$factory = new XmlParameterMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml');
 		$serviceMap = $factory->create();
 
 		self::assertNotNull($serviceMap->getParameter('app.string'));
diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php
index a0a27d98..b43bee49 100644
--- a/tests/Symfony/DefaultServiceMapTest.php
+++ b/tests/Symfony/DefaultServiceMapTest.php
@@ -13,13 +13,13 @@ final class DefaultServiceMapTest extends TestCase
 	 */
 	public function testGetService(string $id, callable $validator): void
 	{
-		$factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml']));
+		$factory = new XmlServiceMapFactory(__DIR__ . '/container.xml');
 		$validator($factory->create()->getService($id));
 	}
 
 	public function testGetContainerEscapedPath(): void
 	{
-		$factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml']));
+		$factory = new XmlServiceMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml');
 		$serviceMap = $factory->create();
 
 		self::assertNotNull($serviceMap->getService('withClass'));

From 65f02c7e585f3c7372e42e14d3d87da034031553 Mon Sep 17 00:00:00 2001
From: Greg Korba <greg@codito.dev>
Date: Tue, 21 Jan 2025 19:57:07 +0100
Subject: [PATCH 285/297] Add result cache meta extension for DI container

---
 composer.json                                 |   2 +-
 extension.neon                                |   5 +
 ...mfonyContainerResultCacheMetaExtension.php |  62 ++++
 src/Symfony/XmlParameterMapFactory.php        |  23 +-
 src/Symfony/XmlServiceMapFactory.php          |  63 ++--
 ...yContainerResultCacheMetaExtensionTest.php | 294 ++++++++++++++++++
 6 files changed, 412 insertions(+), 37 deletions(-)
 create mode 100644 src/Symfony/SymfonyContainerResultCacheMetaExtension.php
 create mode 100644 tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php

diff --git a/composer.json b/composer.json
index 6cf39b10..a832e1bd 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.4 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^2.0"
+    "phpstan/phpstan": "^2.1.2"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index 06580626..34c7d889 100644
--- a/extension.neon
+++ b/extension.neon
@@ -363,3 +363,8 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\ExtensionGetConfigurationReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	-
+		class: PHPStan\Symfony\SymfonyContainerResultCacheMetaExtension
+		tags:
+			- phpstan.resultCacheMetaExtension
diff --git a/src/Symfony/SymfonyContainerResultCacheMetaExtension.php b/src/Symfony/SymfonyContainerResultCacheMetaExtension.php
new file mode 100644
index 00000000..8e2f8028
--- /dev/null
+++ b/src/Symfony/SymfonyContainerResultCacheMetaExtension.php
@@ -0,0 +1,62 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Analyser\ResultCache\ResultCacheMetaExtension;
+use function array_map;
+use function hash;
+use function ksort;
+use function sort;
+use function var_export;
+
+final class SymfonyContainerResultCacheMetaExtension implements ResultCacheMetaExtension
+{
+
+	private ParameterMap $parameterMap;
+
+	private ServiceMap $serviceMap;
+
+	public function __construct(ParameterMap $parameterMap, ServiceMap $serviceMap)
+	{
+		$this->parameterMap = $parameterMap;
+		$this->serviceMap = $serviceMap;
+	}
+
+	public function getKey(): string
+	{
+		return 'symfonyDiContainer';
+	}
+
+	public function getHash(): string
+	{
+		$services = $parameters = [];
+
+		foreach ($this->parameterMap->getParameters() as $parameter) {
+			$parameters[$parameter->getKey()] = $parameter->getValue();
+		}
+		ksort($parameters);
+
+		foreach ($this->serviceMap->getServices() as $service) {
+			$serviceTags = array_map(
+				static fn (ServiceTag $tag) => [
+					'name' => $tag->getName(),
+					'attributes' => $tag->getAttributes(),
+				],
+				$service->getTags(),
+			);
+			sort($serviceTags);
+
+			$services[$service->getId()] = [
+				'class' => $service->getClass(),
+				'public' => $service->isPublic() ? 'yes' : 'no',
+				'synthetic' => $service->isSynthetic() ? 'yes' : 'no',
+				'alias' => $service->getAlias(),
+				'tags' => $serviceTags,
+			];
+		}
+		ksort($services);
+
+		return hash('sha256', var_export(['parameters' => $parameters, 'services' => $services], true));
+	}
+
+}
diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php
index b893308f..4d3d3578 100644
--- a/src/Symfony/XmlParameterMapFactory.php
+++ b/src/Symfony/XmlParameterMapFactory.php
@@ -6,8 +6,10 @@
 use PHPStan\ShouldNotHappenException;
 use SimpleXMLElement;
 use function base64_decode;
+use function count;
 use function file_get_contents;
 use function is_numeric;
+use function ksort;
 use function simplexml_load_string;
 use function sprintf;
 use function strpos;
@@ -40,18 +42,23 @@ public function create(): ParameterMap
 
 		/** @var Parameter[] $parameters */
 		$parameters = [];
-		foreach ($xml->parameters->parameter as $def) {
-			/** @var SimpleXMLElement $attrs */
-			$attrs = $def->attributes();
 
-			$parameter = new Parameter(
-				(string) $attrs->key,
-				$this->getNodeValue($def),
-			);
+		if (count($xml->parameters) > 0) {
+			foreach ($xml->parameters->parameter as $def) {
+				/** @var SimpleXMLElement $attrs */
+				$attrs = $def->attributes();
 
-			$parameters[$parameter->getKey()] = $parameter;
+				$parameter = new Parameter(
+					(string) $attrs->key,
+					$this->getNodeValue($def),
+				);
+
+				$parameters[$parameter->getKey()] = $parameter;
+			}
 		}
 
+		ksort($parameters);
+
 		return new DefaultParameterMap($parameters);
 	}
 
diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index 734c22c7..ac79cb30 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -3,7 +3,9 @@
 namespace PHPStan\Symfony;
 
 use SimpleXMLElement;
+use function count;
 use function file_get_contents;
+use function ksort;
 use function simplexml_load_string;
 use function sprintf;
 use function strpos;
@@ -39,35 +41,38 @@ public function create(): ServiceMap
 		$services = [];
 		/** @var Service[] $aliases */
 		$aliases = [];
-		foreach ($xml->services->service as $def) {
-			/** @var SimpleXMLElement $attrs */
-			$attrs = $def->attributes();
-			if (!isset($attrs->id)) {
-				continue;
-			}
-
-			$serviceTags = [];
-			foreach ($def->tag as $tag) {
-				$tagAttrs = ((array) $tag->attributes())['@attributes'] ?? [];
-				$tagName = $tagAttrs['name'];
-				unset($tagAttrs['name']);
-
-				$serviceTags[] = new ServiceTag($tagName, $tagAttrs);
-			}
-
-			$service = new Service(
-				$this->cleanServiceId((string) $attrs->id),
-				isset($attrs->class) ? (string) $attrs->class : null,
-				isset($attrs->public) && (string) $attrs->public === 'true',
-				isset($attrs->synthetic) && (string) $attrs->synthetic === 'true',
-				isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null,
-				$serviceTags,
-			);
 
-			if ($service->getAlias() !== null) {
-				$aliases[] = $service;
-			} else {
-				$services[$service->getId()] = $service;
+		if (count($xml->services) > 0) {
+			foreach ($xml->services->service as $def) {
+				/** @var SimpleXMLElement $attrs */
+				$attrs = $def->attributes();
+				if (!isset($attrs->id)) {
+					continue;
+				}
+
+				$serviceTags = [];
+				foreach ($def->tag as $tag) {
+					$tagAttrs = ((array) $tag->attributes())['@attributes'] ?? [];
+					$tagName = $tagAttrs['name'];
+					unset($tagAttrs['name']);
+
+					$serviceTags[] = new ServiceTag($tagName, $tagAttrs);
+				}
+
+				$service = new Service(
+					$this->cleanServiceId((string) $attrs->id),
+					isset($attrs->class) ? (string) $attrs->class : null,
+					isset($attrs->public) && (string) $attrs->public === 'true',
+					isset($attrs->synthetic) && (string) $attrs->synthetic === 'true',
+					isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null,
+					$serviceTags,
+				);
+
+				if ($service->getAlias() !== null) {
+					$aliases[] = $service;
+				} else {
+					$services[$service->getId()] = $service;
+				}
 			}
 		}
 		foreach ($aliases as $service) {
@@ -85,6 +90,8 @@ public function create(): ServiceMap
 			);
 		}
 
+		ksort($services);
+
 		return new DefaultServiceMap($services);
 	}
 
diff --git a/tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php b/tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php
new file mode 100644
index 00000000..f5c8503f
--- /dev/null
+++ b/tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php
@@ -0,0 +1,294 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Testing\PHPStanTestCase;
+use function count;
+
+/**
+ * @phpstan-type ContainerContents array{parameters?: ParameterMap, services?: ServiceMap}
+ */
+final class SymfonyContainerResultCacheMetaExtensionTest extends PHPStanTestCase
+{
+
+	/**
+	 * @param list<ContainerContents> $sameHashContents
+	 * @param ContainerContents $invalidatingContent
+	 *
+	 * @dataProvider provideContainerHashIsCalculatedCorrectlyCases
+	 */
+	public function testContainerHashIsCalculatedCorrectly(
+		array $sameHashContents,
+		array $invalidatingContent
+	): void
+	{
+		$hash = null;
+
+		self::assertGreaterThan(0, count($sameHashContents));
+
+		foreach ($sameHashContents as $content) {
+			$currentHash = (new SymfonyContainerResultCacheMetaExtension(
+				$content['parameters'] ?? new DefaultParameterMap([]),
+				$content['services'] ?? new DefaultServiceMap([]),
+			))->getHash();
+
+			if ($hash === null) {
+				$hash = $currentHash;
+			} else {
+				self::assertSame($hash, $currentHash);
+			}
+		}
+
+		self::assertNotSame(
+			$hash,
+			(new SymfonyContainerResultCacheMetaExtension(
+				$invalidatingContent['parameters'] ?? new DefaultParameterMap([]),
+				$invalidatingContent['services'] ?? new DefaultServiceMap([]),
+			))->getHash(),
+		);
+	}
+
+	/**
+	 * @return iterable<string, array{list<ContainerContents>, ContainerContents}>
+	 */
+	public static function provideContainerHashIsCalculatedCorrectlyCases(): iterable
+	{
+		yield 'service "class" changes' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null),
+						new Service('Bar', 'Bar', true, false, null),
+					]),
+				],
+				// Swapping services order in XML file does not affect the calculated hash
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Bar', 'Bar', true, false, null),
+						new Service('Foo', 'Foo', true, false, null),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, false, null),
+					new Service('Bar', 'BarAdapter', true, false, null),
+				]),
+			],
+		];
+
+		yield 'service visibility changes' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', false, false, null),
+				]),
+			],
+		];
+
+		yield 'service syntheticity changes' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, true, null),
+				]),
+			],
+		];
+
+		yield 'service alias changes' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null),
+						new Service('Bar', 'Bar', true, false, null),
+						new Service('Baz', null, true, false, 'Foo'),
+					]),
+				],
+				// Swapping services order in XML file does not affect the calculated hash
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Baz', null, true, false, 'Foo'),
+						new Service('Bar', 'Bar', true, false, null),
+						new Service('Foo', 'Foo', true, false, null),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, false, null),
+					new Service('Bar', 'Bar', true, false, null),
+					new Service('Baz', null, true, false, 'Bar'),
+				]),
+			],
+		];
+
+		yield 'service tag attributes changes' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null, [
+							new ServiceTag('foo.bar', ['baz' => 'bar']),
+							new ServiceTag('foo.baz', ['baz' => 'baz']),
+						]),
+					]),
+				],
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null, [
+							new ServiceTag('foo.baz', ['baz' => 'baz']),
+							new ServiceTag('foo.bar', ['baz' => 'bar']),
+						]),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, false, null, [
+						new ServiceTag('foo.bar', ['baz' => 'bar']),
+						new ServiceTag('foo.baz', ['baz' => 'buzz']),
+					]),
+				]),
+			],
+		];
+
+		yield 'service tag added' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null, [
+							new ServiceTag('foo.bar', ['baz' => 'bar']),
+						]),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, false, null, [
+						new ServiceTag('foo.bar', ['baz' => 'bar']),
+						new ServiceTag('foo.baz', ['baz' => 'baz']),
+					]),
+				]),
+			],
+		];
+
+		yield 'service tag removed' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null, [
+							new ServiceTag('foo.bar', ['baz' => 'bar']),
+							new ServiceTag('foo.baz', ['baz' => 'baz']),
+						]),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, false, null, [
+						new ServiceTag('foo.bar', ['baz' => 'bar']),
+					]),
+				]),
+			],
+		];
+
+		yield 'new service added' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, false, null),
+					new Service('Bar', 'Bar', true, false, null),
+				]),
+			],
+		];
+
+		yield 'service removed' => [
+			[
+				[
+					'services' => new DefaultServiceMap([
+						new Service('Foo', 'Foo', true, false, null),
+						new Service('Bar', 'Bar', true, false, null),
+					]),
+				],
+			],
+			[
+				'services' => new DefaultServiceMap([
+					new Service('Foo', 'Foo', true, false, null),
+				]),
+			],
+		];
+
+		yield 'parameter value changes' => [
+			[
+				[
+					'parameters' => new DefaultParameterMap([
+						new Parameter('foo', 'foo'),
+						new Parameter('bar', 'bar'),
+					]),
+				],
+				[
+					'parameters' => new DefaultParameterMap([
+						new Parameter('bar', 'bar'),
+						new Parameter('foo', 'foo'),
+					]),
+				],
+			],
+			[
+				'parameters' => new DefaultParameterMap([
+					new Parameter('foo', 'foo'),
+					new Parameter('bar', 'buzz'),
+				]),
+			],
+		];
+
+		yield 'new parameter added' => [
+			[
+				[
+					'parameters' => new DefaultParameterMap([
+						new Parameter('foo', 'foo'),
+					]),
+				],
+			],
+			[
+				'parameters' => new DefaultParameterMap([
+					new Parameter('foo', 'foo'),
+					new Parameter('bar', 'bar'),
+				]),
+			],
+		];
+
+		yield 'parameter removed' => [
+			[
+				[
+					'parameters' => new DefaultParameterMap([
+						new Parameter('foo', 'foo'),
+						new Parameter('bar', 'bar'),
+					]),
+				],
+			],
+			[
+				'parameters' => new DefaultParameterMap([
+					new Parameter('foo', 'foo'),
+				]),
+			],
+		];
+	}
+
+}

From d589514e03653639845bd4750db325f83176dbd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= <ondrej@mirtes.cz>
Date: Tue, 28 Jan 2025 10:24:48 +0100
Subject: [PATCH 286/297] Update LICENSE

---
 LICENSE | 1 +
 1 file changed, 1 insertion(+)

diff --git a/LICENSE b/LICENSE
index 0b9f74d9..cb2e557c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
 The MIT License (MIT)
 
 Copyright (c) 2017 Lukáš Unger
+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

From 08b97ab6621a57d6bbb8add1a358c5bf25cd98df Mon Sep 17 00:00:00 2001
From: Christophe Coevoet <stof@notk.org>
Date: Wed, 19 Mar 2025 12:54:42 +0100
Subject: [PATCH 287/297] Synchronize the EventSubscriberInterface with the
 upstream type

As of Symfony 5.4, Symfony ships a precise type for this interface but the stub overrides it.
---
 .../Component/EventDispatcher/EventSubscriberInterface.stub     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub b/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
index 35a77c93..62474d10 100644
--- a/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
+++ b/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub
@@ -5,7 +5,7 @@ namespace Symfony\Component\EventDispatcher;
 interface EventSubscriberInterface
 {
     /**
-     * @return array<string, string|array<string, int>|array<int, string|array<string, int>|array<int, string>>>
+     * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
      */
     public static function getSubscribedEvents();
 }

From deec7cc280ea37e0af5fad8afb1f64e91f67b7a0 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 24 Mar 2025 01:48:24 +0000
Subject: [PATCH 288/297] chore(deps): 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 78b6b5a62f56731d938031c8f59817ed83b2328a Mon Sep 17 00:00:00 2001
From: Christophe Coevoet <stof@notk.org>
Date: Wed, 19 Mar 2025 18:31:11 +0100
Subject: [PATCH 289/297] Remove the generic type for PasswordUpgraderInterface

This interface method can be called on a user provider even for user
types not supported by that provider (for instance when using a chain
provider). The implementation is expected to deal with that case
gracefully, which will be enforced by phpstan when the generics are
gone.
---
 extension.neon                                |  5 ---
 ...ordAuthenticatedUserStubFilesExtension.php | 36 -------------------
 .../PasswordAuthenticatedUserInterface.stub   |  7 ----
 .../Core/User/PasswordUpgraderInterface.stub  | 14 --------
 4 files changed, 62 deletions(-)
 delete mode 100644 src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
 delete mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
 delete mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub

diff --git a/extension.neon b/extension.neon
index cbdfd73d..a38fd4bf 100644
--- a/extension.neon
+++ b/extension.neon
@@ -22,7 +22,6 @@ parameters:
 			- Symfony\Component\Form\FormTypeInterface
 			- Symfony\Component\OptionsResolver\Options
 			- Symfony\Component\Security\Core\Authorization\Voter\Voter
-			- Symfony\Component\Security\Core\User\PasswordUpgraderInterface
 	stubFiles:
 		- stubs/Psr/Cache/CacheException.stub
 		- stubs/Psr/Cache/CacheItemInterface.stub
@@ -333,10 +332,6 @@ services:
 		class: PHPStan\Symfony\InputBagStubFilesExtension
 		tags:
 			- phpstan.stubFilesExtension
-	-
-		class: PHPStan\Symfony\PasswordAuthenticatedUserStubFilesExtension
-		tags:
-			- phpstan.stubFilesExtension
 	-
 		class: PHPStan\Symfony\SymfonyDiagnoseExtension
 		tags:
diff --git a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
deleted file mode 100644
index 8f8c4782..00000000
--- a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php declare(strict_types = 1);
-
-namespace PHPStan\Symfony;
-
-use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound;
-use PHPStan\BetterReflection\Reflector\Reflector;
-use PHPStan\PhpDoc\StubFilesExtension;
-
-class PasswordAuthenticatedUserStubFilesExtension implements StubFilesExtension
-{
-
-	/** @var Reflector */
-	private $reflector;
-
-	public function __construct(
-		Reflector $reflector
-	)
-	{
-		$this->reflector = $reflector;
-	}
-
-	public function getFiles(): array
-	{
-		try {
-			$this->reflector->reflectClass('Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface');
-		} catch (IdentifierNotFound $e) {
-			return [];
-		}
-
-		return [
-			__DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub',
-			__DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub',
-		];
-	}
-
-}
diff --git a/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub b/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
deleted file mode 100644
index 19cc6040..00000000
--- a/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Core\User;
-
-interface PasswordAuthenticatedUserInterface
-{
-}
diff --git a/stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub b/stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub
deleted file mode 100644
index c73afc55..00000000
--- a/stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Symfony\Component\Security\Core\User;
-
-/**
- * @template TUser of PasswordAuthenticatedUserInterface
- */
-interface PasswordUpgraderInterface
-{
-	/**
-	 * @param TUser $user
-	 */
-	public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void;
-}

From dfb238748214bd390b3bfe3795375f930955bb3e Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 7 Apr 2025 03:24:16 +0000
Subject: [PATCH 290/297] chore(deps): 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 1430c2d0a385ac4987c91cddc6cc4c51fdb397ba Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Sat, 19 Apr 2025 22:05:57 +0200
Subject: [PATCH 291/297] Fix build

---
 composer.json         |  2 +-
 phpstan-baseline.neon | 63 +++++++++++++++++++++++++++++++++++++++----
 2 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/composer.json b/composer.json
index a832e1bd..19c6a8be 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
   "require": {
     "php": "^7.4 || ^8.0",
     "ext-simplexml": "*",
-    "phpstan/phpstan": "^2.1.2"
+    "phpstan/phpstan": "^2.1.13"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 0f6edd5c..79e87db9 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,26 +1,79 @@
 parameters:
 	ignoreErrors:
 		-
-			message: "#^Although PHPStan\\\\Reflection\\\\Php\\\\PhpPropertyReflection is covered by backward compatibility promise, this instanceof assumption might break because it's not guaranteed to always stay the same\\.$#"
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Rules/Symfony/UndefinedArgumentRule.php
+
+		-
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Rules/Symfony/UndefinedOptionRule.php
+
+		-
+			message: '#^Although PHPStan\\Reflection\\Php\\PhpPropertyReflection is covered by backward compatibility promise, this instanceof assumption might break because it''s not guaranteed to always stay the same\.$#'
+			identifier: phpstanApi.instanceofAssumption
 			count: 1
 			path: src/Symfony/RequiredAutowiringExtension.php
 
 		-
-			message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Console\\\\Input\\\\InputOption and 'isNegatable' will always evaluate to true\\.$#"
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php
+
+		-
+			message: '#^Call to function method_exists\(\) with Symfony\\Component\\Console\\Input\\InputOption and ''isNegatable'' will always evaluate to true\.$#'
+			identifier: function.alreadyNarrowedType
 			count: 1
 			path: src/Type/Symfony/GetOptionTypeHelper.php
 
 		-
-			message: "#^Accessing PHPStan\\\\Rules\\\\Methods\\\\CallMethodsRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
+
+		-
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
+
+		-
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php
+
+		-
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
+
+		-
+			message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#'
+			identifier: method.internal
+			count: 1
+			path: src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
+
+		-
+			message: '#^Accessing PHPStan\\Rules\\Methods\\CallMethodsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#'
+			identifier: phpstanApi.classConstant
 			count: 1
 			path: tests/Rules/NonexistentInputBagClassTest.php
 
 		-
-			message: "#^Accessing PHPStan\\\\Rules\\\\Properties\\\\UninitializedPropertyRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
+			message: '#^Accessing PHPStan\\Rules\\Properties\\UninitializedPropertyRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#'
+			identifier: phpstanApi.classConstant
 			count: 1
 			path: tests/Symfony/RequiredAutowiringExtensionTest.php
 
 		-
-			message: "#^Accessing PHPStan\\\\Rules\\\\Comparison\\\\ImpossibleCheckTypeMethodCallRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
+			message: '#^Accessing PHPStan\\Rules\\Comparison\\ImpossibleCheckTypeMethodCallRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#'
+			identifier: phpstanApi.classConstant
 			count: 1
 			path: tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php

From cf8c9c6994cf2a07c7aefddef2b26941d19efba1 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 17 Mar 2025 01:51:32 +0000
Subject: [PATCH 292/297] chore(deps): update dependency psr/container to
 v1.1.2

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 19c6a8be..c03d2c99 100644
--- a/composer.json
+++ b/composer.json
@@ -25,7 +25,7 @@
     "phpstan/phpstan-phpunit": "^2.0",
     "phpstan/phpstan-strict-rules": "^2.0",
     "phpunit/phpunit": "^9.6",
-    "psr/container": "1.0 || 1.1.1",
+    "psr/container": "1.1.2",
     "symfony/config": "^5.4 || ^6.1",
     "symfony/console": "^5.4 || ^6.1",
     "symfony/dependency-injection": "^5.4 || ^6.1",

From 582ddbc641cee028386e985972c631d57ceef406 Mon Sep 17 00:00:00 2001
From: Marko Kaznovac <kaznovac@users.noreply.github.com>
Date: Sun, 11 May 2025 21:13:47 +0200
Subject: [PATCH 293/297] Export-ignore github and editor config; remove
 leftover excludes

---
 .gitattributes | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 1545ee73..94b7c8c2 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,11 +1,11 @@
 *.php text eol=lf
 
+.github export-ignore
 tests export-ignore
 tmp export-ignore
-.coveralls.yml export-ignore
+.editorconfig export-ignore
 .gitattributes export-ignore
 .gitignore export-ignore
-.travis.yml export-ignore
 Makefile export-ignore
 phpstan.neon export-ignore
 phpstan-baseline.neon export-ignore

From 5005288e07583546ea00b52de4a9ac412eb869d7 Mon Sep 17 00:00:00 2001
From: Vincent Langlet <VincentLanglet@users.noreply.github.com>
Date: Wed, 14 May 2025 09:00:05 +0200
Subject: [PATCH 294/297] Do not narrow `KernelTestCase::getContainer()` return
 type to internal TestContainer

---
 extension.neon                                |  3 ---
 .../ContainerInterfacePrivateServiceRule.php  | 25 +++++++++++++++++--
 .../Bundle/FrameworkBundle/KernelBrowser.stub | 13 ----------
 .../FrameworkBundle/Test/KernelTestCase.stub  | 16 ------------
 .../FrameworkBundle/Test/TestContainer.stub   |  7 ------
 ...ntainerInterfacePrivateServiceRuleTest.php | 10 ++++++++
 tests/Rules/Symfony/ExampleTest.php           | 23 +++++++++++++++++
 7 files changed, 56 insertions(+), 41 deletions(-)
 delete mode 100644 stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
 delete mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
 delete mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
 create mode 100644 tests/Rules/Symfony/ExampleTest.php

diff --git a/extension.neon b/extension.neon
index 0803248f..c76d573f 100644
--- a/extension.neon
+++ b/extension.neon
@@ -13,9 +13,6 @@ parameters:
 		- stubs/Psr/Cache/CacheItemInterface.stub
 		- stubs/Psr/Cache/InvalidArgumentException.stub
 		- stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub
-		- stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
-		- stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
-		- stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
 		- stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub
 		- stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub
 		- stubs/Symfony/Component/Console/Command.stub
diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index 96f1efea..c8ec11a8 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -43,11 +43,11 @@ public function processNode(Node $node, Scope $scope): array
 
 		$argType = $scope->getType($node->var);
 
-		$isTestContainerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Test\TestContainer'))->isSuperTypeOf($argType);
+		$isTestContainer = $this->isTestContainer($argType, $scope);
 		$isOldServiceSubscriber = (new ObjectType('Symfony\Component\DependencyInjection\ServiceSubscriberInterface'))->isSuperTypeOf($argType);
 		$isServiceSubscriber = $this->isServiceSubscriber($argType, $scope);
 		$isServiceLocator = (new ObjectType('Symfony\Component\DependencyInjection\ServiceLocator'))->isSuperTypeOf($argType);
-		if ($isTestContainerType->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes() || $isServiceLocator->yes()) {
+		if ($isTestContainer->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes() || $isServiceLocator->yes()) {
 			return [];
 		}
 
@@ -91,4 +91,25 @@ private function isServiceSubscriber(Type $containerType, Scope $scope): Trinary
 		return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)->result);
 	}
 
+	private function isTestContainer(Type $containerType, Scope $scope): TrinaryLogic
+	{
+		$testContainer = new ObjectType('Symfony\Bundle\FrameworkBundle\Test\TestContainer');
+		$isTestContainer = $testContainer->isSuperTypeOf($containerType)->result;
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			return $isTestContainer;
+		}
+
+		$containerInterface = new ObjectType('Symfony\Component\DependencyInjection\ContainerInterface');
+		$kernelTestCase = new ObjectType('Symfony\Bundle\FrameworkBundle\Test\KernelTestCase');
+		$containedClassType = new ObjectType($classReflection->getName());
+
+		return $isTestContainer->or(
+			$containerInterface->isSuperTypeOf($containerType)->result->and(
+				$kernelTestCase->isSuperTypeOf($containedClassType)->result,
+			),
+		);
+	}
+
 }
diff --git a/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub b/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
deleted file mode 100644
index ec54f1eb..00000000
--- a/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub
+++ /dev/null
@@ -1,13 +0,0 @@
-<?php
-
-namespace Symfony\Bundle\FrameworkBundle;
-
-use Symfony\Bundle\FrameworkBundle\Test\TestContainer;
-
-abstract class KernelBrowser
-{
-    /**
-     * @return TestContainer
-     */
-    abstract public function getContainer();
-}
diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
deleted file mode 100644
index 88654f6c..00000000
--- a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-namespace Symfony\Bundle\FrameworkBundle\Test;
-
-abstract class KernelTestCase
-{
-    /**
-     * @var TestContainer
-     */
-    protected static $container;
-
-    /**
-     * @return TestContainer
-     */
-    abstract public function getContainer();
-}
diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub b/stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
deleted file mode 100644
index 37f81545..00000000
--- a/stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Symfony\Bundle\FrameworkBundle\Test;
-
-class TestContainer
-{
-}
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
index dfa3d2b7..31dd3680 100644
--- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php
@@ -77,4 +77,14 @@ public function testGetPrivateServiceInServiceSubscriber(): void
 		);
 	}
 
+	public function testGetPrivateServiceInTest(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleTest.php',
+			],
+			[],
+		);
+	}
+
 }
diff --git a/tests/Rules/Symfony/ExampleTest.php b/tests/Rules/Symfony/ExampleTest.php
new file mode 100644
index 00000000..10b06703
--- /dev/null
+++ b/tests/Rules/Symfony/ExampleTest.php
@@ -0,0 +1,23 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use Symfony\Bundle\FrameworkBundle\KernelBrowser;
+use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
+
+abstract class ExampleTest extends KernelTestCase
+{
+
+	public function bar(): void
+	{
+		$container = self::getContainer();
+		$container->get('private');
+	}
+
+	public function foo(KernelBrowser $browser): void
+	{
+		$container = $browser->getContainer();
+		$container->get('private');
+	}
+
+}

From 9c272bdc7f87f27a8965f44458d0d96416b3620a Mon Sep 17 00:00:00 2001
From: Ondrej Mirtes <ondrej@mirtes.cz>
Date: Thu, 29 May 2025 21:19:35 +0200
Subject: [PATCH 295/297] Add PropertyAccessor stub file alongside
 PropertyAccessorInterface

---
 extension.neon                                |  1 +
 .../PropertyAccess/PropertyAccessor.stub      | 23 +++++++++++++++++++
 2 files changed, 24 insertions(+)
 create mode 100644 stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub

diff --git a/extension.neon b/extension.neon
index c76d573f..ecc2ddf3 100644
--- a/extension.neon
+++ b/extension.neon
@@ -55,6 +55,7 @@ parameters:
 		- stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub
 		- stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub
 		- stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub
+		- stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub
 		- stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub
 		- stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub
diff --git a/stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub b/stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub
new file mode 100644
index 00000000..af5423f0
--- /dev/null
+++ b/stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub
@@ -0,0 +1,23 @@
+<?php
+
+namespace Symfony\Component\PropertyAccess;
+
+class PropertyAccessor implements PropertyAccessorInterface
+{
+
+    /**
+     * @phpstan-template T of object|array<mixed>
+     * @phpstan-param T &$objectOrArray
+     * @phpstan-param-out ($objectOrArray is object ? T : array<mixed>) $objectOrArray
+     * @phpstan-param string|PropertyPathInterface $propertyPath
+     * @phpstan-param mixed $value
+     *
+     * @return void
+     *
+     * @throws Exception\InvalidArgumentException If the property path is invalid
+     * @throws Exception\AccessException          If a property/index does not exist or is not public
+     * @throws Exception\UnexpectedTypeException  If a value within the path is neither object nor array
+     */
+    public function setValue(&$objectOrArray, $propertyPath, $value);
+
+}

From 392f7ab8f52a0a776977be4e62535358c28e1b15 Mon Sep 17 00:00:00 2001
From: Nicolas Reynis <nicolas@reyn.is>
Date: Tue, 20 May 2025 14:23:09 +0200
Subject: [PATCH 296/297] fix: Improve ParameterBag stub

---
 stubs/Symfony/Component/HttpFoundation/ParameterBag.stub | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
index 0a6858e3..e412e9e0 100644
--- a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
+++ b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub
@@ -3,12 +3,12 @@
 namespace Symfony\Component\HttpFoundation;
 
 /**
- * @implements \IteratorAggregate<string, mixed>
+ * @implements \IteratorAggregate<array-key, mixed>
  */
 class ParameterBag implements \IteratorAggregate
 {
     /**
-     * @return list<string>
+     * @return list<array-key>
      */
     public function keys(): array
     {

From 3d8847fb5865e5e3319b2189ead03fc6a3482eb2 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 28 Jul 2025 14:15:20 +0000
Subject: [PATCH 297/297] chore(deps): update eomm/why-don-t-you-tweet action
 to v2

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

diff --git a/.github/workflows/release-tweet.yml b/.github/workflows/release-tweet.yml
index 09b39ded..d81f34ca 100644
--- a/.github/workflows/release-tweet.yml
+++ b/.github/workflows/release-tweet.yml
@@ -10,7 +10,7 @@ jobs:
   tweet:
     runs-on: ubuntu-latest
     steps:
-      - uses: Eomm/why-don-t-you-tweet@v1
+      - uses: Eomm/why-don-t-you-tweet@v2
         if: ${{ !github.event.repository.private }}
         with:
           # GitHub event payload
