Skip to content

Commit

Permalink
Fixes - use methods on Type
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 31, 2023
1 parent 32d7550 commit e799fc2
Show file tree
Hide file tree
Showing 27 changed files with 187 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method

if ($methodReflection->getName() === 'getByType' && count($methodCall->getArgs()) >= 2) {
$argType = $scope->getType($methodCall->getArgs()[1]->value);
if ($argType instanceof ConstantBooleanType && $argType->getValue()) {
if ($argType->isTrue()->yes()) {
$returnType = TypeCombinator::addNull($returnType);
}
}
Expand Down
41 changes: 25 additions & 16 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ private function resolveType(string $exprString, Expr $node): Type
}

$resultType = $this->initializerExprTypeResolver->resolveConcatType($resultType, $partType);
if (! $resultType instanceof ConstantStringType && ! $resultType instanceof UnionType) {
if (count($resultType->getConstantStrings()) === 0) {
return $resultType;
}
}
Expand Down Expand Up @@ -1604,25 +1604,26 @@ private function resolveType(string $exprString, Expr $node): Type
if ($node->if === null) {
$conditionType = $this->getType($node->cond);
$booleanConditionType = $conditionType->toBoolean();
if ($booleanConditionType instanceof ConstantBooleanType) {
if ($booleanConditionType->getValue()) {
return $this->filterByTruthyValue($node->cond)->getType($node->cond);
}
if ($booleanConditionType->isTrue()->yes()) {
return $this->filterByTruthyValue($node->cond)->getType($node->cond);
}

if ($booleanConditionType->isFalse()->yes()) {
return $this->filterByFalseyValue($node->cond)->getType($node->else);
}

return TypeCombinator::union(
TypeCombinator::removeFalsey($this->filterByTruthyValue($node->cond)->getType($node->cond)),
$this->filterByFalseyValue($node->cond)->getType($node->else),
);
}

$booleanConditionType = $this->getType($node->cond)->toBoolean();
if ($booleanConditionType instanceof ConstantBooleanType) {
if ($booleanConditionType->getValue()) {
return $this->filterByTruthyValue($node->cond)->getType($node->if);
}
if ($booleanConditionType->isTrue()->yes()) {
return $this->filterByTruthyValue($node->cond)->getType($node->if);
}

if ($booleanConditionType->isFalse()->yes()) {
return $this->filterByFalseyValue($node->cond)->getType($node->else);
}

Expand Down Expand Up @@ -2681,7 +2682,10 @@ public function enterNamespace(string $namespaceName): self
);
}

public function enterClosureBind(?Type $thisType, ?Type $nativeThisType, string $scopeClass): self
/**
* @param list<string> $scopeClasses
*/
public function enterClosureBind(?Type $thisType, ?Type $nativeThisType, array $scopeClasses): self
{
$expressionTypes = $this->expressionTypes;
if ($thisType !== null) {
Expand All @@ -2697,8 +2701,8 @@ public function enterClosureBind(?Type $thisType, ?Type $nativeThisType, string
unset($nativeExpressionTypes['$this']);
}

if ($scopeClass === 'static' && $this->isInClass()) {
$scopeClass = $this->getClassReflection()->getName();
if ($scopeClasses === ['static'] && $this->isInClass()) {
$scopeClasses = [$this->getClassReflection()->getName()];
}

return $this->scopeFactory->create(
Expand All @@ -2709,7 +2713,7 @@ public function enterClosureBind(?Type $thisType, ?Type $nativeThisType, string
$expressionTypes,
$nativeExpressionTypes,
$this->conditionalExpressions,
[$scopeClass],
$scopeClasses,
$this->anonymousFunctionReflection,
);
}
Expand Down Expand Up @@ -3354,7 +3358,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType):
}
}

