Skip to content

Commit

Permalink
ConstantArrayType - preserve array being a list
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 19, 2023
1 parent 97f0039 commit d55c4f2
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
7 changes: 6 additions & 1 deletion src/Type/Php/ArrayMapFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Accessory\AccessoryArrayListType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
Expand Down Expand Up @@ -75,7 +76,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
$constantArray->isOptionalKey($i),
);
}
$arrayTypes[] = $returnedArrayBuilder->getArray();
$returnedArray = $returnedArrayBuilder->getArray();
if ($constantArray->isList()->yes()) {
$returnedArray = AccessoryArrayListType::intersectWith($returnedArray);
}
$arrayTypes[] = $returnedArray;
}

$mappedArrayType = TypeCombinator::union(...$arrayTypes);
Expand Down
5 changes: 5 additions & 0 deletions tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,11 @@ public function testLists(): void
]);
}

public function testConditionalListRule(): void
{
$this->analyse([__DIR__ . '/data/return-list-rule.php'], []);
}

public function testBug6856(): void
{
$this->analyse([__DIR__ . '/data/bug-6856.php'], []);
Expand Down
87 changes: 87 additions & 0 deletions tests/PHPStan/Rules/Methods/data/return-list-rule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace ReturnListRule;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
/**
* @implements Rule<BinaryOp>
*/
class BinaryOpEnumValueRule implements Rule
{

/** @var class-string<BinaryOp> */
private string $className;

/**
* @param class-string $operator
*/
public function __construct(string $operator, ?string $okMessage = null)
{
$this->className = $operator;
}

public function getNodeType(): string
{
return $this->className;
}

/**
* @param BinaryOp $node
* @return list<RuleError>
*/
public function processNode(Node $node, Scope $scope): array
{
$leftType = $scope->getType($node->left);
$rightType = $scope->getType($node->right);
$isDirectCompareType = true;

if (!$this->isEnumWithValue($leftType) || !$this->isEnumWithValue($rightType)) {
$isDirectCompareType = false;
}

$errors = [];
$leftError = $this->processOpExpression($node->left, $leftType, $node->getOperatorSigil());
$rightError = $this->processOpExpression($node->right, $rightType, $node->getOperatorSigil());

if ($leftError !== null) {
$errors[] = $leftError;
}

if ($rightError !== null && $rightError !== $leftError) {
$errors[] = $rightError;
}

if (!$isDirectCompareType && $errors === []) {
return [];
}

if ($isDirectCompareType && $errors === []) {
$errors[] = sprintf(
'Cannot compare %s to %s',
$leftType->describe(VerbosityLevel::typeOnly()),
$rightType->describe(VerbosityLevel::typeOnly()),
);
}

return array_map(static fn (string $message) => RuleErrorBuilder::message($message)->build(), $errors);
}

private function processOpExpression(Expr $expression, Type $expressionType, string $sigil): ?string
{
return null;
}

private function isEnumWithValue(Type $type): bool
{
return false;
}

}

0 comments on commit d55c4f2

Please sign in to comment.