Skip to content
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

Checking variable variables for object properties. #337

Closed
wants to merge 2 commits into from

Conversation

voku
Copy link
Contributor

@voku voku commented Oct 11, 2020

With this pull request, we have the possibility to check variable variables object properties in foreach loops.

For static and non-static variable variables we check if the key from the array shape is available as property, and we check if the type from the array shape (or from the current variable) is compatible with the type from the class.

PropertyReflectionFinder->findPropertyReflectionFromNode() is now
marked as internal. New findPropertyReflectionsFromNode() method is added,
which return an array of FoundPropertyReflection objects and I replaced all findPropertyReflectionFromNode() calls in the codebase.

Example:

class Foo
{
	/**
	 * @var string
	 */
	public static $foo = '';

	/**
	 * @var null|\stdClass
	 */
	public static $lall;

	/**
	 * @phpstan-return array{foo: 'bar', lall: string, noop: int}
	 */
	public function data(): array
	{
		return ['foo' => 'bar', 'lall' => 'lall', 'noop' => 1];
	}

	public function create(): self {
		$self = new self();

		foreach($this->data() as $property => $value) {
			self::${$property} = $value; // <- will report usage of "noop", because the static property is not available
                                                                   //  <- will report Foo::$foo (string) does not accept int
                                                                   //  <- will report Foo::$lall (stdClass|null) does not accept string
		}

		return $self;
	}
}

This change is Reviewable

Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

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

Also please rebase and clean up the commit history.

src/Rules/Properties/AccessPropertiesRule.php Show resolved Hide resolved
src/Rules/Properties/AccessPropertiesRule.php Show resolved Hide resolved
src/Rules/Properties/AccessStaticPropertiesRule.php Outdated Show resolved Hide resolved
src/Rules/Properties/PropertyReflectionFinder.php Outdated Show resolved Hide resolved
@voku voku force-pushed the check_object_properties_extended branch from 8475f1e to 5944440 Compare October 13, 2020 00:13
@voku voku force-pushed the check_object_properties_extended branch from f16a251 to dcb7b6c Compare October 13, 2020 02:54
@voku
Copy link
Contributor Author

voku commented Oct 13, 2020

Also please rebase and clean up the commit history.

done

@voku voku force-pushed the check_object_properties_extended branch 5 times, most recently from 822b477 to bdeed5a Compare October 17, 2020 00:22
@voku voku changed the title Checking variable variables for object (static) properties. Checking variable variables for object properties. Oct 17, 2020
@voku voku force-pushed the check_object_properties_extended branch from 983197a to b95c7c6 Compare October 17, 2020 17:14
-> PropertyReflectionFinder->findPropertyReflectionFromNode() is now
marked as internal, and we added "findPropertyReflectionsFromNode()",
which return an array of FoundPropertyReflection objects
@voku voku force-pushed the check_object_properties_extended branch from b95c7c6 to 6d51662 Compare October 17, 2020 22:22
@ondrejmirtes
Copy link
Member

Hi, I've reworked your PR: #348

I reused your tests and did the most simple implementation to make the tests pass.

This case is problematic as it currently leads to false positives:

		$data = $this->data();

		foreach($data as $property => $value) {
			$self->{$property} = $value;
		}

Can you point out what you did in your PR to make the tests pass?

I feel like it's not worth to change the property rules around this case as the analysis itself needs to be more precise - the rules themselves will start working correctly once the analysis is correct.

I've did a PoC of the better foreach analysis: #349 but it needs more work...

@ondrejmirtes
Copy link
Member

I'm just realizing this will need a completely different solution than what I did in #349. It will need something that gets closer to https://github.com/phpstan/phpstan/milestone/12.

@ondrejmirtes
Copy link
Member

Alright, so I merged #348 without changes to TypesAssignedToPropertiesRule which would require some analyser engine changes, which I can't tackle right now (I'm busy working on PHP 8 support).

Thank you.

@voku
Copy link
Contributor Author

voku commented Oct 18, 2020

Alright, so I merged #348 without changes to TypesAssignedToPropertiesRule which would require some analyser engine changes, which I can't tackle right now (I'm busy working on PHP 8 support).

Thank you

No problem, I am happy if we can push this forward, step by step. 👍

And yes, I also realized while playing with the code and types that there are some missing information for arrays.

The variable variable part needed more code as expected because I could not find the correct types. I only got a merged UnionType object (without keys / index from the array-annotation) [e.g. UnionType<IntType|FloatType> vs. array{foo: int, bar: float, 'lall': float}] or an UnionType object with only information about the types of the keys from array-annotation.

What I missed here was the original information from the array or from the array-shape in the elements of the array. I only found the information in ConstantArrayType object, from the array-shape (Foreach_->expr) itself. This is why I searched for it via findArrayShapeTypesFromAssignmet().

@ondrejmirtes
Copy link
Member

@voku FYI I brought the expressions check back to TypesAssignedToPropertiesRule, thanks to these commits:

Please test the latest dev-master on a codebase that heavily uses this so we can verify it works as expected 👍

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