Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,20 @@
)->setRootExpr($rootExpr));
}

if (
$context->true()

Check warning on line 1552 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ } if ( - $context->true() + $context->truthy() && $constantType->toBoolean()->isTrue()->yes() && ($exprNode instanceof FuncCall || $exprNode instanceof Expr\MethodCall || $exprNode instanceof Expr\StaticCall) ) {

Check warning on line 1552 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ } if ( - $context->true() + $context->truthy() && $constantType->toBoolean()->isTrue()->yes() && ($exprNode instanceof FuncCall || $exprNode instanceof Expr\MethodCall || $exprNode instanceof Expr\StaticCall) ) {
&& $constantType->toBoolean()->isTrue()->yes()

Check warning on line 1553 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $context->true() - && $constantType->toBoolean()->isTrue()->yes() + && !$constantType->toBoolean()->isTrue()->no() && ($exprNode instanceof FuncCall || $exprNode instanceof Expr\MethodCall || $exprNode instanceof Expr\StaticCall) ) { $types = $this->create($exprNode, $constantType, $context, $scope)->setRootExpr($rootExpr);

Check warning on line 1553 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ( $context->true() - && $constantType->toBoolean()->isTrue()->yes() + && !$constantType->toBoolean()->isTrue()->no() && ($exprNode instanceof FuncCall || $exprNode instanceof Expr\MethodCall || $exprNode instanceof Expr\StaticCall) ) { $types = $this->create($exprNode, $constantType, $context, $scope)->setRootExpr($rootExpr);
&& ($exprNode instanceof FuncCall || $exprNode instanceof Expr\MethodCall || $exprNode instanceof Expr\StaticCall)
) {
$types = $this->create($exprNode, $constantType, $context, $scope)->setRootExpr($rootExpr);

return $types->unionWith($this->specifyTypesInCondition(
$scope,
$exprNode,
TypeSpecifierContext::createTrue(),
)->setRootExpr($rootExpr));
}

return null;
}

Expand Down
4 changes: 4 additions & 0 deletions src/Reflection/InitializerExprTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ public function getType(Expr $expr, InitializerExprContext $context): Type
$leftType = $this->getType($expr->left, $context);
$rightType = $this->getType($expr->right, $context);

if ($leftType instanceof ErrorType) {
return $rightType;
}

return TypeCombinator::union(TypeCombinator::removeNull($leftType), $rightType);
}

