Skip to content

Fix get_class() on HasMethodType #2350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: 1.10.x
Choose a base branch
from
Open

Conversation

staabm
Copy link
Contributor

@staabm staabm commented Apr 19, 2023

@phpstan-bot
Copy link
Collaborator

You've opened the pull request against the latest branch 1.11.x. If your code is relevant on 1.10.x and you want it to be released sooner, please rebase your pull request and change its target to 1.10.x.

@staabm staabm changed the base branch from 1.11.x to 1.10.x April 19, 2023 13:25
@@ -85,6 +86,8 @@ static function (Type $type, callable $traverse): Type {
return new GenericClassStringType($type);
} elseif ($type instanceof ObjectWithoutClassType) {
return new ClassStringType();
} elseif ($type instanceof HasMethodType) {
return new ClassStringType();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd make more sense to get rid of this whole monstrosity (because it'd still fail for HasPropertyType for example - feel free to add a test for it) and also other types like ObjectShapeType.

Instead the whole TypeTraverser::map should be replacable with some methods called on Type.

  • Type::isObject()->no() - always returns false
  • Type::isObject()->yes() - never returns false
  • Type::isObject()->maybe() - might return false

As for the actual type, you could do something like this:

new GenericClassStringType(TypeCombinator::intersect($argType, new ObjectWithoutClassType()));

It will change the result in some cases but it will still be correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your idea works pretty good. one exception is

 PHPStan\Analyser\NodeScopeResolverTest::testFileAsserts with data set "C:\dvl\Workspace\phpstan-src-staabm\tests\PHPStan\Analyser/data/bug-7167.php:11" ('type', 'C:\dvl\Workspace\phpstan-src-...67.php', PHPStan\Type\Constant\ConstantStringType Object (...), PHPStan\Type\Generic\GenericClassStringType Object (...), 11)
Expected type class-string<Bug7167\Foo>, got type class-string<Bug7167\Foo::Value> in C:\dvl\Workspace\phpstan-src-staabm\tests\PHPStan\Analyser/data/bug-7167.php on line 11.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'class-string<Bug7167\Foo>'
+'class-string<Bug7167\Foo::Value>'

which I am not yet sure how to handle

Comment on lines 53 to 56
if ($argType instanceof ObjectShapeType) {
return new ClassStringType();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sure you won't like it, but I think it seems to be necessary.

without this case we get things like
class-string<object{foo: Bug4890\HelloWorld, bar: int, baz?: string}>

when intersecting ObjectShapeType and ObjectWithoutClassType.

@clxmstaab clxmstaab force-pushed the bug4890 branch 2 times, most recently from 7f8af6f to 77c061f Compare April 20, 2023 08:16

interface Proxy {}

class HelloWorld
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see https://phpstan.org/r/b6b95d5a-a3fb-4c29-be47-4e7832f00ddc regarding how this test fails before this PR

@staabm
Copy link
Contributor Author

staabm commented Apr 26, 2023

think I figured it out - I had to re-introduce the TypeTraverser for the EnumCaseObjectType

#2350 (comment)

if ($type instanceof ObjectWithoutClassType) {
return new GenericClassStringType($type);
$isObject = $type->isObject();
if ($isObject->yes() || $isObject->maybe()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For which type here it's going to be a maybe?


$objectType = TypeCombinator::intersect($type, new ObjectWithoutClassType());
if ($objectType instanceof StaticType) {
$objectType = $objectType->getStaticObjectType();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you checking this after an intersection?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

false positive due to method_exists call beforehand
3 participants