Skip to content

Commit

Permalink
Support intersections with __toString
Browse files Browse the repository at this point in the history
Fixes #3149
  • Loading branch information
muglug committed Apr 27, 2020
1 parent 5331a3e commit e65bffc
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 27 deletions.
66 changes: 39 additions & 27 deletions src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php
Expand Up @@ -2229,39 +2229,51 @@ private static function castStringAttempt(
}

if ($atomic_type instanceof TNamedObject
&& $codebase->methods->methodExists(
new \Psalm\Internal\MethodIdentifier(
$atomic_type->value,
'__tostring'
)
)
|| $atomic_type instanceof TObject
) {
$return_type = $codebase->methods->getMethodReturnType(
new \Psalm\Internal\MethodIdentifier(
$atomic_type->value,
'__tostring'
),
$self_class
);
$intersection_types = [$atomic_type];

if ($return_type) {
$castable_types = array_merge(
$castable_types,
array_values($return_type->getAtomicTypes())
);
} else {
$castable_types[] = new TString();
if ($atomic_type->extra_types) {
$intersection_types = array_merge($intersection_types, $atomic_type->extra_types);
}

continue;
}
foreach ($intersection_types as $intersection_type) {
if ($intersection_type instanceof TNamedObject
&& $codebase->methods->methodExists(
new \Psalm\Internal\MethodIdentifier(
$intersection_type->value,
'__tostring'
)
)
) {
$return_type = $codebase->methods->getMethodReturnType(
new \Psalm\Internal\MethodIdentifier(
$intersection_type->value,
'__tostring'
),
$self_class
);

if ($atomic_type instanceof Type\Atomic\TObjectWithProperties
&& isset($atomic_type->methods['__toString'])
) {
$castable_types[] = new TString();
if ($return_type) {
$castable_types = array_merge(
$castable_types,
array_values($return_type->getAtomicTypes())
);
} else {
$castable_types[] = new TString();
}

continue;
continue 2;
}

if ($intersection_type instanceof Type\Atomic\TObjectWithProperties
&& isset($intersection_type->methods['__toString'])
) {
$castable_types[] = new TString();

continue 2;
}
}
}

if ($atomic_type instanceof Type\Atomic\TTemplateParam) {
Expand Down
23 changes: 23 additions & 0 deletions tests/ToStringTest.php
Expand Up @@ -119,6 +119,29 @@ function __toString(): string {
}
}'
],
'intersectionCanBeString' => [
'<?php
interface EmptyInterface {}
class StringCastable implements EmptyInterface
{
public function __toString()
{
return \'I am castable\';
}
}
function factory(): EmptyInterface
{
return new StringCastable();
}
$object = factory();
if (method_exists($object, \'__toString\')) {
$a = (string) $object;
echo $a;
}'
],
];
}

Expand Down

0 comments on commit e65bffc

Please sign in to comment.