Skip to content

Commit

Permalink
added AccessoryNonFalsyStringType
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm authored and ondrejmirtes committed Jul 26, 2022
1 parent 6478c73 commit 1ec152b
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/PhpDoc/TypeNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Accessory\AccessoryLiteralStringType;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
use PHPStan\Type\Accessory\AccessoryNumericStringType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
Expand Down Expand Up @@ -242,6 +243,12 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco
new AccessoryNonEmptyStringType(),
]);

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

case 'bool':
return new BooleanType();

Expand Down
6 changes: 6 additions & 0 deletions src/Type/Accessory/AccessoryNonEmptyStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Type\Accessory;

use PHPStan\TrinaryLogic;
use PHPStan\Type\BooleanType;
use PHPStan\Type\CompoundType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantIntegerType;
Expand Down Expand Up @@ -144,6 +145,11 @@ public function toFloat(): Type
return new FloatType();
}

public function toBoolean(): BooleanType
{
return new BooleanType();
}

public function toString(): Type
{
return $this;
Expand Down
203 changes: 203 additions & 0 deletions src/Type/Accessory/AccessoryNonFalsyStringType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Accessory;

use PHPStan\TrinaryLogic;
use PHPStan\Type\BooleanType;
use PHPStan\Type\CompoundType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\FloatType;
use PHPStan\Type\GeneralizePrecision;
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\NonRemoveableTypeTrait;
use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;

class AccessoryNonFalsyStringType implements CompoundType, AccessoryType
{

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

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

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

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

return $type->isNonEmptyString();
}

public function isSuperTypeOf(Type $type): TrinaryLogic
{
if ($type instanceof CompoundType) {
return $type->isSubTypeOf($this);
}

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(VerbosityLevel $level): string
{
return 'non-falsy-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 unsetOffset(Type $offsetType): Type
{
return new ErrorType();
}

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

public function toNumber(): Type
{
return new ErrorType();
}

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

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

public function toBoolean(): BooleanType
{
return new ConstantBooleanType(true);
}

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

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

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

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

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

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

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

public function generalize(GeneralizePrecision $precision): Type
{
return new StringType();
}

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,16 @@ public function testBug6473(): void
$this->analyse([__DIR__ . '/data/bug-6473.php'], []);
}

public function testBug5317(): void
{
$this->treatPhpDocTypesAsCertain = true;
$this->analyse([__DIR__ . '/data/bug-5317.php'], [
[
'Negated boolean expression is always false.',
18,
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
],
]);
}

}
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/bug-5317.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Bug5317;

/**
* @param non-empty-string $in
*/
function nonEmptyString(string $in): void
{
var_dump(!$in);
}

/**
* @param non-falsy-string $in
*/
function nonFalsyString(string $in): void
{
var_dump(!$in);
}

0 comments on commit 1ec152b

Please sign in to comment.