diff --git a/README.md b/README.md
index 87f25054..492c8795 100644
--- a/README.md
+++ b/README.md
@@ -11,8 +11,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 `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.
 * 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`.
 
 ## Usage
 
@@ -55,3 +58,21 @@ parameters:
 ```
 
 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().
+
+```
+parameters:
+	symfony:
+		console_application_loader: tests/console-application.php
+```
+
+For example, in a Symfony project, `console-application.php` would look something like this:
+
+```php
+require dirname(__DIR__).'/../config/bootstrap.php';
+$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
+return new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
+```
diff --git a/composer.json b/composer.json
index 4118192a..16e4658d 100644
--- a/composer.json
+++ b/composer.json
@@ -33,8 +33,9 @@
     "phpstan/phpstan-phpunit": "^0.11",
     "symfony/framework-bundle": "^3.0 || ^4.0",
     "squizlabs/php_codesniffer": "^3.3.2",
-    "symfony/serializer": "^3|^4",
-    "symfony/messenger": "^4.2"
+    "symfony/serializer": "^3.0 || ^4.0",
+    "symfony/messenger": "^4.2",
+    "symfony/console": "^3.0 || ^4.0"
   },
   "conflict": {
     "symfony/framework-bundle": "<3.0"
diff --git a/extension.neon b/extension.neon
index ca7288f2..a98009f5 100644
--- a/extension.neon
+++ b/extension.neon
@@ -1,12 +1,23 @@
 parameters:
 	symfony:
+		container_xml_path: null
 		constant_hassers: true
+		console_application_loader: null
 
 rules:
 	- PHPStan\Rules\Symfony\ContainerInterfacePrivateServiceRule
 	- PHPStan\Rules\Symfony\ContainerInterfaceUnknownServiceRule
+	- PHPStan\Rules\Symfony\UndefinedArgumentRule
+	- PHPStan\Rules\Symfony\InvalidArgumentDefaultValueRule
+	- PHPStan\Rules\Symfony\UndefinedOptionRule
+	- PHPStan\Rules\Symfony\InvalidOptionDefaultValueRule
 
 services:
+	# console resolver
+	-
+		factory: PHPStan\Symfony\ConsoleApplicationResolver
+		arguments: [%symfony.console_application_loader%]
+
 	# service map
 	symfony.serviceMapFactory:
 		class: PHPStan\Symfony\ServiceMapFactory
@@ -55,3 +66,33 @@ services:
 	-
 		factory: PHPStan\Type\Symfony\EnvelopeReturnTypeExtension
 		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# InputInterface::getArgument() return type
+	-
+		factory: PHPStan\Type\Symfony\InputInterfaceGetArgumentDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# InputInterface::hasArgument() type specification
+	-
+		factory: PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension
+		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
+
+	# InputInterface::hasArgument() return type
+	-
+		factory: PHPStan\Type\Symfony\InputInterfaceHasArgumentDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# InputInterface::getOption() return type
+	-
+		factory: PHPStan\Type\Symfony\InputInterfaceGetOptionDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
+
+	# InputInterface::hasOption() type specification
+	-
+		factory: PHPStan\Type\Symfony\OptionTypeSpecifyingExtension
+		tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
+
+	# InputInterface::hasOption() return type
+	-
+		factory: PHPStan\Type\Symfony\InputInterfaceHasOptionDynamicReturnTypeExtension
+		tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
diff --git a/phpstan.neon b/phpstan.neon
index 66c99036..4dd356bf 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -7,6 +7,8 @@ parameters:
 	excludes_analyse:
 		- */tests/tmp/*
 		- */tests/*/Example*.php
+		- */tests/*/console_application_loader.php
+		- */tests/*/envelope_all.php
 		- */tests/*/header_bag_get.php
 		- */tests/*/request_get_content.php
 		- */tests/*/serializer.php
diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
index b9e978a6..9e4d6aa5 100644
--- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php
@@ -62,7 +62,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [];
 		}
 
-		$serviceId = ServiceMap::getServiceIdFromNode($node->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($node->args[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 c7265b81..0e147982 100644
--- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
+++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php
@@ -59,7 +59,7 @@ public function processNode(Node $node, Scope $scope): array
 			return [];
 		}
 
-		$serviceId = ServiceMap::getServiceIdFromNode($node->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($node->args[0]->value, $scope);
 		if ($serviceId !== null) {
 			$service = $this->serviceMap->getService($serviceId);
 			$serviceIdType = $scope->getType($node->args[0]->value);
diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
new file mode 100644
index 00000000..9da3d536
--- /dev/null
+++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php
@@ -0,0 +1,78 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PhpParser\Node;
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Rules\Rule;
+use PHPStan\ShouldNotHappenException;
+use PHPStan\Type\ArrayType;
+use PHPStan\Type\Constant\ConstantIntegerType;
+use PHPStan\Type\IntegerType;
+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 sprintf;
+
+final class InvalidArgumentDefaultValueRule implements Rule
+{
+
+	public function getNodeType(): string
+	{
+		return MethodCall::class;
+	}
+
+	/**
+	 * @param \PhpParser\Node $node
+	 * @param \PHPStan\Analyser\Scope $scope
+	 * @return (string|\PHPStan\Rules\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 [];
+		}
+		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'addArgument') {
+			return [];
+		}
+		if (!isset($node->args[3])) {
+			return [];
+		}
+
+		$modeType = isset($node->args[1]) ? $scope->getType($node->args[1]->value) : new NullType();
+		if ($modeType instanceof NullType) {
+			$modeType = new ConstantIntegerType(2); // InputArgument::OPTIONAL
+		}
+		$modeTypes = TypeUtils::getConstantScalars($modeType);
+		if (count($modeTypes) !== 1) {
+			return [];
+		}
+		if (!$modeTypes[0] instanceof ConstantIntegerType) {
+			return [];
+		}
+		$mode = $modeTypes[0]->getValue();
+
+		$defaultType = $scope->getType($node->args[3]->value);
+
+		// 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()))];
+		}
+
+		// 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 [];
+	}
+
+}
diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
new file mode 100644
index 00000000..8af4da19
--- /dev/null
+++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php
@@ -0,0 +1,86 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PhpParser\Node;
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Rules\Rule;
+use PHPStan\ShouldNotHappenException;
+use PHPStan\Type\ArrayType;
+use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\Constant\ConstantIntegerType;
+use PHPStan\Type\IntegerType;
+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;
+use function sprintf;
+
+final class InvalidOptionDefaultValueRule implements Rule
+{
+
+	public function getNodeType(): string
+	{
+		return MethodCall::class;
+	}
+
+	/**
+	 * @param \PhpParser\Node $node
+	 * @param \PHPStan\Analyser\Scope $scope
+	 * @return (string|\PHPStan\Rules\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 [];
+		}
+		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'addOption') {
+			return [];
+		}
+		if (!isset($node->args[4])) {
+			return [];
+		}
+
+		$modeType = isset($node->args[2]) ? $scope->getType($node->args[2]->value) : new NullType();
+		if ($modeType instanceof NullType) {
+			$modeType = new ConstantIntegerType(1); // InputOption::VALUE_NONE
+		}
+		$modeTypes = TypeUtils::getConstantScalars($modeType);
+		if (count($modeTypes) !== 1) {
+			return [];
+		}
+		if (!$modeTypes[0] instanceof ConstantIntegerType) {
+			return [];
+		}
+		$mode = $modeTypes[0]->getValue();
+
+		$defaultType = $scope->getType($node->args[4]->value);
+
+		// 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));
+			}
+			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()))];
+			}
+		}
+
+		// is array
+		if (($mode & 8) === 8 && !(new UnionType([new ArrayType(new IntegerType(), new StringType()), new NullType()]))->isSuperTypeOf($defaultType)->yes()) {
+			return [sprintf('Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array<int, string>|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()))];
+		}
+
+		return [];
+	}
+
+}
diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php
new file mode 100644
index 00000000..39af2ba0
--- /dev/null
+++ b/src/Rules/Symfony/UndefinedArgumentRule.php
@@ -0,0 +1,90 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use InvalidArgumentException;
+use PhpParser\Node;
+use PhpParser\Node\Expr\MethodCall;
+use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Analyser\Scope;
+use PHPStan\Rules\Rule;
+use PHPStan\ShouldNotHappenException;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\Symfony\Helper;
+use PHPStan\Type\TypeUtils;
+use function count;
+use function sprintf;
+
+final class UndefinedArgumentRule implements Rule
+{
+
+	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	/** @var \PhpParser\PrettyPrinter\Standard */
+	private $printer;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+		$this->printer = $printer;
+	}
+
+	public function getNodeType(): string
+	{
+		return MethodCall::class;
+	}
+
+	/**
+	 * @param \PhpParser\Node $node
+	 * @param \PHPStan\Analyser\Scope $scope
+	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 */
+	public function processNode(Node $node, Scope $scope): array
+	{
+		if (!$node instanceof MethodCall) {
+			throw new ShouldNotHappenException();
+		};
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			throw new ShouldNotHappenException();
+		}
+
+		if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf(new ObjectType($classReflection->getName()))->yes()) {
+			return [];
+		}
+		if (!(new ObjectType('Symfony\Component\Console\Input\InputInterface'))->isSuperTypeOf($scope->getType($node->var))->yes()) {
+			return [];
+		}
+		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'getArgument') {
+			return [];
+		}
+		if (!isset($node->args[0])) {
+			return [];
+		}
+
+		$argType = $scope->getType($node->args[0]->value);
+		$argStrings = TypeUtils::getConstantStrings($argType);
+		if (count($argStrings) !== 1) {
+			return [];
+		}
+		$argName = $argStrings[0]->getValue();
+
+		$errors = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $name => $command) {
+			try {
+				$command->getDefinition()->getArgument($argName);
+			} catch (InvalidArgumentException $e) {
+				if ($scope->getType(Helper::createMarkerNode($node->var, $argType, $this->printer))->equals($argType)) {
+					continue;
+				}
+				$errors[] = sprintf('Command "%s" does not define argument "%s".', $name, $argName);
+			}
+		}
+
+		return $errors;
+	}
+
+}
diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php
new file mode 100644
index 00000000..656d13fd
--- /dev/null
+++ b/src/Rules/Symfony/UndefinedOptionRule.php
@@ -0,0 +1,90 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use InvalidArgumentException;
+use PhpParser\Node;
+use PhpParser\Node\Expr\MethodCall;
+use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Analyser\Scope;
+use PHPStan\Rules\Rule;
+use PHPStan\ShouldNotHappenException;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Type\ObjectType;
+use PHPStan\Type\Symfony\Helper;
+use PHPStan\Type\TypeUtils;
+use function count;
+use function sprintf;
+
+final class UndefinedOptionRule implements Rule
+{
+
+	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	/** @var \PhpParser\PrettyPrinter\Standard */
+	private $printer;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+		$this->printer = $printer;
+	}
+
+	public function getNodeType(): string
+	{
+		return MethodCall::class;
+	}
+
+	/**
+	 * @param \PhpParser\Node $node
+	 * @param \PHPStan\Analyser\Scope $scope
+	 * @return (string|\PHPStan\Rules\RuleError)[] errors
+	 */
+	public function processNode(Node $node, Scope $scope): array
+	{
+		if (!$node instanceof MethodCall) {
+			throw new ShouldNotHappenException();
+		};
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			throw new ShouldNotHappenException();
+		}
+
+		if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf(new ObjectType($classReflection->getName()))->yes()) {
+			return [];
+		}
+		if (!(new ObjectType('Symfony\Component\Console\Input\InputInterface'))->isSuperTypeOf($scope->getType($node->var))->yes()) {
+			return [];
+		}
+		if (!$node->name instanceof Node\Identifier || $node->name->name !== 'getOption') {
+			return [];
+		}
+		if (!isset($node->args[0])) {
+			return [];
+		}
+
+		$optType = $scope->getType($node->args[0]->value);
+		$optStrings = TypeUtils::getConstantStrings($optType);
+		if (count($optStrings) !== 1) {
+			return [];
+		}
+		$optName = $optStrings[0]->getValue();
+
+		$errors = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $name => $command) {
+			try {
+				$command->getDefinition()->getOption($optName);
+			} catch (InvalidArgumentException $e) {
+				if ($scope->getType(Helper::createMarkerNode($node->var, $optType, $this->printer))->equals($optType)) {
+					continue;
+				}
+				$errors[] = sprintf('Command "%s" does not define option "%s".', $name, $optName);
+			}
+		}
+
+		return $errors;
+	}
+
+}
diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php
new file mode 100644
index 00000000..9a684d17
--- /dev/null
+++ b/src/Symfony/ConsoleApplicationResolver.php
@@ -0,0 +1,66 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PHPStan\Reflection\ClassReflection;
+use PHPStan\ShouldNotHappenException;
+use PHPStan\Type\ObjectType;
+use function file_exists;
+use function get_class;
+use function is_readable;
+
+final class ConsoleApplicationResolver
+{
+
+	/** @var \Symfony\Component\Console\Application|null */
+	private $consoleApplication;
+
+	public function __construct(?string $consoleApplicationLoader)
+	{
+		if ($consoleApplicationLoader === null) {
+			return;
+		}
+		$this->consoleApplication = $this->loadConsoleApplication($consoleApplicationLoader);
+	}
+
+	/**
+	 * @return \Symfony\Component\Console\Application|null
+	 * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
+	 */
+	private function loadConsoleApplication(string $consoleApplicationLoader)
+	{
+		if (!file_exists($consoleApplicationLoader)
+			|| !is_readable($consoleApplicationLoader)
+		) {
+			throw new ShouldNotHappenException();
+		}
+
+		return require $consoleApplicationLoader;
+	}
+
+	/**
+	 * @return \Symfony\Component\Console\Command\Command[]
+	 */
+	public function findCommands(ClassReflection $classReflection): array
+	{
+		if ($this->consoleApplication === null) {
+			return [];
+		}
+
+		$classType = new ObjectType($classReflection->getName());
+		if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($classType)->yes()) {
+			return [];
+		}
+
+		$commands = [];
+		foreach ($this->consoleApplication->all() as $name => $command) {
+			if (!$classType->isSuperTypeOf(new ObjectType(get_class($command)))->yes()) {
+				continue;
+			}
+			$commands[$name] = $command;
+		}
+
+		return $commands;
+	}
+
+}
diff --git a/src/Symfony/DefaultServiceMap.php b/src/Symfony/DefaultServiceMap.php
new file mode 100644
index 00000000..6339b450
--- /dev/null
+++ b/src/Symfony/DefaultServiceMap.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 DefaultServiceMap implements ServiceMap
+{
+
+	/** @var \PHPStan\Symfony\ServiceDefinition[] */
+	private $services;
+
+	/**
+	 * @param \PHPStan\Symfony\ServiceDefinition[] $services
+	 */
+	public function __construct(array $services)
+	{
+		$this->services = $services;
+	}
+
+	/**
+	 * @return \PHPStan\Symfony\ServiceDefinition[]
+	 */
+	public function getServices(): array
+	{
+		return $this->services;
+	}
+
+	public function getService(string $id): ?ServiceDefinition
+	{
+		return $this->services[$id] ?? null;
+	}
+
+	public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string
+	{
+		$strings = TypeUtils::getConstantStrings($scope->getType($node));
+		return count($strings) === 1 ? $strings[0]->getValue() : null;
+	}
+
+}
diff --git a/src/Symfony/FakeServiceMap.php b/src/Symfony/FakeServiceMap.php
new file mode 100644
index 00000000..d05fe8ed
--- /dev/null
+++ b/src/Symfony/FakeServiceMap.php
@@ -0,0 +1,29 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Symfony;
+
+use PhpParser\Node\Expr;
+use PHPStan\Analyser\Scope;
+
+final class FakeServiceMap implements ServiceMap
+{
+
+	/**
+	 * @return \PHPStan\Symfony\ServiceDefinition[]
+	 */
+	public function getServices(): array
+	{
+		return [];
+	}
+
+	public function getService(string $id): ?ServiceDefinition
+	{
+		return null;
+	}
+
+	public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string
+	{
+		return null;
+	}
+
+}
diff --git a/src/Symfony/ServiceMap.php b/src/Symfony/ServiceMap.php
index 71da7e9a..b954dc72 100644
--- a/src/Symfony/ServiceMap.php
+++ b/src/Symfony/ServiceMap.php
@@ -4,40 +4,17 @@
 
 use PhpParser\Node\Expr;
 use PHPStan\Analyser\Scope;
-use PHPStan\Type\TypeUtils;
-use function count;
 
-final class ServiceMap
+interface ServiceMap
 {
 
-	/** @var \PHPStan\Symfony\ServiceDefinition[] */
-	private $services;
-
-	/**
-	 * @param \PHPStan\Symfony\ServiceDefinition[] $services
-	 */
-	public function __construct(array $services)
-	{
-		$this->services = $services;
-	}
-
 	/**
 	 * @return \PHPStan\Symfony\ServiceDefinition[]
 	 */
-	public function getServices(): array
-	{
-		return $this->services;
-	}
+	public function getServices(): array;
 
-	public function getService(string $id): ?ServiceDefinition
-	{
-		return $this->services[$id] ?? null;
-	}
+	public function getService(string $id): ?ServiceDefinition;
 
-	public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string
-	{
-		$strings = TypeUtils::getConstantStrings($scope->getType($node));
-		return count($strings) === 1 ? $strings[0]->getValue() : null;
-	}
+	public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string;
 
 }
diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php
index de40d695..0f575891 100644
--- a/src/Symfony/XmlServiceMapFactory.php
+++ b/src/Symfony/XmlServiceMapFactory.php
@@ -10,16 +10,20 @@
 final class XmlServiceMapFactory implements ServiceMapFactory
 {
 
-	/** @var string */
+	/** @var string|null */
 	private $containerXml;
 
-	public function __construct(string $containerXml)
+	public function __construct(?string $containerXml)
 	{
 		$this->containerXml = $containerXml;
 	}
 
 	public function create(): ServiceMap
 	{
+		if ($this->containerXml === null) {
+			return new FakeServiceMap();
+		}
+
 		$fileContents = file_get_contents($this->containerXml);
 		if ($fileContents === false) {
 			throw new XmlContainerNotExistsException(sprintf('Container %s does not exist or cannot be parsed', $this->containerXml));
@@ -70,7 +74,7 @@ public function create(): ServiceMap
 			);
 		}
 
-		return new ServiceMap($services);
+		return new DefaultServiceMap($services);
 	}
 
 }
diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
new file mode 100644
index 00000000..edf5574c
--- /dev/null
+++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php
@@ -0,0 +1,57 @@
+<?php declare(strict_types = 1);
+
+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\Reflection\MethodReflection;
+use PHPStan\Type\MethodTypeSpecifyingExtension;
+
+final class ArgumentTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
+{
+
+	/** @var \PhpParser\PrettyPrinter\Standard */
+	private $printer;
+
+	/** @var \PHPStan\Analyser\TypeSpecifier */
+	private $typeSpecifier;
+
+	public function __construct(Standard $printer)
+	{
+		$this->printer = $printer;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Input\InputInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
+	{
+		return $methodReflection->getName() === 'hasArgument' && !$context->null();
+	}
+
+	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
+	{
+		if (!isset($node->args[0])) {
+			return new SpecifiedTypes();
+		}
+		$argType = $scope->getType($node->args[0]->value);
+		return $this->typeSpecifier->create(
+			Helper::createMarkerNode($node->var, $argType, $this->printer),
+			$argType,
+			$context
+		);
+	}
+
+	public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
+	{
+		$this->typeSpecifier = $typeSpecifier;
+	}
+
+}
diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..235a1dce
--- /dev/null
+++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php
@@ -0,0 +1,85 @@
+<?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\ShouldNotHappenException;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Type\ArrayType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\IntegerType;
+use PHPStan\Type\StringType;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeCombinator;
+use PHPStan\Type\TypeUtils;
+use function count;
+
+final class InputInterfaceGetArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Input\InputInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getArgument';
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	{
+		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->args, $methodReflection->getVariants())->getReturnType();
+
+		if (!isset($methodCall->args[0])) {
+			return $defaultReturnType;
+		}
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			throw new ShouldNotHappenException();
+		}
+
+		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		if (count($argStrings) !== 1) {
+			return $defaultReturnType;
+		}
+		$argName = $argStrings[0]->getValue();
+
+		$argTypes = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
+			try {
+				$argument = $command->getDefinition()->getArgument($argName);
+				if ($argument->isArray()) {
+					$argType = new ArrayType(new IntegerType(), new StringType());
+					if (!$argument->isRequired() && $argument->getDefault() !== []) {
+						$argType = TypeCombinator::union($argType, $scope->getTypeFromValue($argument->getDefault()));
+					}
+				} else {
+					$argType = new StringType();
+					if (!$argument->isRequired()) {
+						$argType = TypeCombinator::union($argType, $scope->getTypeFromValue($argument->getDefault()));
+					}
+				}
+				$argTypes[] = $argType;
+			} catch (InvalidArgumentException $e) {
+				// noop
+			}
+		}
+
+		return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : $defaultReturnType;
+	}
+
+}
diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..f340a5d7
--- /dev/null
+++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php
@@ -0,0 +1,88 @@
+<?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\ShouldNotHappenException;
+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;
+use function count;
+
+final class InputInterfaceGetOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Input\InputInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'getOption';
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	{
+		$defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->args, $methodReflection->getVariants())->getReturnType();
+
+		if (!isset($methodCall->args[0])) {
+			return $defaultReturnType;
+		}
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			throw new ShouldNotHappenException();
+		}
+
+		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		if (count($optStrings) !== 1) {
+			return $defaultReturnType;
+		}
+		$optName = $optStrings[0]->getValue();
+
+		$optTypes = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
+			try {
+				$option = $command->getDefinition()->getOption($optName);
+				if (!$option->acceptValue()) {
+					$optType = new BooleanType();
+				} else {
+					$optType = TypeCombinator::union(new StringType(), new IntegerType(), 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 = TypeCombinator::union($optType, $scope->getTypeFromValue($option->getDefault()));
+				}
+				$optTypes[] = $optType;
+			} catch (InvalidArgumentException $e) {
+				// noop
+			}
+		}
+
+		return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : $defaultReturnType;
+	}
+
+}
diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..b0e471df
--- /dev/null
+++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php
@@ -0,0 +1,77 @@
+<?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\ShouldNotHappenException;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Type\BooleanType;
+use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeUtils;
+use function array_unique;
+use function count;
+
+final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Input\InputInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'hasArgument';
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	{
+		$defaultReturnType = new BooleanType();
+
+		if (!isset($methodCall->args[0])) {
+			return $defaultReturnType;
+		}
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			throw new ShouldNotHappenException();
+		}
+
+		$argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		if (count($argStrings) !== 1) {
+			return $defaultReturnType;
+		}
+		$argName = $argStrings[0]->getValue();
+
+		$returnTypes = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
+			try {
+				$command->getDefinition()->getArgument($argName);
+				$returnTypes[] = true;
+			} catch (InvalidArgumentException $e) {
+				$returnTypes[] = false;
+			}
+		}
+
+		if (count($returnTypes) === 0) {
+			return $defaultReturnType;
+		}
+
+		$returnTypes = array_unique($returnTypes);
+		return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType;
+	}
+
+}
diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
new file mode 100644
index 00000000..3c5a1a3d
--- /dev/null
+++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php
@@ -0,0 +1,77 @@
+<?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\ShouldNotHappenException;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Type\BooleanType;
+use PHPStan\Type\Constant\ConstantBooleanType;
+use PHPStan\Type\DynamicMethodReturnTypeExtension;
+use PHPStan\Type\Type;
+use PHPStan\Type\TypeUtils;
+use function array_unique;
+use function count;
+
+final class InputInterfaceHasOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
+{
+
+	/** @var \PHPStan\Symfony\ConsoleApplicationResolver */
+	private $consoleApplicationResolver;
+
+	public function __construct(ConsoleApplicationResolver $consoleApplicationResolver)
+	{
+		$this->consoleApplicationResolver = $consoleApplicationResolver;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Input\InputInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection): bool
+	{
+		return $methodReflection->getName() === 'hasOption';
+	}
+
+	public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
+	{
+		$defaultReturnType = new BooleanType();
+
+		if (!isset($methodCall->args[0])) {
+			return $defaultReturnType;
+		}
+
+		$classReflection = $scope->getClassReflection();
+		if ($classReflection === null) {
+			throw new ShouldNotHappenException();
+		}
+
+		$optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value));
+		if (count($optStrings) !== 1) {
+			return $defaultReturnType;
+		}
+		$optName = $optStrings[0]->getValue();
+
+		$returnTypes = [];
+		foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) {
+			try {
+				$command->getDefinition()->getOption($optName);
+				$returnTypes[] = true;
+			} catch (InvalidArgumentException $e) {
+				$returnTypes[] = false;
+			}
+		}
+
+		if (count($returnTypes) === 0) {
+			return $defaultReturnType;
+		}
+
+		$returnTypes = array_unique($returnTypes);
+		return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType;
+	}
+
+}
diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
new file mode 100644
index 00000000..aaff84c0
--- /dev/null
+++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php
@@ -0,0 +1,57 @@
+<?php declare(strict_types = 1);
+
+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\Reflection\MethodReflection;
+use PHPStan\Type\MethodTypeSpecifyingExtension;
+
+final class OptionTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
+{
+
+	/** @var \PhpParser\PrettyPrinter\Standard */
+	private $printer;
+
+	/** @var \PHPStan\Analyser\TypeSpecifier */
+	private $typeSpecifier;
+
+	public function __construct(Standard $printer)
+	{
+		$this->printer = $printer;
+	}
+
+	public function getClass(): string
+	{
+		return 'Symfony\Component\Console\Input\InputInterface';
+	}
+
+	public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
+	{
+		return $methodReflection->getName() === 'hasOption' && !$context->null();
+	}
+
+	public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
+	{
+		if (!isset($node->args[0])) {
+			return new SpecifiedTypes();
+		}
+		$argType = $scope->getType($node->args[0]->value);
+		return $this->typeSpecifier->create(
+			Helper::createMarkerNode($node->var, $argType, $this->printer),
+			$argType,
+			$context
+		);
+	}
+
+	public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
+	{
+		$this->typeSpecifier = $typeSpecifier;
+	}
+
+}
diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
index 48e8aacf..6b2f6f84 100644
--- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
+++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
@@ -24,13 +24,13 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
 	private $constantHassers;
 
 	/** @var \PHPStan\Symfony\ServiceMap */
-	private $symfonyServiceMap;
+	private $serviceMap;
 
 	public function __construct(string $className, bool $constantHassers, ServiceMap $symfonyServiceMap)
 	{
 		$this->className = $className;
 		$this->constantHassers = $constantHassers;
-		$this->symfonyServiceMap = $symfonyServiceMap;
+		$this->serviceMap = $symfonyServiceMap;
 	}
 
 	public function getClass(): string
@@ -65,9 +65,9 @@ private function getGetTypeFromMethodCall(
 			return $returnType;
 		}
 
-		$serviceId = ServiceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
 		if ($serviceId !== null) {
-			$service = $this->symfonyServiceMap->getService($serviceId);
+			$service = $this->serviceMap->getService($serviceId);
 			if ($service !== null && !$service->isSynthetic()) {
 				return new ObjectType($service->getClass() ?? $serviceId);
 			}
@@ -87,9 +87,9 @@ private function getHasTypeFromMethodCall(
 			return $returnType;
 		}
 
-		$serviceId = ServiceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
+		$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
 		if ($serviceId !== null) {
-			$service = $this->symfonyServiceMap->getService($serviceId);
+			$service = $this->serviceMap->getService($serviceId);
 			return new ConstantBooleanType($service !== null && $service->isPublic());
 		}
 
diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
new file mode 100644
index 00000000..db79af6c
--- /dev/null
+++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php
@@ -0,0 +1,59 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PHPStan\Rules\Rule;
+use PHPStan\Symfony\XmlServiceMapFactory;
+use PHPStan\Testing\RuleTestCase;
+
+final class ContainerInterfacePrivateServiceRuleFakeTest extends RuleTestCase
+{
+
+	protected function getRule(): Rule
+	{
+		return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(null))->create());
+	}
+
+	public function testGetPrivateService(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleController.php',
+			],
+			[]
+		);
+	}
+
+	public function testGetPrivateServiceInLegacyServiceSubscriber(): void
+	{
+		if (!interface_exists('Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface')) {
+			self::markTestSkipped('The test needs Symfony\Component\DependencyInjection\ServiceSubscriberInterface class.');
+		}
+
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleLegacyServiceSubscriber.php',
+				__DIR__ . '/ExampleLegacyServiceSubscriberFromAbstractController.php',
+				__DIR__ . '/ExampleLegacyServiceSubscriberFromLegacyController.php',
+			],
+			[]
+		);
+	}
+
+	public function testGetPrivateServiceInServiceSubscriber(): void
+	{
+		if (!interface_exists('Symfony\Contracts\Service\ServiceSubscriberInterface')) {
+			self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.');
+		}
+
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleServiceSubscriber.php',
+				__DIR__ . '/ExampleServiceSubscriberFromAbstractController.php',
+				__DIR__ . '/ExampleServiceSubscriberFromLegacyController.php',
+			],
+			[]
+		);
+	}
+
+}
diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
new file mode 100644
index 00000000..cc0f2977
--- /dev/null
+++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Rules\Rule;
+use PHPStan\Symfony\XmlServiceMapFactory;
+use PHPStan\Testing\RuleTestCase;
+use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+
+final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase
+{
+
+	protected function getRule(): Rule
+	{
+		return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(null))->create(), new Standard());
+	}
+
+	/**
+	 * @return \PHPStan\Type\MethodTypeSpecifyingExtension[]
+	 */
+	protected function getMethodTypeSpecifyingExtensions(): array
+	{
+		return [
+			new ServiceTypeSpecifyingExtension(Controller::class, new Standard()),
+		];
+	}
+
+	public function testGetPrivateService(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleController.php',
+			],
+			[]
+		);
+	}
+
+}
diff --git a/tests/Rules/Symfony/ExampleCommand.php b/tests/Rules/Symfony/ExampleCommand.php
new file mode 100644
index 00000000..ba3fe7bf
--- /dev/null
+++ b/tests/Rules/Symfony/ExampleCommand.php
@@ -0,0 +1,53 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+final class ExampleCommand extends Command
+{
+
+	protected function configure(): void
+	{
+		$this->setName('example-rule');
+
+		$this->addArgument('arg');
+
+		$this->addArgument('foo1', null, '', null);
+		$this->addArgument('bar1', null, '', '');
+		$this->addArgument('baz1', null, '', 1);
+		$this->addArgument('quz1', null, '', ['']);
+
+		$this->addArgument('quz2', InputArgument::IS_ARRAY, '', ['a' => 'b']);
+
+		$this->addOption('aaa');
+
+		$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);
+	}
+
+	protected function execute(InputInterface $input, OutputInterface $output): int
+	{
+		$input->getArgument('arg');
+		$input->getArgument('undefined');
+
+		if ($input->hasArgument('guarded')) {
+			$input->getArgument('guarded');
+		}
+
+		$input->getOption('aaa');
+		$input->getOption('bbb');
+
+		if ($input->hasOption('ccc')) {
+			$input->getOption('ccc');
+		}
+
+		return 0;
+	}
+
+}
diff --git a/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
new file mode 100644
index 00000000..cc06fc0b
--- /dev/null
+++ b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php
@@ -0,0 +1,39 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PHPStan\Rules\Rule;
+use PHPStan\Testing\RuleTestCase;
+
+final class InvalidArgumentDefaultValueRuleTest extends RuleTestCase
+{
+
+	protected function getRule(): Rule
+	{
+		return new InvalidArgumentDefaultValueRule();
+	}
+
+	public function testGetArgument(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleCommand.php',
+			],
+			[
+				[
+					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, int given.',
+					22,
+				],
+				[
+					'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, array<int, string> given.',
+					23,
+				],
+				[
+					'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
new file mode 100644
index 00000000..66ea42f7
--- /dev/null
+++ b/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PHPStan\Rules\Rule;
+use PHPStan\Testing\RuleTestCase;
+
+final class InvalidOptionDefaultValueRuleTest extends RuleTestCase
+{
+
+	protected function getRule(): Rule
+	{
+		return new InvalidOptionDefaultValueRule();
+	}
+
+	public function testGetArgument(): void
+	{
+		$this->analyse(
+			[
+				__DIR__ . '/ExampleCommand.php',
+			],
+			[
+				[
+					'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array<int, string>|null, array<int, int> given.',
+					29,
+				],
+			]
+		);
+	}
+
+}
diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
new file mode 100644
index 00000000..842f70a7
--- /dev/null
+++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php
@@ -0,0 +1,44 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Rules\Rule;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Testing\RuleTestCase;
+use PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension;
+
+final class UndefinedArgumentRuleTest extends RuleTestCase
+{
+
+	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(
+			[
+				__DIR__ . '/ExampleCommand.php',
+			],
+			[
+				[
+					'Command "example-rule" does not define argument "undefined".',
+					37,
+				],
+			]
+		);
+	}
+
+}
diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
new file mode 100644
index 00000000..1c70e36f
--- /dev/null
+++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php
@@ -0,0 +1,44 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Rules\Symfony;
+
+use PhpParser\PrettyPrinter\Standard;
+use PHPStan\Rules\Rule;
+use PHPStan\Symfony\ConsoleApplicationResolver;
+use PHPStan\Testing\RuleTestCase;
+use PHPStan\Type\Symfony\OptionTypeSpecifyingExtension;
+
+final class UndefinedOptionRuleTest extends RuleTestCase
+{
+
+	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(
+			[
+				__DIR__ . '/ExampleCommand.php',
+			],
+			[
+				[
+					'Command "example-rule" does not define option "bbb".',
+					44,
+				],
+			]
+		);
+	}
+
+}
diff --git a/tests/Rules/Symfony/console_application_loader.php b/tests/Rules/Symfony/console_application_loader.php
new file mode 100644
index 00000000..05f8ed51
--- /dev/null
+++ b/tests/Rules/Symfony/console_application_loader.php
@@ -0,0 +1,10 @@
+<?php declare(strict_types = 1);
+
+use PHPStan\Rules\Symfony\ExampleCommand;
+use Symfony\Component\Console\Application;
+
+require_once __DIR__ . '/../../../vendor/autoload.php';
+
+$application = new Application();
+$application->add(new ExampleCommand());
+return $application;
diff --git a/tests/Symfony/ServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php
similarity index 98%
rename from tests/Symfony/ServiceMapTest.php
rename to tests/Symfony/DefaultServiceMapTest.php
index 5bbbe412..5a1c1801 100644
--- a/tests/Symfony/ServiceMapTest.php
+++ b/tests/Symfony/DefaultServiceMapTest.php
@@ -5,7 +5,7 @@
 use Iterator;
 use PHPUnit\Framework\TestCase;
 
-final class ServiceMapTest extends TestCase
+final class DefaultServiceMapTest extends TestCase
 {
 
 	/**
diff --git a/tests/Symfony/NeonTest.php b/tests/Symfony/NeonTest.php
index a189b043..18b90a9e 100644
--- a/tests/Symfony/NeonTest.php
+++ b/tests/Symfony/NeonTest.php
@@ -25,8 +25,8 @@ public function testExtensionNeon(): void
 		$class = $loader->load(function (Compiler $compiler): void {
 			$compiler->addExtension('rules', new RulesExtension());
 			$compiler->addConfig(['parameters' => ['rootDir' => __DIR__]]);
-			$compiler->loadConfig(__DIR__ . '/config.neon');
 			$compiler->loadConfig(__DIR__ . '/../../extension.neon');
+			$compiler->loadConfig(__DIR__ . '/config.neon');
 		}, $key);
 		/** @var \Nette\DI\Container $container */
 		$container = new $class();
@@ -36,12 +36,13 @@ public function testExtensionNeon(): void
 			'symfony' => [
 				'container_xml_path' => __DIR__ . '/container.xml',
 				'constant_hassers' => true,
+				'console_application_loader' => null,
 			],
 		], $container->getParameters());
 
-		self::assertCount(2, $container->findByTag('phpstan.rules.rule'));
-		self::assertCount(7, $container->findByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
-		self::assertCount(3, $container->findByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
+		self::assertCount(6, $container->findByTag('phpstan.rules.rule'));
+		self::assertCount(11, $container->findByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
+		self::assertCount(5, $container->findByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
 		self::assertInstanceOf(ServiceMap::class, $container->getByType(ServiceMap::class));
 	}
 
diff --git a/tests/Type/Symfony/ExampleACommand.php b/tests/Type/Symfony/ExampleACommand.php
new file mode 100644
index 00000000..4ba27410
--- /dev/null
+++ b/tests/Type/Symfony/ExampleACommand.php
@@ -0,0 +1,21 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Symfony\Component\Console\Input\InputArgument;
+
+final class ExampleACommand extends ExampleBaseCommand
+{
+
+	protected function configure(): void
+	{
+		parent::configure();
+		$this->setName('example-a');
+
+		$this->addArgument('aaa', null, '', 'aaa');
+		$this->addArgument('both');
+		$this->addArgument('diff', null, '', 'ddd');
+		$this->addArgument('arr', InputArgument::IS_ARRAY, '', ['arr']);
+	}
+
+}
diff --git a/tests/Type/Symfony/ExampleBCommand.php b/tests/Type/Symfony/ExampleBCommand.php
new file mode 100644
index 00000000..b6b00dc2
--- /dev/null
+++ b/tests/Type/Symfony/ExampleBCommand.php
@@ -0,0 +1,20 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\Type\Symfony;
+
+use Symfony\Component\Console\Input\InputArgument;
+
+final class ExampleBCommand extends ExampleBaseCommand
+{
+
+	protected function configure(): void
+	{
+		parent::configure();
+		$this->setName('example-b');
+
+		$this->addArgument('both');
+		$this->addArgument('bbb', null, '', 'bbb');
+		$this->addArgument('diff', InputArgument::IS_ARRAY, '', ['diff']);
+	}
+
+}
diff --git a/tests/Type/Symfony/ExampleBaseCommand.php b/tests/Type/Symfony/ExampleBaseCommand.php
new file mode 100644
index 00000000..b058ee2c
--- /dev/null
+++ b/tests/Type/Symfony/ExampleBaseCommand.php
@@ -0,0 +1,31 @@
+<?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\Output\OutputInterface;
+
+abstract class ExampleBaseCommand extends Command
+{
+
+	protected function configure(): void
+	{
+		parent::configure();
+
+		$this->addArgument('base');
+	}
+
+	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;
+	}
+
+}
diff --git a/tests/Type/Symfony/ExampleOptionCommand.php b/tests/Type/Symfony/ExampleOptionCommand.php
new file mode 100644
index 00000000..c18d55e2
--- /dev/null
+++ b/tests/Type/Symfony/ExampleOptionCommand.php
@@ -0,0 +1,46 @@
+<?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;
+
+final class ExampleOptionCommand extends Command
+{
+
+	protected function configure(): void
+	{
+		parent::configure();
+		$this->setName('example-option');
+
+		$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
+	{
+		$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;
+	}
+
+}
diff --git a/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
new file mode 100644
index 00000000..216321f4
--- /dev/null
+++ b/tests/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtensionTest.php
@@ -0,0 +1,34 @@
+<?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'))
+		);
+	}
+
+	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
new file mode 100644
index 00000000..6e949ce6
--- /dev/null
+++ b/tests/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtensionTest.php
@@ -0,0 +1,38 @@
+<?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'))
+		);
+	}
+
+	public function argumentTypesProvider(): Iterator
+	{
+		yield ['$a', 'bool'];
+		yield ['$b', 'int|string|null'];
+		yield ['$c', 'int|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/ServiceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
index a1ef5100..9d77d459 100644
--- a/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
+++ b/tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
@@ -12,45 +12,57 @@ final class ServiceDynamicReturnTypeExtensionTest extends ExtensionTestCase
 	/**
 	 * @dataProvider servicesProvider
 	 */
-	public function testServices(string $expression, string $type): void
+	public function testServices(string $expression, string $type, ?string $container): void
 	{
 		$this->processFile(
 			__DIR__ . '/ExampleController.php',
 			$expression,
 			$type,
-			new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
+			new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory($container))->create())
 		);
 	}
 
 	public function servicesProvider(): Iterator
 	{
-		yield ['$service1', 'Foo'];
-		yield ['$service2', 'object'];
-		yield ['$service3', 'object'];
-		yield ['$service4', 'object'];
-		yield ['$has1', 'true'];
-		yield ['$has2', 'false'];
-		yield ['$has3', 'bool'];
-		yield ['$has4', 'bool'];
+		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): void
+	public function testConstantHassersOff(string $expression, string $type, ?string $container): void
 	{
 		$this->processFile(
 			__DIR__ . '/ExampleController.php',
 			$expression,
 			$type,
-			new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
+			new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory($container))->create())
 		);
 	}
 
 	public function constantHassersOffProvider(): Iterator
 	{
-		yield ['$has1', 'bool'];
-		yield ['$has2', 'bool'];
+		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/console_application_loader.php b/tests/Type/Symfony/console_application_loader.php
new file mode 100644
index 00000000..524bc159
--- /dev/null
+++ b/tests/Type/Symfony/console_application_loader.php
@@ -0,0 +1,14 @@
+<?php declare(strict_types = 1);
+
+use PHPStan\Type\Symfony\ExampleACommand;
+use PHPStan\Type\Symfony\ExampleBCommand;
+use PHPStan\Type\Symfony\ExampleOptionCommand;
+use Symfony\Component\Console\Application;
+
+require_once __DIR__ . '/../../../vendor/autoload.php';
+
+$application = new Application();
+$application->add(new ExampleACommand());
+$application->add(new ExampleBCommand());
+$application->add(new ExampleOptionCommand());
+return $application;