Bug report
StringsLengthTypeSpecifiyingExtension narrows the argument of Strings::length($s) to non-empty-string when the call is analyzed in a truthy/true context. That is useful for a direct truthiness check such as:
if (Strings::length($s)) {
}
However, the same narrowing currently also leads to an alreadyNarrowedType diagnostic when the call is used as the numeric operand of a comparison, for example a max-length guard:
if ($s !== '' && Strings::length($s) < 100) {
}
In this expression, the condition being tested is not "is Strings::length($s) truthy?" but "is the numeric length less than 100?". Even when $s is already known to be non-empty-string, the comparison result is not constant; it still depends on the runtime length.
Code snippet that reproduces the problem
declare(strict_types=1);
use Nette\Utils\Strings;
function withoutPriorCheck(string $body): void
{
if (Strings::length($body) < 100) { // no error
echo 'a';
}
}
function withPriorCheck(string $body): void
{
if ($body !== '' && Strings::length($body) < 100) { // false positive
echo 'a';
}
}
Expected output
No error for withPriorCheck(). The prior $body !== '' guard only makes the result of Strings::length($body) non-zero; it does not make Strings::length($body) < 100 always true.
Actual output
Call to static method Nette\Utils\Strings::length() with non-empty-string will always evaluate to true.
staticMethod.alreadyNarrowedType
The diagnostic appears to treat the Strings::length($body) call as if its truthiness were the relevant check, but in this code the call's numeric result is used in a < 100 comparison.
Versions
- phpstan/phpstan: 2.2.3
- phpstan/phpstan-nette: 2.0.11
- PHPStan level: 8
Related pattern: phpstan/phpstan-webmozart-assert#191
Bug report
StringsLengthTypeSpecifiyingExtensionnarrows the argument ofStrings::length($s)tonon-empty-stringwhen the call is analyzed in a truthy/true context. That is useful for a direct truthiness check such as:However, the same narrowing currently also leads to an
alreadyNarrowedTypediagnostic when the call is used as the numeric operand of a comparison, for example a max-length guard:In this expression, the condition being tested is not "is
Strings::length($s)truthy?" but "is the numeric length less than 100?". Even when $s is already known to benon-empty-string, the comparison result is not constant; it still depends on the runtime length.Code snippet that reproduces the problem
Expected output
No error for
withPriorCheck(). The prior$body !== ''guard only makes the result ofStrings::length($body)non-zero; it does not makeStrings::length($body) < 100always true.Actual output
The diagnostic appears to treat the
Strings::length($body)call as if its truthiness were the relevant check, but in this code the call's numeric result is used in a< 100comparison.Versions
Related pattern: phpstan/phpstan-webmozart-assert#191