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

Property expr #348

Merged
merged 6 commits into from
Oct 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 26 additions & 3 deletions src/Rules/Properties/AccessPropertiesRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
namespace PHPStan\Rules\Properties;

use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Identifier;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\VerbosityLevel;

/**
Expand Down Expand Up @@ -41,11 +45,30 @@ public function getNodeType(): string

public function processNode(\PhpParser\Node $node, Scope $scope): array
{
if (!$node->name instanceof \PhpParser\Node\Identifier) {
return [];
if ($node->name instanceof Identifier) {
$names = [$node->name->name];
} else {
$names = array_map(static function (ConstantStringType $type): string {
return $type->getValue();
}, TypeUtils::getConstantStrings($scope->getType($node->name)));
}

$errors = [];
foreach ($names as $name) {
$errors = array_merge($errors, $this->processSingleProperty($scope, $node, $name));
}

$name = $node->name->name;
return $errors;
}

/**
* @param Scope $scope
* @param PropertyFetch $node
* @param string $name
* @return RuleError[]
*/
private function processSingleProperty(Scope $scope, PropertyFetch $node, string $name): array
{
$typeResult = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$node->var,
Expand Down
27 changes: 24 additions & 3 deletions src/Rules/Properties/AccessStaticPropertiesRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
Expand Down Expand Up @@ -49,11 +51,30 @@ public function getNodeType(): string

public function processNode(Node $node, Scope $scope): array
{
if (!$node->name instanceof Node\VarLikeIdentifier) {
return [];
if ($node->name instanceof Node\VarLikeIdentifier) {
$names = [$node->name->name];
} else {
$names = array_map(static function (ConstantStringType $type): string {
return $type->getValue();
}, TypeUtils::getConstantStrings($scope->getType($node->name)));
}

$errors = [];
foreach ($names as $name) {
$errors = array_merge($errors, $this->processSingleProperty($scope, $node, $name));
}

$name = $node->name->name;
return $errors;
}

/**
* @param Scope $scope
* @param StaticPropertyFetch $node
* @param string $name
* @return RuleError[]
*/
private function processSingleProperty(Scope $scope, StaticPropertyFetch $node, string $name): array
{
$messages = [];
if ($node->class instanceof Name) {
$class = (string) $node->class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,36 @@ public function testRule(): void
]);
}

public function testRuleExpressionNames(): void
{
$this->analyse([__DIR__ . '/data/properties-from-variable-into-object.php'], [
[
'Access to an undefined property PropertiesFromVariableIntoObject\Foo::$noop.',
26,
],
]);
}

public function testRuleExpressionNames2(): void
{
$this->analyse([__DIR__ . '/data/properties-from-array-into-object.php'], [
[
'Access to an undefined property PropertiesFromArrayIntoObject\Foo::$noop.',
42,
],
[
'Access to an undefined property PropertiesFromArrayIntoObject\Foo::$noop.',
54,
],
[
'Access to an undefined property PropertiesFromArrayIntoObject\Foo::$noop.',
69,
],
[
'Access to an undefined property PropertiesFromArrayIntoObject\Foo::$noop.',
110,
],
]);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,14 @@ public function testRule(): void
]);
}

public function testRuleExpressionNames(): void
{
$this->analyse([__DIR__ . '/data/properties-from-array-into-static-object.php'], [
[
'Access to an undefined static property PropertiesFromArrayIntoStaticObject\Foo::$noop.',
29,
],
]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php declare(strict_types = 1);

namespace PropertiesFromArrayIntoObject;

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

/**
* @var float
*/
public $float_test = 0.0;

/**
* @var int
*/
public $lall = 0;

/**
* @var int|null
*/
public $test;

/**
* @phpstan-return array{float_test: float, foo: 'bar', lall: string, noop: int, test: int}
*/
public function data(): array
{
/** @var mixed $array */
$array = [];

return $array;
}

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

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

return $self;
}

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

$data = $this->data();

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

return $self;
}

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

foreach($this->data() as $property => $value) {
if ($property === 'test') {
if ($self->{$property} === null) {
$self->{$property} = new \stdClass();
}
} else {
$self->{$property} = $value;
}

if ($property === 'foo') {
$self->{$property} += 1;
}
if ($property === 'foo') {
$self->{$property} .= ' ';
}
if ($property === 'lall') {
$self->{$property} += 1;
}
$tmp = 1.1;
if ($property === 'foo') {
$self->{$property} += $tmp;
}
}

return $self;
}

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

$data = $this->data();

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

return $self;
}

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

$data = $this->data();

foreach($data as $property => $value) {
foreach([1, 2, 3] as $value_2) {
$self->{$property} = $value;
}
}

return $self;
}
}


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

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

public function data(): array
{
return ['foo' => 'bar', 'lall' => 'lall', 'noop' => 1];
}

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

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

if ($property === 'lall') {
$this->{$property} = null;
}

if ($property === 'foo') {
$this->{$property} = 1.1;
}
}

return $self;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php declare(strict_types = 1);

namespace PropertiesFromArrayIntoStaticObject;

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;

if ($property === 'lall') {
self::${$property} = null;
}

if ($property === 'foo') {
self::${$property} = 1.1;
}
}

return $self;
}
}

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

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

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;

if ($property === 'lall') {
self::${$property} = null;
}

if ($property === 'foo') {
self::${$property} = 1.1;
}
}

return $self;
}
}