Skip to content

Commit

Permalink
feat: phpdoc_to_property_type - handle virtual types and null initial…
Browse files Browse the repository at this point in the history
…ization, enable in php-highest CI job (PHP-CS-Fixer#8283)
  • Loading branch information
keradus authored Nov 18, 2024
1 parent ccad080 commit 52dc062
Show file tree
Hide file tree
Showing 23 changed files with 191 additions and 25 deletions.
7 changes: 7 additions & 0 deletions .php-cs-fixer.php-highest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
$config->setRules(array_merge($config->getRules(), [
'@PHP83Migration' => true,
'@PHP82Migration:risky' => true,
'phpdoc_to_property_type' => [ // experimental
'map_types' => [
'TFixerInputConfig' => 'array',
'TFixerComputedConfig' => 'array',
'TFixer' => '\PhpCsFixer\AbstractFixer',
],
],
'fully_qualified_strict_types' => ['import_symbols' => true],
'php_unit_attributes' => false, // as is not yet supported by PhpCsFixerInternal/configurable_fixer_template
]));
Expand Down
72 changes: 72 additions & 0 deletions dev-tools/phpstan/baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/CheckCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\CheckCommand\\:\\:\\$defaultDescription has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/CheckCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\CheckCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/CheckCommand.php',
];
$ignoreErrors[] = [
// identifier: offsetAccess.notFound
'message' => '#^Offset 1 might not exist on array\\.$#',
Expand Down Expand Up @@ -103,6 +115,18 @@
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/DescribeCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\DescribeCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/DescribeCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\DocumentationCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/DocumentationCommand.php',
];
$ignoreErrors[] = [
// identifier: argument.type
'message' => '#^Parameter \\#3 \\$cwd of class PhpCsFixer\\\\Console\\\\ConfigurationResolver constructor expects string, string\\|false given\\.$#',
Expand All @@ -115,6 +139,24 @@
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/FixCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\FixCommand\\:\\:\\$defaultDescription has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/FixCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\FixCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/FixCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\HelpCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/HelpCommand.php',
];
$ignoreErrors[] = [
// identifier: argument.type
'message' => '#^Parameter \\#2 \\$basePath of static method Symfony\\\\Component\\\\Filesystem\\\\Path\\:\\:makeRelative\\(\\) expects string, string\\|false given\\.$#',
Expand All @@ -127,6 +169,18 @@
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/ListFilesCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\ListFilesCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/ListFilesCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\ListSetsCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/ListSetsCommand.php',
];
$ignoreErrors[] = [
// identifier: offsetAccess.notFound
'message' => '#^Offset \'argv\' might not exist on array\\.$#',
Expand All @@ -139,12 +193,30 @@
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/SelfUpdateCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\SelfUpdateCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/SelfUpdateCommand.php',
];
$ignoreErrors[] = [
// identifier: offsetAccess.notFound
'message' => '#^Offset \'files\' might not exist on array\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/WorkerCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\WorkerCommand\\:\\:\\$defaultDescription has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/WorkerCommand.php',
];
$ignoreErrors[] = [
// identifier: missingType.property
'message' => '#^Property PhpCsFixer\\\\Console\\\\Command\\\\WorkerCommand\\:\\:\\$defaultName has no type specified\\.$#',
'count' => 1,
'path' => __DIR__ . '/../../src/Console/Command/WorkerCommand.php',
];
$ignoreErrors[] = [
// identifier: offsetAccess.notFound
'message' => '#^Offset \'allow\\-risky\' might not exist on array\\<string, mixed\\>\\.$#',
Expand Down
9 changes: 9 additions & 0 deletions doc/rules/function_notation/phpdoc_to_param_type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ actions are required if inherited signatures are not properly documented.
Configuration
-------------

``map_types``
~~~~~~~~~~~~~

Map of custom types, eg template types from PHPStan.

Allowed types: ``array<string, string>``

Default value: ``[]``

``scalar_types``
~~~~~~~~~~~~~~~~

Expand Down
9 changes: 9 additions & 0 deletions doc/rules/function_notation/phpdoc_to_property_type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ be required for newly typed properties that are read before initialization.
Configuration
-------------

``map_types``
~~~~~~~~~~~~~

Map of custom types, eg template types from PHPStan.

Allowed types: ``array<string, string>``

Default value: ``[]``

``scalar_types``
~~~~~~~~~~~~~~~~

Expand Down
9 changes: 9 additions & 0 deletions doc/rules/function_notation/phpdoc_to_return_type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ Manual actions are required if inherited signatures are not properly documented.
Configuration
-------------

``map_types``
~~~~~~~~~~~~~

Map of custom types, eg template types from PHPStan.

Allowed types: ``array<string, string>``

Default value: ``[]``

``scalar_types``
~~~~~~~~~~~~~~~~

Expand Down
10 changes: 10 additions & 0 deletions src/AbstractPhpdocToTypeDeclarationFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
*
* @phpstan-type _CommonTypeInfo array{commonType: string, isNullable: bool}
* @phpstan-type _AutogeneratedInputConfiguration array{
* map_types?: array<string, string>,
* scalar_types?: bool,
* union_types?: bool
* }
* @phpstan-type _AutogeneratedComputedConfiguration array{
* map_types: array<string, string>,
* scalar_types: bool,
* union_types: bool
* }
Expand Down Expand Up @@ -95,6 +97,10 @@ protected function createConfigurationDefinition(): FixerConfigurationResolverIn
->setAllowedTypes(['bool'])
->setDefault(\PHP_VERSION_ID >= 8_00_00)
->getOption(),
(new FixerOptionBuilder('map_types', 'Map of custom types, eg template types from PHPStan.'))
->setAllowedTypes(['array<string, string>'])
->setDefault([])
->getOption(),
]);
}

