Skip to content

Commit

Permalink
Merge pull request #279 from Lctrs/feature/events
Browse files Browse the repository at this point in the history
psalm: switch from legacy plugin hook to new event handler system
  • Loading branch information
lctrs-bot committed Jun 8, 2021
2 parents 72ecc01 + ecacbca commit d7bfae6
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 109 deletions.
2 changes: 1 addition & 1 deletion phpcs.xml.dist
Expand Up @@ -6,7 +6,7 @@
<arg name="cache" value=".build/php_codesniffer/phpcs-cache"/>

<rule ref="Doctrine">
<exclude name="Squiz.NamingConventions.ValidVariableName.NotCamelCaps"/>
<exclude name="Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps"/>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint"/>
Expand Down
41 changes: 13 additions & 28 deletions src/Checker/PsrContainerChecker.php
Expand Up @@ -4,14 +4,11 @@

namespace Lctrs\PsalmPsrContainerPlugin\Checker;

use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use Psalm\Codebase;
use Psalm\Context;
use Psalm\Plugin\Hook\AfterMethodCallAnalysisInterface;
use Psalm\StatementsSource;
use Psalm\Plugin\EventHandler\AfterMethodCallAnalysisInterface;
use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent;
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\TClassString;
use Psalm\Type\Atomic\TNamedObject;
Expand All @@ -27,33 +24,23 @@
*/
final class PsrContainerChecker implements AfterMethodCallAnalysisInterface
{
/**
* @inheritDoc
*/
public static function afterMethodCallAnalysis(
Expr $expr,
string $method_id,
string $appearing_method_id,
string $declaring_method_id,
Context $context,
StatementsSource $statements_source,
Codebase $codebase,
array &$file_replacements = [],
?Union &$return_type_candidate = null
): void {
if (! $expr instanceof MethodCall || $return_type_candidate === null) {
public static function afterMethodCallAnalysis(AfterMethodCallAnalysisEvent $event): void
{
$expr = $event->getExpr();

if (! $expr instanceof MethodCall || $event->getReturnTypeCandidate() === null) {
return;
}

[$className, $methodName] = explode('::', $declaring_method_id);
[$className, $methodName] = explode('::', $event->getDeclaringMethodId());

if ($methodName !== 'get') {
return;
}

if (
$className !== ContainerInterface::class
&& ! $codebase->classImplements($className, ContainerInterface::class)
&& ! $event->getCodebase()->classImplements($className, ContainerInterface::class)
) {
return;
}
Expand All @@ -68,15 +55,14 @@ public static function afterMethodCallAnalysis(
return;
}

// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$variableType = $context->vars_in_scope['$' . $arg->value->name] ?? null;
$variableType = $event->getContext()->vars_in_scope['$' . $arg->value->name] ?? null;
if (! $variableType instanceof Union) {
return;
}

$candidate = self::handleVariable($variableType);
if (! $candidate->isMixed()) {
$return_type_candidate = $candidate;
$event->setReturnTypeCandidate($candidate);
}

return;
Expand All @@ -87,14 +73,13 @@ public static function afterMethodCallAnalysis(
return;
}

$return_type_candidate = new Union([
$event->setReturnTypeCandidate(new Union([
new TNamedObject(
(string) $class->getAttribute('resolvedName')
),
]);
]));
}

// phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
private static function handleVariable(Union $variableType): Union
{
/** @var list<Atomic> $types */
Expand Down
40 changes: 21 additions & 19 deletions test/Unit/Checker/PsrContainerCheckerClassStringTest.php
Expand Up @@ -13,6 +13,7 @@
use Prophecy\PhpUnit\ProphecyTrait;
use Psalm\Codebase;
use Psalm\Context;
use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent;
use Psalm\StatementsSource;
use Psalm\Type\Atomic\TClassString;
use Psalm\Type\Atomic\TMixed;
Expand All @@ -32,44 +33,45 @@ class PsrContainerCheckerClassStringTest extends TestCase

public function testItDoesNothingWithEmptyContext(): void
{
$fileReplacements = [];
$returnTypeCandidate = $baseReturnType = new Union([new TMixed()]);

PsrContainerChecker::afterMethodCallAnalysis(
$event = new AfterMethodCallAnalysisEvent(
$this->getMethodCall(),
self::METHOD_ID,
self::METHOD_ID,
self::METHOD_ID,
$this->createStub(Context::class),
$this->createStub(StatementsSource::class),
$this->createStub(Codebase::class),
$fileReplacements,
$returnTypeCandidate
[],
new Union([new TMixed()])
);

self::assertSame($baseReturnType, $returnTypeCandidate);
PsrContainerChecker::afterMethodCallAnalysis($event);

$returnTypeCandidate = $event->getReturnTypeCandidate();
self::assertNotNull($returnTypeCandidate);
self::assertTrue($returnTypeCandidate->equals(new Union([new TMixed()])));
}

/**
* @dataProvider pairsProvider
*/
public function testItSetsTheReturnTypeAsAUnionWithFetchedClass(Union $variableType, Union $expectedType): void
{
$fileReplacements = [];
$returnTypeCandidate = new Union([new TMixed()]);

PsrContainerChecker::afterMethodCallAnalysis(
$event = new AfterMethodCallAnalysisEvent(
$this->getMethodCall(),
self::METHOD_ID,
self::METHOD_ID,
self::METHOD_ID,
$this->createContext($variableType),
$this->createStub(StatementsSource::class),
$this->createStub(Codebase::class),
$fileReplacements,
$returnTypeCandidate
[],
new Union([new TMixed()])
);

PsrContainerChecker::afterMethodCallAnalysis($event);

$returnTypeCandidate = $event->getReturnTypeCandidate();
self::assertNotNull($returnTypeCandidate);
self::assertTrue($expectedType->equals($returnTypeCandidate));

Expand All @@ -88,26 +90,26 @@ public function testItSetsTheReturnTypeAsAUnionWithFetchedClassWithContainerImpl
Union $variableType,
Union $expectedType
): void {
$fileReplacements = [];
$returnTypeCandidate = new Union([new TMixed()]);

$codebase = $this->prophesize(Codebase::class);
$codebase->classImplements(MyOtherContainer::class, ContainerInterface::class)
->willReturn(true)
->shouldBeCalledOnce();

PsrContainerChecker::afterMethodCallAnalysis(
$event = new AfterMethodCallAnalysisEvent(
$this->getMethodCall(),
MyOtherContainer::class . '::get',
MyOtherContainer::class . '::get',
MyOtherContainer::class . '::get',
$this->createContext($variableType),
$this->createStub(StatementsSource::class),
$codebase->reveal(),
$fileReplacements,
$returnTypeCandidate
[],
new Union([new TMixed()])
);

PsrContainerChecker::afterMethodCallAnalysis($event);

$returnTypeCandidate = $event->getReturnTypeCandidate();
self::assertNotNull($returnTypeCandidate);
self::assertTrue($expectedType->equals($returnTypeCandidate));

Expand Down

0 comments on commit d7bfae6

Please sign in to comment.