Detect ContainerInterface::get() result type (#6)

This commit is contained in:
Farhad Safarov 2020-01-03 07:45:29 +03:00 committed by GitHub
parent c2924cb89a
commit 4e45a9d033
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 8 deletions

View File

@ -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"

View File

@ -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.

View File

@ -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;
}
}
}

View File

@ -0,0 +1,64 @@
Feature: Container service
Detect ContainerInterface::get() result type
Background:
Given I have the following config
"""
<?xml version="1.0"?>
<psalm totallyTyped="true">
<projectFiles>
<directory name="."/>
<ignoreFiles> <directory name="../../vendor"/> </ignoreFiles>
</projectFiles>
<plugins>
<pluginClass class="Seferov\SymfonyPsalmPlugin\Plugin"/>
</plugins>
</psalm>
"""
Scenario: Asserting psalm recognizes return type of service got via 'ContainerInterface::get()'
Given I have the following code
"""
<?php
class SomeService
{
public function do(): void {}
}
class SomeController
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
public function index(): void
{
$this->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
"""
<?php
class SomeService
{
public function do(): void {}
}
class SomeController
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
public function index(): void
{
$this->container->get(SomeService::class)->noSuchMethod();
}
}
"""
When I run Psalm
Then I see these errors
| Type | Message |
| UndefinedMethod | Method SomeService::nosuchmethod does not exist |