Expand Down Expand Up @@ -213,6 +219,10 @@ protected function getCommonTypeInfo(TypeExpression $typesExpression, bool $isRe
return null;
}

if (\array_key_exists($commonType, $this->configuration['map_types'])) {
$commonType = $this->configuration['map_types'][$commonType];
}

if (isset($this->scalarTypes[$commonType])) {
if (false === $this->configuration['scalar_types']) {
return null;
Expand Down
2 changes: 0 additions & 2 deletions src/Console/Command/CheckCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@
#[AsCommand(name: 'check', description: 'Checks if configured files/directories comply with configured rules.')]
final class CheckCommand extends FixCommand
{
/** @var string */
protected static $defaultName = 'check';

/** @var string */
protected static $defaultDescription = 'Checks if configured files/directories comply with configured rules.';

public function __construct(ToolInfoInterface $toolInfo)
Expand Down
1 change: 0 additions & 1 deletion src/Console/Command/DescribeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
#[AsCommand(name: 'describe')]
final class DescribeCommand extends Command
{
/** @var string */
protected static $defaultName = 'describe';

/**
Expand Down
1 change: 0 additions & 1 deletion src/Console/Command/DocumentationCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
#[AsCommand(name: 'documentation')]
final class DocumentationCommand extends Command
{
/** @var string */
protected static $defaultName = 'documentation';

private Filesystem $filesystem;
Expand Down
2 changes: 0 additions & 2 deletions src/Console/Command/FixCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@
#[AsCommand(name: 'fix', description: 'Fixes a directory or a file.')]
/* final */ class FixCommand extends Command
{
/** @var string */
protected static $defaultName = 'fix';

/** @var string */
protected static $defaultDescription = 'Fixes a directory or a file.';

private EventDispatcherInterface $eventDispatcher;
Expand Down
1 change: 0 additions & 1 deletion src/Console/Command/HelpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#[AsCommand(name: 'help')]
final class HelpCommand extends BaseHelpCommand
{
/** @var string */
protected static $defaultName = 'help';

/**
Expand Down
1 change: 0 additions & 1 deletion src/Console/Command/ListFilesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
#[AsCommand(name: 'list-files')]
final class ListFilesCommand extends Command
{
/** @var string */
protected static $defaultName = 'list-files';

private ConfigInterface $defaultConfig;
Expand Down
1 change: 0 additions & 1 deletion src/Console/Command/ListSetsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
#[AsCommand(name: 'list-sets')]
final class ListSetsCommand extends Command
{
/** @var string */
protected static $defaultName = 'list-sets';

protected function configure(): void
Expand Down
1 change: 0 additions & 1 deletion src/Console/Command/SelfUpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#[AsCommand(name: 'self-update')]
final class SelfUpdateCommand extends Command
{
/** @var string */
protected static $defaultName = 'self-update';

private NewVersionCheckerInterface $versionChecker;
Expand Down
2 changes: 0 additions & 2 deletions src/Console/Command/WorkerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ final class WorkerCommand extends Command
/** @var string Prefix used before JSON-encoded error printed in the worker's process */
public const ERROR_PREFIX = 'WORKER_ERROR::';

/** @var string */
protected static $defaultName = 'worker';

/** @var string */
protected static $defaultDescription = 'Internal command for running fixers in parallel';

private ToolInfoInterface $toolInfo;
Expand Down
2 changes: 2 additions & 0 deletions src/Fixer/FunctionNotation/PhpdocToParamTypeFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
* @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
*
* @phpstan-type _AutogeneratedInputConfiguration array{
* map_types?: array<string, string>,
* scalar_types?: bool,
* union_types?: bool
* }
* @phpstan-type _AutogeneratedComputedConfiguration array{
* map_types: array<string, string>,
* scalar_types: bool,
* union_types: bool
* }
Expand Down
29 changes: 24 additions & 5 deletions src/Fixer/FunctionNotation/PhpdocToPropertyTypeFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
* @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
*
* @phpstan-type _AutogeneratedInputConfiguration array{
* map_types?: array<string, string>,
* scalar_types?: bool,
* union_types?: bool
* }
* @phpstan-type _AutogeneratedComputedConfiguration array{
* map_types: array<string, string>,
* scalar_types: bool,
* union_types: bool
* }
Expand Down Expand Up @@ -101,12 +103,12 @@ public function isCandidate(Tokens $tokens): bool
/**
* {@inheritdoc}
*
* Must run before NoSuperfluousPhpdocTagsFixer, PhpdocAlignFixer.
* Must run before FullyQualifiedStrictTypesFixer, NoSuperfluousPhpdocTagsFixer, PhpdocAlignFixer.
* Must run after AlignMultilineCommentFixer, CommentToPhpdocFixer, PhpdocIndentFixer, PhpdocScalarFixer, PhpdocToCommentFixer, PhpdocTypesFixer.
*/
public function getPriority(): int
{
return 7;
return 8;
}

protected function isSkippedType(string $type): bool
Expand Down Expand Up @@ -185,10 +187,27 @@ private function fixClass(Tokens $tokens, int $index): void
[new Token([T_WHITESPACE, ' '])]
);

$tokens->insertAt(current($propertyIndices), $newTokens);
$firstPropertyIndex = current($propertyIndices);
$slices = [$firstPropertyIndex => $newTokens];

// handling of adding `= null` currently supports only _single_ declaration
if ($isNullable && 1 === \count($propertyIndices)) {
$nextTokenIndex = $tokens->getNextTokenOfKind($firstPropertyIndex, ['=', ';']);
if (null !== $nextTokenIndex && $tokens[$nextTokenIndex]->equals(';')) {
$slices[$nextTokenIndex] = [
new Token([T_WHITESPACE, ' ']),
new Token('='),
new Token([T_WHITESPACE, ' ']),
new Token([T_STRING, 'null']),
];
}
}

$tokens->insertSlices($slices);
$tokensInSlicesCount = \count($slices, COUNT_RECURSIVE) - \count($slices);

$index = max($propertyIndices) + \count($newTokens) + 1;
$classEndIndex += \count($newTokens);
$index = max($propertyIndices) + $tokensInSlicesCount + 1;
$classEndIndex += $tokensInSlicesCount;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
* @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
*
* @phpstan-type _AutogeneratedInputConfiguration array{
* map_types?: array<string, string>,
* scalar_types?: bool,
* union_types?: bool
* }
* @phpstan-type _AutogeneratedComputedConfiguration array{
* map_types: array<string, string>,
* scalar_types: bool,
* union_types: bool
* }
Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/Import/FullyQualifiedStrictTypesFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public function foo(): \Other\FunctionReturnType
* {@inheritdoc}
*
* Must run before NoSuperfluousPhpdocTagsFixer, OrderedAttributesFixer, OrderedImportsFixer, OrderedInterfacesFixer, StatementIndentationFixer.
* Must run after ClassKeywordFixer, PhpUnitAttributesFixer, PhpdocToReturnTypeFixer.
* Must run after ClassKeywordFixer, PhpUnitAttributesFixer, PhpdocToPropertyTypeFixer, PhpdocToReturnTypeFixer.
*/
public function getPriority(): int
{
Expand Down
1 change: 1 addition & 0 deletions tests/AutoReview/FixerFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ private static function getFixersPriorityGraph(): array
'no_superfluous_phpdoc_tags',
],
'phpdoc_to_property_type' => [
'fully_qualified_strict_types',
'no_superfluous_phpdoc_tags',
],
'phpdoc_to_return_type' => [
Expand Down
Loading

0 comments on commit 52dc062

Please sign in to comment.