mirror of
https://github.com/danog/psalm-plugin-symfony.git
synced 2024-11-30 04:29:10 +01:00
Detect ContainerInterface::get() result type (#6)
This commit is contained in:
parent
c2924cb89a
commit
4e45a9d033
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
tests/acceptance/ContainerService.feature
Normal file
64
tests/acceptance/ContainerService.feature
Normal 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 |
|
Loading…
Reference in New Issue
Block a user