diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 5f73560bd7..bbefa0933c 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -687,8 +687,17 @@ private function resolveType(Expr $node): Type if ($type instanceof ConstantStringType) { return new ConstantStringType(~$type->getValue()); } - if ($type instanceof StringType) { - return new StringType(); + if ($type->isString()->yes()) { + $accessories = [ + new StringType(), + ]; + if ($type->isNonEmptyString()->yes()) { + $accessories[] = new AccessoryNonEmptyStringType(); + } + // it is not useful to apply numeric and literal strings here. + // numeric string isn't certainly kept numeric: 3v4l.org/JERDB + + return TypeCombinator::intersect(...$accessories); } if ($type instanceof IntegerType || $type instanceof FloatType) { return new IntegerType(); //no const types here, result depends on PHP_INT_SIZE diff --git a/tests/PHPStan/Analyser/data/bitwise-not.php b/tests/PHPStan/Analyser/data/bitwise-not.php index f7f07c1996..d3ec67530c 100644 --- a/tests/PHPStan/Analyser/data/bitwise-not.php +++ b/tests/PHPStan/Analyser/data/bitwise-not.php @@ -6,10 +6,12 @@ /** * @param string|int $stringOrInt + * @param non-empty-string $nonEmptyString */ -function foo(int $int, string $string, float $float, $stringOrInt) : void{ +function foo(int $int, string $string, float $float, $stringOrInt, string $nonEmptyString) : void{ assertType('int', ~$int); assertType('string', ~$string); + assertType('non-empty-string', ~$nonEmptyString); assertType('int', ~$float); assertType('int|string', ~$stringOrInt); assertType("'" . (~"abc") . "'", ~"abc");