From 4e45a9d033a4bca2cdf240c235e640b5dac4980f Mon Sep 17 00:00:00 2001 From: Farhad Safarov Date: Fri, 3 Jan 2020 07:45:29 +0300 Subject: [PATCH] Detect ContainerInterface::get() result type (#6) --- .travis.yml | 2 +- README.md | 1 + src/Handler/ClassHandler.php | 22 +++++--- tests/acceptance/ContainerService.feature | 64 +++++++++++++++++++++++ 4 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 tests/acceptance/ContainerService.feature diff --git a/.travis.yml b/.travis.yml index e8478fa..dbb5c4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,4 +22,4 @@ env: - DEPS="high" - DEPS="stable" global: - - DEFAULT_COMPOSER_FLAGS="--no-interaction --no-suggest" + - DEFAULT_COMPOSER_FLAGS="--no-interaction --no-suggest --prefer-dist" diff --git a/README.md b/README.md index 93274b9..1fc6b4a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ vendor/bin/psalm-plugin enable seferov/symfony-psalm-plugin ### Features +- Detect `ContainerInterface::get()` result type - Fixes `PossiblyInvalidArgument` for `Symfony\Component\HttpFoundation\Request::getContent`. The plugin calculates real return type by checking the given argument and marks return type as either string or resource. - Complains when `Container` is injected to a service. Use dependency-injection. diff --git a/src/Handler/ClassHandler.php b/src/Handler/ClassHandler.php index 901fd92..926713d 100644 --- a/src/Handler/ClassHandler.php +++ b/src/Handler/ClassHandler.php @@ -13,6 +13,7 @@ use Psalm\Plugin\Hook\AfterClassLikeAnalysisInterface; use Psalm\Plugin\Hook\AfterMethodCallAnalysisInterface; use Psalm\StatementsSource; use Psalm\Storage\ClassLikeStorage; +use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Union; use Seferov\SymfonyPsalmPlugin\Issue\ContainerDependency; use Seferov\SymfonyPsalmPlugin\Issue\RepositoryStringShortcut; @@ -56,15 +57,13 @@ class ClassHandler implements AfterClassLikeAnalysisInterface, AfterMethodCallAn Union &$return_type_candidate = null ) { switch ($declaring_method_id) { - case 'Doctrine\ORM\EntityManagerInterface::getrepository': - if (!$expr->args[0]->value instanceof ClassConstFetch) { - IssueBuffer::accepts( - new RepositoryStringShortcut(new CodeLocation($statements_source, $expr->args[0]->value)), - $statements_source->getSuppressedIssues() - ); + case 'Psr\Container\ContainerInterface::get': + case 'Symfony\Component\DependencyInjection\ContainerInterface::get': + if ($return_type_candidate && $expr->args[0]->value instanceof ClassConstFetch) { + $className = (string) $expr->args[0]->value->class->getAttribute('resolvedName'); + $return_type_candidate = new Union([new TNamedObject($className)]); } break; - case 'Symfony\Component\HttpFoundation\Request::getcontent': if ($return_type_candidate) { $removeType = 'resource'; @@ -75,6 +74,15 @@ class ClassHandler implements AfterClassLikeAnalysisInterface, AfterMethodCallAn $return_type_candidate->removeType($removeType); } break; + case 'Doctrine\ORM\EntityManagerInterface::getrepository': + case 'Doctrine\Persistence\ObjectManager::getrepository': + if (!$expr->args[0]->value instanceof ClassConstFetch) { + IssueBuffer::accepts( + new RepositoryStringShortcut(new CodeLocation($statements_source, $expr->args[0]->value)), + $statements_source->getSuppressedIssues() + ); + } + break; } } } diff --git a/tests/acceptance/ContainerService.feature b/tests/acceptance/ContainerService.feature new file mode 100644 index 0000000..9ac5f32 --- /dev/null +++ b/tests/acceptance/ContainerService.feature @@ -0,0 +1,64 @@ +Feature: Container service + Detect ContainerInterface::get() result type + + Background: + Given I have the following config + """ + + + + + + + + + + + + """ + + Scenario: Asserting psalm recognizes return type of service got via 'ContainerInterface::get()' + Given I have the following code + """ + container->get(SomeService::class)->do(); + } + } + """ + When I run Psalm + Then I see no errors + + Scenario: Asserting psalm recognizes return type of service got via 'ContainerInterface::get()'. + Given I have the following code + """ + container->get(SomeService::class)->noSuchMethod(); + } + } + """ + When I run Psalm + Then I see these errors + | Type | Message | + | UndefinedMethod | Method SomeService::nosuchmethod does not exist |