Skip to content

Commit

Permalink
Fix constructor called methods side effects overwriting the type (#740)
Browse files Browse the repository at this point in the history
* properly handling side effects originating from constructors methods calls

* remove comment

* Fix styling

---------

Co-authored-by: romalytvynenko <[email protected]>
  • Loading branch information
romalytvynenko and romalytvynenko authored Feb 17, 2025
1 parent b297c9a commit 6da32ea
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 2 deletions.
13 changes: 11 additions & 2 deletions src/Infer/Services/ReferenceTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -815,13 +815,22 @@ private function getMethodCallsSideEffectIntroducedTypesInConstructor(Generic $t
continue;
}

$type = $this->getFunctionCallResult($methodDefinition, $se->arguments, $type, new MethodCallEvent(
$event = new MethodCallEvent(
instance: $type,
name: $se->methodName,
scope: $scope,
arguments: $se->arguments,
methodDefiningClassName: $type->name,
));
);

foreach ($methodDefinition->sideEffects as $sideEffect) {
if (
$sideEffect instanceof SelfTemplateDefinition
&& $type instanceof Generic
) {
$sideEffect->apply($type, $event);
}
}
}

return $type;
Expand Down
65 changes: 65 additions & 0 deletions tests/Infer/Analyzer/ClassAnalyzerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,71 @@
expect($type->toString())->toBe('Dedoc\Scramble\Tests\Infer\stubs\ChildParentSetterCalls<string(from ChildParentSetterCalls constructor), string(from ChildParentSetterCalls wow), string(some)>');
});

it('analyzes fluent setters called in constructor', function () {
$this->classAnalyzer->analyze(Foo_ClassAnalyzerTest::class);

$type = getStatementType('new Foo_ClassAnalyzerTest()');

expect($type->toString())->toBe('Foo_ClassAnalyzerTest<int(42), string(baz)>');
});
class Foo_ClassAnalyzerTest
{
public int $foo;

public string $bar;

public function __construct()
{
$this
->setFoo(42)
->setBar('baz');
}

public function setFoo($number)
{
$this->foo = $number;

return $this;
}

public function setBar($string)
{
$this->bar = $string;

return $this;
}
}

it('analyzes not fluent setters called in constructor', function () {
$this->classAnalyzer->analyze(FooNotFluent_ClassAnalyzerTest::class);

$type = getStatementType('new FooNotFluent_ClassAnalyzerTest()');

expect($type->toString())->toBe('FooNotFluent_ClassAnalyzerTest<int(42), string(baz)>');
});
class FooNotFluent_ClassAnalyzerTest
{
public int $foo;

public string $bar;

public function __construct()
{
$this->setFoo(42);
$this->setBar('baz');
}

public function setFoo($number)
{
$this->foo = $number;
}

public function setBar($string)
{
$this->bar = $string;
}
}

it('analyzes static method call on class constants', function () {
$this->classAnalyzer->analyze(ConstFetchStaticCallChild_ClassAnalyzerTest::class);

Expand Down

0 comments on commit 6da32ea

Please sign in to comment.