Skip to content

Commit

Permalink
properly handling side effects originating from constructors methods …
Browse files Browse the repository at this point in the history
…calls
  • Loading branch information
romalytvynenko committed Feb 17, 2025
1 parent b297c9a commit 5d0828f
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/Infer/Services/ReferenceTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,10 @@ private function resolveNewCallReferenceType(Scope $scope, NewCallReferenceType
->map(fn (TemplateType $t) => $inferredTemplates->get($t->name, new UnknownType))
->toArray(),
);
// dd(
// $type,
// $this->getMethodCallsSideEffectIntroducedTypesInConstructor($type, $scope, $classDefinition, $constructorDefinition)
// );

return $this->getMethodCallsSideEffectIntroducedTypesInConstructor($type, $scope, $classDefinition, $constructorDefinition);
}
Expand Down Expand Up @@ -815,13 +819,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
53 changes: 53 additions & 0 deletions tests/Infer/Analyzer/ClassAnalyzerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,59 @@
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 5d0828f

Please sign in to comment.