Skip to content

Commit 6da32ea

Browse files
Fix constructor called methods side effects overwriting the type (#740)
* properly handling side effects originating from constructors methods calls * remove comment * Fix styling --------- Co-authored-by: romalytvynenko <[email protected]>
1 parent b297c9a commit 6da32ea

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

src/Infer/Services/ReferenceTypeResolver.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -815,13 +815,22 @@ private function getMethodCallsSideEffectIntroducedTypesInConstructor(Generic $t
815815
continue;
816816
}
817817

818-
$type = $this->getFunctionCallResult($methodDefinition, $se->arguments, $type, new MethodCallEvent(
818+
$event = new MethodCallEvent(
819819
instance: $type,
820820
name: $se->methodName,
821821
scope: $scope,
822822
arguments: $se->arguments,
823823
methodDefiningClassName: $type->name,
824-
));
824+
);
825+
826+
foreach ($methodDefinition->sideEffects as $sideEffect) {
827+
if (
828+
$sideEffect instanceof SelfTemplateDefinition
829+
&& $type instanceof Generic
830+
) {
831+
$sideEffect->apply($type, $event);
832+
}
833+
}
825834
}
826835

827836
return $type;

tests/Infer/Analyzer/ClassAnalyzerTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,71 @@
105105
expect($type->toString())->toBe('Dedoc\Scramble\Tests\Infer\stubs\ChildParentSetterCalls<string(from ChildParentSetterCalls constructor), string(from ChildParentSetterCalls wow), string(some)>');
106106
});
107107

108+
it('analyzes fluent setters called in constructor', function () {
109+
$this->classAnalyzer->analyze(Foo_ClassAnalyzerTest::class);
110+
111+
$type = getStatementType('new Foo_ClassAnalyzerTest()');
112+
113+
expect($type->toString())->toBe('Foo_ClassAnalyzerTest<int(42), string(baz)>');
114+
});
115+
class Foo_ClassAnalyzerTest
116+
{
117+
public int $foo;
118+
119+
public string $bar;
120+
121+
public function __construct()
122+
{
123+
$this
124+
->setFoo(42)
125+
->setBar('baz');
126+
}
127+
128+
public function setFoo($number)
129+
{
130+
$this->foo = $number;
131+
132+
return $this;
133+
}
134+
135+
public function setBar($string)
136+
{
137+
$this->bar = $string;
138+
139+
return $this;
140+
}
141+
}
142+
143+
it('analyzes not fluent setters called in constructor', function () {
144+
$this->classAnalyzer->analyze(FooNotFluent_ClassAnalyzerTest::class);
145+
146+
$type = getStatementType('new FooNotFluent_ClassAnalyzerTest()');
147+
148+
expect($type->toString())->toBe('FooNotFluent_ClassAnalyzerTest<int(42), string(baz)>');
149+
});
150+
class FooNotFluent_ClassAnalyzerTest
151+
{
152+
public int $foo;
153+
154+
public string $bar;
155+
156+
public function __construct()
157+
{
158+
$this->setFoo(42);
159+
$this->setBar('baz');
160+
}
161+
162+
public function setFoo($number)
163+
{
164+
$this->foo = $number;
165+
}
166+
167+
public function setBar($string)
168+
{
169+
$this->bar = $string;
170+
}
171+
}
172+
108173
it('analyzes static method call on class constants', function () {
109174
$this->classAnalyzer->analyze(ConstFetchStaticCallChild_ClassAnalyzerTest::class);
110175

0 commit comments

Comments
 (0)