if ($expr instanceof FuncCall && $expr->name instanceof Name && $type instanceof ConstantBooleanType && !$type->getValue()) {
if ($expr instanceof FuncCall && $expr->name instanceof Name && $type->isFalse()->yes()) {
$functionName = $this->reflectionProvider->resolveFunctionName($expr->name, $this);
if ($functionName !== null && in_array(strtolower($functionName), [
'is_dir',
Expand Down Expand Up @@ -4811,8 +4815,13 @@ private function getTypeToInstantiateForNew(Type $type): Type
return TypeCombinator::intersect(...$types);
}

if ($type instanceof ConstantStringType) {
return new ObjectType($type->getValue());
if (count($type->getConstantStrings()) > 0) {
$types = [];
foreach ($type->getConstantStrings() as $constantString) {
$types[] = new ObjectType($constantString->getValue());
}

return TypeCombinator::union(...$types);
}

if ($type instanceof GenericClassStringType) {
Expand Down
45 changes: 28 additions & 17 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeMap;
Expand Down Expand Up @@ -1916,8 +1916,8 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
foreach ($callArgs as $callArg) {
$callArgType = $scope->getType($callArg->value);
if ($callArg->unpack) {
if ($callArgType instanceof ConstantArrayType) {
$iterableValueTypes = $callArgType->getValueTypes();
if (count($callArgType->getConstantArrays()) === 1) {
$iterableValueTypes = $callArgType->getConstantArrays()[0]->getValueTypes();
} else {
$iterableValueTypes = [$callArgType->getIterableValueType()];
$nonConstantArrayWasUnpacked = true;
Expand Down Expand Up @@ -1960,7 +1960,7 @@ static function (?Type $offsetType, Type $valueType, bool $optional) use (&$arra
$valueTypes = $constantArray->getValueTypes();
foreach ($keyTypes as $k => $keyType) {
$arrayTypeBuilder->setOffsetValueType(
$keyType instanceof ConstantStringType ? $keyType : null,
count($keyType->getConstantStrings()) === 1 ? $keyType->getConstantStrings()[0] : null,
$valueTypes[$k],
$constantArray->isOptionalKey($k),
);
Expand All @@ -1969,10 +1969,11 @@ static function (?Type $offsetType, Type $valueType, bool $optional) use (&$arra

$constantArray = $arrayTypeBuilder->getArray();

if ($constantArray instanceof ConstantArrayType && $nonConstantArrayWasUnpacked) {
if ($constantArray->isConstantArray()->yes() && $nonConstantArrayWasUnpacked) {
$array = new ArrayType($constantArray->generalize(GeneralizePrecision::lessSpecific())->getIterableKeyType(), $constantArray->getIterableValueType());
$constantArray = $constantArray->isIterableAtLeastOnce()->yes()
? TypeCombinator::intersect($constantArray->generalizeKeys(), new NonEmptyArrayType())
: $constantArray->generalizeKeys();
? TypeCombinator::intersect($array, new NonEmptyArrayType())
: $array;
}

$newArrayTypes[] = $constantArray;
Expand Down Expand Up @@ -2202,27 +2203,37 @@ static function (?Type $offsetType, Type $valueType, bool $optional) use (&$arra
$nativeThisType = $nativeArgType;
}
}
$scopeClass = 'static';
$scopeClasses = ['static'];
if (isset($expr->getArgs()[2])) {
$argValue = $expr->getArgs()[2]->value;
$argValueType = $scope->getType($argValue);

$scopeClasses = [];
$directClassNames = $argValueType->getObjectClassNames();
if (count($directClassNames) === 1) {
$scopeClass = $directClassNames[0];
$thisType = new ObjectType($scopeClass);
} elseif ($argValueType instanceof ConstantStringType) {
$scopeClass = $argValueType->getValue();
$thisType = new ObjectType($scopeClass);
if (count($directClassNames) > 0) {
$scopeClasses = $directClassNames;
$thisTypes = [];
foreach ($directClassNames as $directClassName) {
$thisTypes[] = new ObjectType($directClassName);
}
$thisType = TypeCombinator::union(...$thisTypes);
} elseif (count($argValueType->getConstantStrings()) > 0) {
$thisTypes = [];
foreach ($argValueType->getConstantStrings() as $constantString) {
$scopeClasses[] = $constantString->getValue();
$thisTypes[] = new ObjectType($constantString->getValue());
}

$thisType = TypeCombinator::union(...$thisTypes);
} elseif ($argValueType instanceof GenericClassStringType) {
$genericClassNames = $argValueType->getGenericType()->getObjectClassNames();
if (count($genericClassNames) === 1) {
$scopeClass = $genericClassNames[0];
if (count($genericClassNames) > 0) {
$scopeClasses = $genericClassNames;
$thisType = $argValueType->getGenericType();
}
}
}
$closureBindScope = $scope->enterClosureBind($thisType, $nativeThisType, $scopeClass);
$closureBindScope = $scope->enterClosureBind($thisType, $nativeThisType, $scopeClasses);
}
} else {
$throwPoints[] = ThrowPoint::createImplicit($scope, $expr);
Expand Down
19 changes: 10 additions & 9 deletions src/Reflection/InitializerExprTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -484,20 +484,21 @@ public function getArrayType(Expr\Array_ $expr, callable $getTypeCallback): Type

$valueType = $getTypeCallback($arrayItem->value);
if ($arrayItem->unpack) {
if ($valueType instanceof ConstantArrayType) {
if (count($valueType->getConstantArrays()) === 1) {
$constantArrayType = $valueType->getConstantArrays()[0];
$hasStringKey = false;
foreach ($valueType->getKeyTypes() as $keyType) {
if ($keyType instanceof ConstantStringType) {
foreach ($constantArrayType->getKeyTypes() as $keyType) {
if ($keyType->isString()->yes()) {
$hasStringKey = true;
break;
}
}

foreach ($valueType->getValueTypes() as $i => $innerValueType) {
foreach ($constantArrayType->getValueTypes() as $i => $innerValueType) {
if ($hasStringKey && $this->phpVersion->supportsArrayUnpackingWithStringKeys()) {
$arrayBuilder->setOffsetValueType($valueType->getKeyTypes()[$i], $innerValueType, $valueType->isOptionalKey($i));
$arrayBuilder->setOffsetValueType($constantArrayType->getKeyTypes()[$i], $innerValueType, $constantArrayType->isOptionalKey($i));
} else {
$arrayBuilder->setOffsetValueType(null, $innerValueType, $valueType->isOptionalKey($i));
$arrayBuilder->setOffsetValueType(null, $innerValueType, $constantArrayType->isOptionalKey($i));
}
}
} else {
Expand Down Expand Up @@ -1493,9 +1494,9 @@ private function resolveCommonMath(Expr\BinaryOp $expr, Type $leftType, Type $ri

$types = TypeCombinator::union($leftType, $rightType);
if (
$leftType instanceof ArrayType
|| $rightType instanceof ArrayType
|| $types instanceof ArrayType
$leftType->isArray()->yes()
|| $rightType->isArray()->yes()
|| $types->isArray()->yes()
) {
return new ErrorType();
}
Expand Down
3 changes: 1 addition & 2 deletions src/Rules/Api/ApiInstanceofRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
Expand Down Expand Up @@ -84,7 +83,7 @@ private function processCoveredClass(Node\Expr\Instanceof_ $node, Scope $scope,
}

$instanceofType = $scope->getType($node);
if ($instanceofType instanceof ConstantBooleanType) {
if ($instanceofType->isTrue()->or($instanceofType->isFalse())->yes()) {
return [];
}

Expand Down
5 changes: 2 additions & 3 deletions src/Rules/Arrays/AppendedArrayItemTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\ArrayType;
use PHPStan\Type\VerbosityLevel;
use function sprintf;

Expand Down Expand Up @@ -62,7 +61,7 @@ public function processNode(Node $node, Scope $scope): array
}

$assignedToType = $propertyReflection->getWritableType();
if (!($assignedToType instanceof ArrayType)) {
if (!$assignedToType->isArray()->yes()) {
return [];
}

Expand All @@ -72,7 +71,7 @@ public function processNode(Node $node, Scope $scope): array
$assignedValueType = $scope->getType($node);
}

$itemType = $assignedToType->getItemType();
$itemType = $assignedToType->getIterableValueType();
$accepts = $this->ruleLevelHelper->acceptsWithReason($itemType, $assignedValueType, $scope->isDeclareStrictTypes());
if (!$accepts->result) {
$verbosityLevel = VerbosityLevel::getRecommendedLevelByType($itemType, $assignedValueType);
Expand Down
3 changes: 1 addition & 2 deletions src/Rules/Arrays/AppendedArrayKeyTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use PHPStan\Rules\Properties\PropertyReflectionFinder;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\ArrayType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
Expand Down Expand Up @@ -53,7 +52,7 @@ public function processNode(Node $node, Scope $scope): array
}

$arrayType = $propertyReflection->getReadableType();
if (!$arrayType instanceof ArrayType) {
if (!$arrayType->isArray()->yes()) {
return [];
}

Expand Down
14 changes: 2 additions & 12 deletions src/Rules/Arrays/ArrayDestructuringRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Scalar\LNumber;
use PHPStan\Analyser\Scope;
use PHPStan\Node\Expr\TypeExpr;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
Expand Down Expand Up @@ -88,11 +87,7 @@ private function getErrors(Scope $scope, Expr $var, Expr $expr): array
$keyExpr = new Node\Scalar\LNumber($i);
} else {
$keyType = $scope->getType($item->key);
if ($keyType instanceof ConstantIntegerType) {
$keyExpr = new LNumber($keyType->getValue());
} elseif ($keyType instanceof ConstantStringType) {
$keyExpr = new Node\Scalar\String_($keyType->getValue());
}
$keyExpr = new TypeExpr($keyType);
}

$itemErrors = $this->nonexistentOffsetInArrayDimFetchCheck->check(
Expand All @@ -103,11 +98,6 @@ private function getErrors(Scope $scope, Expr $var, Expr $expr): array
);
$errors = array_merge($errors, $itemErrors);

if ($keyExpr === null) {
$i++;
continue;
}

if (!$item->value instanceof Node\Expr\List_ && !$item->value instanceof Node\Expr\Array_) {
$i++;
continue;
Expand Down
26 changes: 13 additions & 13 deletions src/Rules/DateTimeInstantiationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Constant\ConstantStringType;
use Throwable;
use function count;
use function in_array;
Expand Down Expand Up @@ -38,19 +37,20 @@ public function processNode(Node $node, Scope $scope): array
}

$arg = $scope->getType($node->getArgs()[0]->value);
if (!($arg instanceof ConstantStringType)) {
return [];
}

$errors = [];
$dateString = $arg->getValue();
try {
new DateTime($dateString);
} catch (Throwable) {
// an exception is thrown for errors only but we want to catch warnings too
}
$lastErrors = DateTime::getLastErrors();
if ($lastErrors !== false) {

foreach ($arg->getConstantStrings() as $constantString) {
$dateString = $constantString->getValue();
try {
new DateTime($dateString);
} catch (Throwable) {
// an exception is thrown for errors only but we want to catch warnings too
}
$lastErrors = DateTime::getLastErrors();
if ($lastErrors === false) {
continue;
}

foreach ($lastErrors['errors'] as $error) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Instantiating %s with %s produces an error: %s',
Expand Down
Loading

0 comments on commit e799fc2

Please sign in to comment.