Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make dumpType() more consistent with PHPDoc types #3555

Closed
wants to merge 3 commits into from
Closed
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
56 changes: 56 additions & 0 deletions src/Internal/StringLiteralHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php declare(strict_types = 1);

namespace PHPStan\Internal;

use function addcslashes;
use function preg_match;
use function sprintf;
use function str_contains;

final class StringLiteralHelper
{

public static function escape(string $string): string
{
return addcslashes($string, "\0..\37\177\\");
}

public static function escapeAndQuoteIfNeeded(string $string): string
{
if ($string === '') {
return "''";
}

$escaped = self::escape($string);

if (str_contains($escaped, '\\') || str_contains($escaped, "'")) {
return $escaped === addcslashes($string, '\'\\')
? sprintf("'%s'", $escaped)
: sprintf('"%s"', addcslashes($escaped, '"'));
}

if (preg_match('/[!-,.\/:-@[-^`{|}~]/', $escaped) === 1 || preg_match('/\p{Zs}/u', $escaped) === 1) {
return sprintf("'%s'", addcslashes($escaped, "\\'"));
}

return $escaped;
}

public static function quote(string $string): string
{
if ($string === '') {
return "''";
}

$escaped = self::escape($string);

if (str_contains($escaped, '\\') || str_contains($escaped, "'")) {
return $escaped === addcslashes($string, '\'\\')
? sprintf("'%s'", $escaped)
: sprintf('"%s"', addcslashes($escaped, '"'));
}

return sprintf("'%s'", addcslashes($escaped, "\\'"));
}

}
8 changes: 2 additions & 6 deletions src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Analyser\OutOfClassScope;
use PHPStan\DependencyInjection\BleedingEdgeToggle;
use PHPStan\Internal\CombinationsHelper;
use PHPStan\Internal\StringLiteralHelper;
use PHPStan\Php\PhpVersion;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
Expand Down Expand Up @@ -66,7 +67,6 @@
use function range;
use function sort;
use function sprintf;
use function str_contains;

