Skip to content

Commit

Permalink
non-empty-string
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 7, 2021
1 parent 2a1597b commit 6ef5e91
Show file tree
Hide file tree
Showing 44 changed files with 664 additions and 13 deletions.
5 changes: 5 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

-
class: PHPStan\Type\Php\StrTokFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

-
class: PHPStan\Type\Php\SprintfFunctionDynamicReturnTypeExtension
tags:
Expand Down
2 changes: 1 addition & 1 deletion resources/functionMap_php80delta.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
'date_time_set' => ['DateTime', 'object'=>'DateTime', 'hour'=>'int', 'minute'=>'int', 'second='=>'int', 'microseconds='=>'int'],
'date_timestamp_set' => ['DateTime', 'object'=>'DateTime', 'unixtimestamp'=>'int'],
'date_timezone_set' => ['DateTime', 'object'=>'DateTime', 'timezone'=>'DateTimeZone'],
'explode' => ['array<int,string>', 'separator'=>'string', 'str'=>'string', 'limit='=>'int'],
'explode' => ['array<int,string>', 'separator'=>'non-empty-string', 'str'=>'string', 'limit='=>'int'],
'fdiv' => ['float', 'dividend'=>'float', 'divisor'=>'float'],
'get_debug_type' => ['string', 'var'=>'mixed'],
'get_resource_id' => ['int', 'res'=>'resource'],
Expand Down
45 changes: 43 additions & 2 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use PhpParser\Node\Name;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Accessory\HasOffsetType;
use PHPStan\Type\Accessory\HasPropertyType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
Expand All @@ -41,6 +42,7 @@
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\StaticType;
use PHPStan\Type\StaticTypeFactory;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
Expand Down Expand Up @@ -219,6 +221,26 @@ public function specifyTypesInCondition(
}
}
}

if (
!$context->null()
&& $exprNode instanceof FuncCall
&& count($exprNode->args) === 1
&& $exprNode->name instanceof Name
&& strtolower((string) $exprNode->name) === 'strlen'
&& $constantType instanceof ConstantIntegerType
) {
if ($context->truthy() || $constantType->getValue() === 0) {
$newContext = $context;
if ($constantType->getValue() === 0) {
$newContext = $newContext->negate();
}
$argType = $scope->getType($exprNode->args[0]->value);
if ($argType instanceof StringType) {
return $this->create($exprNode->args[0]->value, new AccessoryNonEmptyStringType(), $newContext, false, $scope);
}
}
}
}

