Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Checking variable variables for object (static) properties.
-> PropertyReflectionFinder->findPropertyReflectionFromNode() is now marked as internal, and we added "findPropertyReflectionsFromNode()", which return an array of FoundPropertyReflection objects
  • Loading branch information
voku committed Oct 17, 2020
commit 6d51662563292d114ba37897c352dc43968f7232
42 changes: 22 additions & 20 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use function array_key_exists;

class MutatingScope implements Scope
{
Expand Down Expand Up @@ -367,7 +366,7 @@ public function getVariableType(string $variableName): Type
throw new \PHPStan\Analyser\UndefinedVariableException($this, $variableName);
}

if (!array_key_exists($variableName, $this->variableTypes)) {
if (!isset($this->variableTypes[$variableName])) {
return new MixedType();
}

Expand Down Expand Up @@ -413,20 +412,20 @@ public function hasConstant(Name $name): bool
return $this->fileHasCompilerHaltStatementCalls();
}
if ($name->isFullyQualified()) {
if (array_key_exists($name->toCodeString(), $this->constantTypes)) {
if (isset($this->constantTypes[$name->toCodeString()])) {
return true;
}
}

if ($this->getNamespace() !== null) {
$constantName = new FullyQualified([$this->getNamespace(), $name->toString()]);
if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
if (isset($this->constantTypes[$constantName->toCodeString()])) {
return true;
}
}

$constantName = new FullyQualified($name->toString());
if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
if (isset($this->constantTypes[$constantName->toCodeString()])) {
return true;
}

Expand Down Expand Up @@ -474,7 +473,7 @@ public function getType(Expr $node): Type
{
$key = $this->getNodeKey($node);

if (!array_key_exists($key, $this->resolvedTypes)) {
if (!isset($this->resolvedTypes[$key])) {
$this->resolvedTypes[$key] = $this->resolveType($node);
}
return $this->resolvedTypes[$key];
Expand Down Expand Up @@ -1512,20 +1511,20 @@ private function resolveType(Expr $node): Type
}

if ($node->name->isFullyQualified()) {
if (array_key_exists($node->name->toCodeString(), $this->constantTypes)) {
if (isset($this->constantTypes[$node->name->toCodeString()])) {
return $this->resolveConstantType($node->name->toString(), $this->constantTypes[$node->name->toCodeString()]);
}
}

if ($this->getNamespace() !== null) {
$constantName = new FullyQualified([$this->getNamespace(), $constName]);
if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
if (isset($this->constantTypes[$constantName->toCodeString()])) {
return $this->resolveConstantType($constantName->toString(), $this->constantTypes[$constantName->toCodeString()]);
}
}

$constantName = new FullyQualified($constName);
if (array_key_exists($constantName->toCodeString(), $this->constantTypes)) {
if (isset($this->constantTypes[$constantName->toCodeString()])) {
return $this->resolveConstantType($constantName->toString(), $this->constantTypes[$constantName->toCodeString()]);
}

Expand Down Expand Up @@ -1917,7 +1916,7 @@ public function getNativeType(Expr $expr): Type
{
$key = $this->getNodeKey($expr);

if (array_key_exists($key, $this->nativeExpressionTypes)) {
if (isset($this->nativeExpressionTypes[$key])) {
return $this->nativeExpressionTypes[$key];
}

Expand Down Expand Up @@ -1997,16 +1996,19 @@ private function promoteNativeTypes(): self
*/
private function hasPropertyNativeType($propertyFetch): bool
{
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($propertyFetch, $this);
if ($propertyReflection === null) {
return false;
}
$propertyReflections = $this->propertyReflectionFinder->findPropertyReflectionsFromNode($propertyFetch, $this);

if (!$propertyReflection->isNative()) {
return false;
foreach ($propertyReflections as $propertyReflection) {
if (!$propertyReflection->isNative()) {
continue;
}

if (!$propertyReflection->getNativeType() instanceof MixedType) {
return true;
}
}

return !$propertyReflection->getNativeType() instanceof MixedType;
return false;
}

protected function getTypeFromArrayDimFetch(
Expand Down Expand Up @@ -2440,7 +2442,7 @@ private function enterFunctionLike(
$nativeExpressionTypes[sprintf('$%s', $parameter->getName())] = $parameter->getNativeType();
}

if (!$preserveThis && array_key_exists('this', $variableTypes)) {
if (!$preserveThis && isset($variableTypes['this'])) {
unset($variableTypes['this']);
}

Expand Down Expand Up @@ -2810,7 +2812,7 @@ public function exitExpressionAssign(Expr $expr): self
public function isInExpressionAssign(Expr $expr): bool
{
$exprString = $this->getNodeKey($expr);
return array_key_exists($exprString, $this->currentlyAssignedExpressions);
return isset($this->currentlyAssignedExpressions[$exprString]);
}

public function assignVariable(string $variableName, Type $type, ?TrinaryLogic $certainty = null): self
Expand Down Expand Up @@ -3220,7 +3222,7 @@ public function mergeWith(?self $otherScope): self
$theirVariableTypes = $otherScope->getVariableTypes();
if ($this->isRootScope()) {
foreach (array_keys($theirVariableTypes) as $name) {
if (array_key_exists($name, $ourVariableTypes)) {
if (isset($ourVariableTypes[$name])) {
continue;
}

Expand Down
42 changes: 24 additions & 18 deletions src/Rules/Arrays/AppendedArrayItemTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,35 +56,41 @@ public function processNode(\PhpParser\Node $node, Scope $scope): array
return [];
}

$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($node->var->var, $scope);
if ($propertyReflection === null) {
$propertyReflections = $this->propertyReflectionFinder->findPropertyReflectionsFromNode($node->var->var, $scope);
if (count($propertyReflections) === 0) {
return [];
}

$assignedToType = $propertyReflection->getWritableType();
if (!($assignedToType instanceof ArrayType)) {
return [];
}
$errors = [];
foreach ($propertyReflections as $propertyReflection) {
$assignedToType = $propertyReflection->getWritableType();
if (!($assignedToType instanceof ArrayType)) {
continue;
}

if ($node instanceof Assign) {
$assignedValueType = $scope->getType($node->expr);
} else {
$assignedValueType = $scope->getType($node);
}
if ($node instanceof Assign) {
$assignedValueType = $scope->getType($node->expr);
} else {
$assignedValueType = $scope->getType($node);
}

$itemType = $assignedToType->getItemType();
if ($this->ruleLevelHelper->accepts($itemType, $assignedValueType, $scope->isDeclareStrictTypes())) {
continue;
}

$itemType = $assignedToType->getItemType();
if (!$this->ruleLevelHelper->accepts($itemType, $assignedValueType, $scope->isDeclareStrictTypes())) {
$verbosityLevel = VerbosityLevel::getRecommendedLevelByType($itemType);
return [
RuleErrorBuilder::message(sprintf(

$errors[] = RuleErrorBuilder::message(
sprintf(
'Array (%s) does not accept %s.',
$assignedToType->describe($verbosityLevel),
$assignedValueType->describe($verbosityLevel)
))->build(),
];
)
)->build();
}

return [];
return $errors;
}

}
53 changes: 29 additions & 24 deletions src/Rules/Arrays/AppendedArrayKeyTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,43 +49,48 @@ public function processNode(\PhpParser\Node $node, Scope $scope): array
return [];
}

$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($node->var->var, $scope);
if ($propertyReflection === null) {
$propertyReflections = $this->propertyReflectionFinder->findPropertyReflectionsFromNode($node->var->var, $scope);
if (count($propertyReflections) === 0) {
return [];
}

$arrayType = $propertyReflection->getReadableType();
if (!$arrayType instanceof ArrayType) {
return [];
}
$errors = [];
foreach ($propertyReflections as $propertyReflection) {
$arrayType = $propertyReflection->getReadableType();
if (!$arrayType instanceof ArrayType) {
continue;
}

if ($node->var->dim !== null) {
$dimensionType = $scope->getType($node->var->dim);
$isValidKey = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
if (!$isValidKey->yes()) {
// already handled by InvalidKeyInArrayDimFetchRule
return [];
if ($node->var->dim !== null) {
$dimensionType = $scope->getType($node->var->dim);
$isValidKey = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
if (!$isValidKey->yes()) {
// already handled by InvalidKeyInArrayDimFetchRule
continue;
}

$keyType = ArrayType::castToArrayKeyType($dimensionType);
if (!$this->checkUnionTypes && $keyType instanceof UnionType) {
continue;
}
} else {
$keyType = new IntegerType();
}

$keyType = ArrayType::castToArrayKeyType($dimensionType);
if (!$this->checkUnionTypes && $keyType instanceof UnionType) {
return [];
if ($arrayType->getIterableKeyType()->isSuperTypeOf($keyType)->yes()) {
continue;
}
} else {
$keyType = new IntegerType();
}

if (!$arrayType->getIterableKeyType()->isSuperTypeOf($keyType)->yes()) {
return [
RuleErrorBuilder::message(sprintf(
$errors[] = RuleErrorBuilder::message(
sprintf(
'Array (%s) does not accept key %s.',
$arrayType->describe(VerbosityLevel::typeOnly()),
$keyType->describe(VerbosityLevel::value())
))->build(),
];
)
)->build();
}

return [];
return $errors;
}

}
38 changes: 21 additions & 17 deletions src/Rules/IssetCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,32 +82,36 @@ public function check(Expr $expr, Scope $scope, string $operatorDescription, ?Ru

} elseif ($expr instanceof Node\Expr\PropertyFetch || $expr instanceof Node\Expr\StaticPropertyFetch) {

$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $scope);

if ($propertyReflection === null) {
$propertyReflections = $this->propertyReflectionFinder->findPropertyReflectionsFromNode($expr, $scope);
if (count($propertyReflections) === 0) {
return null;
}

if (!$propertyReflection->isNative()) {
return null;
}
foreach ($propertyReflections as $propertyReflection) {
if (!$propertyReflection->isNative()) {
return null;
}

$nativeType = $propertyReflection->getNativeType();
if (!$nativeType instanceof MixedType) {
if (!$scope->isSpecified($expr)) {
$nativeType = $propertyReflection->getNativeType();
if (!$nativeType instanceof MixedType
&&
!$scope->isSpecified($expr)
) {
return null;
}
}

$propertyDescription = $this->propertyDescriptor->describeProperty($propertyReflection, $expr);
$propertyType = $propertyReflection->getWritableType();
$propertyDescription = $this->propertyDescriptor->describeProperty($propertyReflection, $expr);
$propertyType = $propertyReflection->getWritableType();

$error = $error ?? $this->generateError(
$propertyReflection->getWritableType(),
sprintf('%s (%s) %s', $propertyDescription, $propertyType->describe(VerbosityLevel::typeOnly()), $operatorDescription)
);
$error = $error ?? $this->generateError(
$propertyType,
sprintf('%s (%s) %s', $propertyDescription, $propertyType->describe(VerbosityLevel::typeOnly()), $operatorDescription)
);

if ($error === null) {
continue;
}

if ($error !== null) {
if ($expr instanceof Node\Expr\PropertyFetch) {
return $this->check($expr->var, $scope, $operatorDescription, $error);
}
Expand Down
Loading