/**
* @api
Expand Down Expand Up @@ -1474,11 +1474,7 @@ public function describe(VerbosityLevel $level): string

$keyDescription = $keyType->getValue();
if (is_string($keyDescription)) {
if (str_contains($keyDescription, '"')) {
$keyDescription = sprintf('\'%s\'', $keyDescription);
} elseif (str_contains($keyDescription, '\'')) {
$keyDescription = sprintf('"%s"', $keyDescription);
}
$keyDescription = StringLiteralHelper::escapeAndQuoteIfNeeded($keyDescription);
}

$valueTypeDescription = $valueType->describe($level);
Expand Down
9 changes: 2 additions & 7 deletions src/Type/Constant/ConstantStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PhpParser\Node\Name;
use PHPStan\Analyser\OutOfClassScope;
use PHPStan\DependencyInjection\BleedingEdgeToggle;
use PHPStan\Internal\StringLiteralHelper;
use PHPStan\PhpDocParser\Ast\ConstExpr\QuoteAwareConstExprStringNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
Expand Down Expand Up @@ -43,7 +44,6 @@
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\VerbosityLevel;
use function addcslashes;
use function in_array;
use function is_float;
use function is_int;
Expand Down Expand Up @@ -139,12 +139,7 @@ function (): string {

private function export(string $value): string
{
$escapedValue = addcslashes($value, "\0..\37");
if ($escapedValue !== $value) {
return '"' . addcslashes($value, "\0..\37\\\"") . '"';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The escape of DEL (\177) was missing.

}

return "'" . addcslashes($value, '\\\'') . "'";
return StringLiteralHelper::quote($value);
}

public function isSuperTypeOf(Type $type): TrinaryLogic
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ public function dataCasts(): array
'(float) $str',
],
[
"array{\0TypesNamespaceCasts\\Foo\0foo: TypesNamespaceCasts\\Foo, \0TypesNamespaceCasts\\Foo\0int: int, \0*\0protectedInt: int, publicInt: int, \0TypesNamespaceCasts\\Bar\0barProperty: TypesNamespaceCasts\\Bar}",
'array{"\000TypesNamespaceCasts\\\\Foo\000foo": TypesNamespaceCasts\Foo, "\000TypesNamespaceCasts\\\\Foo\000int": int, "\000*\000protectedInt": int, publicInt: int, "\000TypesNamespaceCasts\\\\Bar\000barProperty": TypesNamespaceCasts\Bar}',
'(array) $foo',
],
[
Expand Down
3 changes: 2 additions & 1 deletion tests/PHPStan/Analyser/data/bug-5081.php

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions tests/PHPStan/Analyser/nsrt/array-combine-php7.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ function withBoolKey(): void
$c = [false, 'red', 'yellow'];
$d = ['avocado', 'apple', 'banana'];

assertType("array{: 'avocado', red: 'apple', yellow: 'banana'}", array_combine($c, $d));
assertType("array{'': 'avocado', red: 'apple', yellow: 'banana'}", array_combine($c, $d));
}

function withFloatKey(): void
{
$a = [1.5, 'red', 'yellow'];
$b = ['avocado', 'apple', 'banana'];

assertType("array{1.5: 'avocado', red: 'apple', yellow: 'banana'}", array_combine($a, $b));
assertType("array{'1.5': 'avocado', red: 'apple', yellow: 'banana'}", array_combine($a, $b));
}

function withIntegerKey(): void
Expand Down
4 changes: 2 additions & 2 deletions tests/PHPStan/Analyser/nsrt/array-combine-php8.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ function withBoolKey(): void
$c = [false, 'red', 'yellow'];
$d = ['avocado', 'apple', 'banana'];

assertType("array{: 'avocado', red: 'apple', yellow: 'banana'}", array_combine($c, $d));
assertType("array{'': 'avocado', red: 'apple', yellow: 'banana'}", array_combine($c, $d));
}

function withFloatKey(): void
{
$a = [1.5, 'red', 'yellow'];
$b = ['avocado', 'apple', 'banana'];

assertType("array{1.5: 'avocado', red: 'apple', yellow: 'banana'}", array_combine($a, $b));
assertType("array{'1.5': 'avocado', red: 'apple', yellow: 'banana'}", array_combine($a, $b));
}

function withIntegerKey(): void
Expand Down
4 changes: 2 additions & 2 deletions tests/PHPStan/Analyser/nsrt/array-fill-keys.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ class Baz {}
function withBoolKey() : array
{
assertType("array{1: 'b'}", array_fill_keys([true], 'b'));
assertType("array{: 'b'}", array_fill_keys([false], 'b'));
assertType("array{'': 'b'}", array_fill_keys([false], 'b'));
}

function withFloatKey() : array
{
assertType("array{1.5: 'b'}", array_fill_keys([1.5], 'b'));
assertType("array{'1.5': 'b'}", array_fill_keys([1.5], 'b'));
}

function withIntegerKey() : array
Expand Down
4 changes: 2 additions & 2 deletions tests/PHPStan/Analyser/nsrt/array-shapes-keys-strings.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Foo
*/
public function doFoo(array $slash, array $dollar): void
{
assertType('array{namespace/key: string}', $slash);
assertType('array<int, array{$ref: string}>', $dollar);
assertType("array{'namespace/key': string}", $slash);
assertType("array<int, array{'\$ref': string}>", $dollar);
}

}
6 changes: 3 additions & 3 deletions tests/PHPStan/Analyser/nsrt/bug-11716.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function parse(string $glue): string
{
$seenGlues = ['|' => false, '&' => false];

assertType("array{|: false, &: false}", $seenGlues);
assertType("array{'|': false, '&': false}", $seenGlues);

if ($glue !== '') {
assertType('non-empty-string', $glue);
Expand All @@ -22,13 +22,13 @@ public function parse(string $glue): string
$seenGlues[$glue] = true;

assertType("'&'|'|'", $glue);
assertType('array{|: bool, &: bool}', $seenGlues);
assertType("array{'|': bool, '&': bool}", $seenGlues);
} else {
assertType("''", $glue);
}

assertType("''|'&'|'|'", $glue);
assertType("array{|: bool, &: bool}", $seenGlues);
assertType("array{'|': bool, '&': bool}", $seenGlues);

return array_key_first($seenGlues);
}
Expand Down
3 changes: 1 addition & 2 deletions tests/PHPStan/Analyser/nsrt/bug-7621-1.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public function unpackShortcut(string $shortcut, array $supportedGroups): array
!array_key_exists($groupOrShortcut, self::SHORTCUTS)
) {
// Nothing
assertType("array{constants: array{'public constants', 'protected constants', 'private constants'}, static properties: array{'public static properties', 'protected static properties', 'private static properties'}, properties: array{'static properties', 'public properties', 'protected properties', 'private properties'}, all public methods: array{'public final methods', 'public static final methods', 'public abstract methods', 'public static abstract methods', 'public static methods', 'public methods'}, all protected methods: array{'protected final methods', 'protected static final methods', 'protected abstract methods', 'protected static abstract methods', 'protected static methods', 'protected methods'}, all private methods: array{'private static methods', 'private methods'}, final methods: array{'public final methods', 'protected final methods', 'public static final methods', 'protected static final methods'}, abstract methods: array{'public abstract methods', 'protected abstract methods', 'public static abstract methods', 'protected static abstract methods'}, static methods: array{'static constructors', 'public static final methods', 'protected static final methods', 'public static abstract methods', 'protected static abstract methods', 'public static methods', 'protected static methods', 'private static methods'}, methods: array{'final methods', 'abstract methods', 'static methods', 'constructor', 'destructor', 'public methods', 'protected methods', 'private methods', 'magic methods'}}", self::SHORTCUTS);
assertType("array{constants: array{'public constants', 'protected constants', 'private constants'}, 'static properties': array{'public static properties', 'protected static properties', 'private static properties'}, properties: array{'static properties', 'public properties', 'protected properties', 'private properties'}, 'all public methods': array{'public final methods', 'public static final methods', 'public abstract methods', 'public static abstract methods', 'public static methods', 'public methods'}, 'all protected methods': array{'protected final methods', 'protected static final methods', 'protected abstract methods', 'protected static abstract methods', 'protected static methods', 'protected methods'}, 'all private methods': array{'private static methods', 'private methods'}, 'final methods': array{'public final methods', 'protected final methods', 'public static final methods', 'protected static final methods'}, 'abstract methods': array{'public abstract methods', 'protected abstract methods', 'public static abstract methods', 'protected static abstract methods'}, 'static methods': array{'static constructors', 'public static final methods', 'protected static final methods', 'public static abstract methods', 'protected static abstract methods', 'public static methods', 'protected static methods', 'private static methods'}, methods: array{'final methods', 'abstract methods', 'static methods', 'constructor', 'destructor', 'public methods', 'protected methods', 'private methods', 'magic methods'}}", self::SHORTCUTS);
assertType("array{'public final methods', 'protected final methods', 'public static final methods', 'protected static final methods'}", self::SHORTCUTS[self::GROUP_SHORTCUT_FINAL_METHODS]);
} else {
$groups = array_merge($groups, $this->unpackShortcut($groupOrShortcut, $supportedGroups));
Expand All @@ -146,4 +146,3 @@ public function unpackShortcut(string $shortcut, array $supportedGroups): array
}

}

1 change: 0 additions & 1 deletion tests/PHPStan/Analyser/nsrt/static-has-method.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,3 @@ public function sayHello(): void
$it = rex_var::varsIterator();
}
}

Loading
Loading