if ($context->true()) {
Expand Down Expand Up @@ -383,11 +405,11 @@ public function specifyTypesInCondition(
$expr->left instanceof FuncCall
&& count($expr->left->args) === 1
&& $expr->left->name instanceof Name
&& strtolower((string) $expr->left->name) === 'count'
&& in_array(strtolower((string) $expr->left->name), ['count', 'strlen'], true)
&& (
!$expr->right instanceof FuncCall
|| !$expr->right->name instanceof Name
|| strtolower((string) $expr->right->name) !== 'count'
|| !in_array(strtolower((string) $expr->right->name), ['count', 'strlen'], true)
)
) {
$inverseOperator = $expr instanceof Node\Expr\BinaryOp\Smaller
Expand Down Expand Up @@ -422,6 +444,25 @@ public function specifyTypesInCondition(
}
}

if (
!$context->null()
&& $expr->right instanceof FuncCall
&& count($expr->right->args) === 1
&& $expr->right->name instanceof Name
&& strtolower((string) $expr->right->name) === 'strlen'
&& (new IntegerType())->isSuperTypeOf($leftType)->yes()
) {
if (
$context->truthy() && (IntegerRangeType::createAllGreaterThanOrEqualTo(1 - $offset)->isSuperTypeOf($leftType)->yes())
|| ($context->falsey() && (new ConstantIntegerType(1 - $offset))->isSuperTypeOf($leftType)->yes())
) {
$argType = $scope->getType($expr->right->args[0]->value);
if ($argType instanceof StringType) {
$result = $result->unionWith($this->create($expr->right->args[0]->value, new AccessoryNonEmptyStringType(), $context, false, $scope));
}
}
}

if ($leftType instanceof ConstantIntegerType) {
if ($expr->right instanceof Expr\PostInc) {
$result = $result->unionWith($this->createRangeTypes(
Expand Down
2 changes: 1 addition & 1 deletion src/File/FuzzyRelativePathHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class FuzzyRelativePathHelper implements RelativePathHelper
* @param RelativePathHelper $fallbackRelativePathHelper
* @param string $currentWorkingDirectory
* @param string[] $analysedPaths
* @param string|null $directorySeparator
* @param non-empty-string|null $directorySeparator
*/
public function __construct(
RelativePathHelper $fallbackRelativePathHelper,
Expand Down
7 changes: 7 additions & 0 deletions src/PhpDoc/TypeNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use PHPStan\Reflection\Native\NativeParameterReflection;
use PHPStan\Reflection\PassedByReference;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Accessory\AccessoryNumericStringType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
Expand Down Expand Up @@ -188,6 +189,12 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco
new AccessoryNumericStringType(),
]);

case 'non-empty-string':
return new IntersectionType([
new StringType(),
new AccessoryNonEmptyStringType(),
]);

case 'bool':
return new BooleanType();

Expand Down
172 changes: 172 additions & 0 deletions src/Type/Accessory/AccessoryNonEmptyStringType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Accessory;

use PHPStan\TrinaryLogic;
use PHPStan\Type\CompoundType;
use PHPStan\Type\CompoundTypeHelper;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\StringType;
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
use PHPStan\Type\Traits\NonGenericTypeTrait;
use PHPStan\Type\Traits\NonIterableTypeTrait;
use PHPStan\Type\Traits\NonObjectTypeTrait;
use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;

class AccessoryNonEmptyStringType implements CompoundType, AccessoryType
{

use MaybeCallableTypeTrait;
use NonObjectTypeTrait;
use NonIterableTypeTrait;
use TruthyBooleanTypeTrait;
use UndecidedComparisonCompoundTypeTrait;
use NonGenericTypeTrait;

/** @api */
public function __construct()
{
}

public function getReferencedClasses(): array
{
return [];
}

public function accepts(Type $type, bool $strictTypes): TrinaryLogic
{
if ($type instanceof CompoundType) {
return CompoundTypeHelper::accepts($type, $this, $strictTypes);
}

return $type->isNonEmptyString();
}

public function isSuperTypeOf(Type $type): TrinaryLogic
{
if ($this->equals($type)) {
return TrinaryLogic::createYes();
}

return $type->isNonEmptyString();
}

public function isSubTypeOf(Type $otherType): TrinaryLogic
{
if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
return $otherType->isSuperTypeOf($this);
}

return $otherType->isNonEmptyString()
->and($otherType instanceof self ? TrinaryLogic::createYes() : TrinaryLogic::createMaybe());
}

public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
{
return $this->isSubTypeOf($acceptingType);
}

public function equals(Type $type): bool
{
return $type instanceof self;
}

public function describe(\PHPStan\Type\VerbosityLevel $level): string
{
return 'non-empty-string';
}

public function isOffsetAccessible(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return (new IntegerType())->isSuperTypeOf($offsetType)->and(TrinaryLogic::createMaybe());
}

public function getOffsetValueType(Type $offsetType): Type
{
if ($this->hasOffsetValueType($offsetType)->no()) {
return new ErrorType();
}

if ((new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes()) {
return new IntersectionType([new StringType(), new AccessoryNonEmptyStringType()]);
}

return new StringType();
}

public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
{
return $this;
}

public function isArray(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function toNumber(): Type
{
return new UnionType([
$this->toInteger(),
$this->toFloat(),
]);
}

public function toInteger(): Type
{
return new IntegerType();
}

public function toFloat(): Type
{
return new FloatType();
}

public function toString(): Type
{
return $this;
}

public function toArray(): Type
{
return new ConstantArrayType(
[new ConstantIntegerType(0)],
[$this],
1
);
}

public function isNumericString(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function traverse(callable $cb): Type
{
return $this;
}

public static function __set_state(array $properties): Type
{
return new self();
}

}
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryNumericStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ public function isNumericString(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function traverse(callable $cb): Type
{
return $this;
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasOffsetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ public function isNumericString(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function toNumber(): Type
{
return new ErrorType();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/NonEmptyArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ public function isNumericString(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function toNumber(): Type
{
return new ErrorType();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ public function isNumericString(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isOffsetAccessible(): TrinaryLogic
{
return TrinaryLogic::createYes();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/CallableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ public function isNumericString(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isCommonCallable(): bool
{
return $this->isCommonCallable;
Expand Down
10 changes: 10 additions & 0 deletions src/Type/ClassStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public function isSuperTypeOf(Type $type): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isNumericString(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

/**
* @param mixed[] $properties
* @return Type
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ClosureType.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,11 @@ public function isNumericString(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isNonEmptyString(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

/**
* @param mixed[] $properties
* @return Type
Expand Down
Loading

0 comments on commit 6ef5e91

Please sign in to comment.