Expand Down
12 changes: 11 additions & 1 deletion src/Type/Php/StrRepeatFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,15 @@ public function getTypeFromFunctionCall(
$accessoryTypes = [];
if ($inputType->isNonEmptyString()->yes()) {
if (IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($multiplierType)->yes()) {
if ($inputType->isNonFalsyString()->yes()) {
if ($inputType->isNonFalsyString()->yes() || IntegerRangeType::fromInterval(2, null)->isSuperTypeOf($multiplierType)->yes()) {
$accessoryTypes[] = new AccessoryNonFalsyStringType();
} else {
$accessoryTypes[] = new AccessoryNonEmptyStringType();
}
}
}

$addedNumericString = false;
if ($inputType->isLiteralString()->yes()) {
$accessoryTypes[] = new AccessoryLiteralStringType();

Expand All @@ -93,10 +94,19 @@ public function getTypeFromFunctionCall(

if ($onlyNumbers) {
$accessoryTypes[] = new AccessoryNumericStringType();
$addedNumericString = true;
}
}
}

if (
!$addedNumericString
&& $inputType->isNumericString()->yes()
&& (new ConstantIntegerType(1))->isSuperTypeOf($multiplierType)->yes()
) {
$accessoryTypes[] = new AccessoryNumericStringType();
}

if ($inputType->isLowercaseString()->yes()) {
$accessoryTypes[] = new AccessoryLowercaseStringType();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Foo

public function doFoo(): void
{
assertType('*ERROR*', self::COALESCE_SPECIAL); // could be 42
assertType('42', self::COALESCE_SPECIAL);
assertType("0|1|2|'foo'", self::COALESCE);
assertType("'bar'|'foo'|true", self::TERNARY_SHORT);
assertType("'bar'|'foo'", self::TERNARY_FULL);
Expand Down
8 changes: 4 additions & 4 deletions tests/PHPStan/Analyser/nsrt/literal-string.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ public function doFoo($literalString, string $string, $numericString)
);
assertType('literal-string&lowercase-string&non-falsy-string', str_repeat('a', 100));
assertType('literal-string&non-falsy-string&uppercase-string', str_repeat('A', 100));
assertType('literal-string&lowercase-string&non-empty-string&numeric-string&uppercase-string', str_repeat('0', 100)); // could be non-falsy-string
assertType('literal-string&lowercase-string&non-falsy-string&numeric-string&uppercase-string', str_repeat('0', 100));
assertType('literal-string&lowercase-string&non-falsy-string&numeric-string&uppercase-string', str_repeat('1', 100));
// Repeating a numeric type multiple times can lead to a non-numeric type: 3v4l.org/aRBdZ
assertType('non-empty-string', str_repeat($numericString, 100));
assertType('non-falsy-string', str_repeat($numericString, 100));

assertType("''", str_repeat('1.23', 0));
assertType("''", str_repeat($string, 0));
assertType("''", str_repeat($numericString, 0));

// see https://3v4l.org/U4bM2
assertType("non-empty-string", str_repeat($numericString, 1)); // could be numeric-string
assertType("non-empty-string", str_repeat($numericString, 2));
assertType("non-empty-string&numeric-string", str_repeat($numericString, 1));
assertType("non-falsy-string", str_repeat($numericString, 2));
assertType("literal-string", str_repeat($literalString, 1));
$x = rand(1,2);
assertType("literal-string&lowercase-string&non-falsy-string&uppercase-string", str_repeat(' 1 ', $x));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,72 @@ public function variants(string $s) {

if (strpos($s, ':') !== false) {
assertType('non-falsy-string', $s);
} else {
assertType('string', $s);
}
assertType('string', $s);
if (strpos($s, ':') === false) {
assertType('string', $s);
} else {
assertType('non-falsy-string', $s);
}
assertType('string', $s);

if (strpos($s, ':') === 5) {
if (strpos($s, '0') == 0) { // 0|false
assertType('string', $s);
} else {
assertType('string', $s);
}
assertType('string', $s);

$oneOrZero = rand(0, 1);
if (strpos($s, '0') == $oneOrZero) { // 0|1|false
assertType('string', $s);
} else {
assertType('string', $s);
}
assertType('string', $s);

$oneOrZero = rand(0, 1);
if (strpos($s, '0') === $oneOrZero) {
assertType('string', $s); // could be non-empty-string
} else {
assertType('string', $s);
}
assertType('string', $s);

if (strpos($s, '0') == 1) {
assertType('string', $s); // could be non-empty-string
} else {
assertType('string', $s);
}
assertType('string', $s);

if (strpos($s, '0') === 0) {
assertType('string', $s); // could be non-empty-string
} else {
assertType('string', $s);
}
assertType('string', $s);

if (strpos($s, '0') === 5) {
assertType('non-empty-string', $s); // could be non-falsy-string
} else {
assertType('string', $s);
}
assertType('string', $s);

if (strpos($s, ':') === 5) {
assertType('non-falsy-string', $s);
} else {
assertType('string', $s);
}
assertType('string', $s);

if (strpos($s, ':') !== 5) {
assertType('string', $s);
} else {
assertType('non-falsy-string', $s);
}
assertType('string', $s);

Expand Down Expand Up @@ -152,7 +205,7 @@ public function variants(string $s) {
assertType('string', $s);

if (mb_strpos($s, ':') === 5) {
assertType('string', $s); // could be non-empty-string
assertType('non-falsy-string', $s);
}
assertType('string', $s);
if (mb_strpos($s, ':') !== 5) {
Expand Down
Loading