Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor conversion of PHPDoc to type declarations
- Loading branch information
1 parent
f2928db
commit 336a3d4
Showing
15 changed files
with
1,124 additions
and
536 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of PHP CS Fixer. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* Dariusz Rumiński <dariusz.ruminski@gmail.com> | ||
* | ||
* This source file is subject to the MIT license that is bundled | ||
* with this source code in the file LICENSE. | ||
*/ | ||
|
||
namespace PhpCsFixer; | ||
|
||
use PhpCsFixer\DocBlock\Annotation; | ||
use PhpCsFixer\DocBlock\DocBlock; | ||
use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface; | ||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver; | ||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder; | ||
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer; | ||
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer; | ||
use PhpCsFixer\Tokenizer\CT; | ||
use PhpCsFixer\Tokenizer\Token; | ||
use PhpCsFixer\Tokenizer\Tokens; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
abstract class AbstractPhpdocToTypeDeclarationFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
private $classRegex = '/^\\\\?[a-zA-Z_\\x7f-\\xff](?:\\\\?[a-zA-Z0-9_\\x7f-\\xff]+)*$/'; | ||
|
||
/** | ||
* @var array<string, int> | ||
*/ | ||
private $versionSpecificTypes = [ | ||
'void' => 70100, | ||
'iterable' => 70100, | ||
'object' => 70200, | ||
]; | ||
|
||
/** | ||
* @var array<string, bool> | ||
*/ | ||
private $scalarTypes = [ | ||
'bool' => true, | ||
'float' => true, | ||
'int' => true, | ||
'string' => true, | ||
]; | ||
|
||
/** | ||
* @var array<string, bool> | ||
*/ | ||
private $skippedTypes = [ | ||
'mixed' => true, | ||
'resource' => true, | ||
'null' => true, | ||
]; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function isRisky() | ||
{ | ||
return true; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function createConfigurationDefinition() | ||
{ | ||
return new FixerConfigurationResolver([ | ||
(new FixerOptionBuilder('scalar_types', 'Fix also scalar types; may have unexpected behaviour due to PHP bad type coercion system.')) | ||
->setAllowedTypes(['bool']) | ||
->setDefault(true) | ||
->getOption(), | ||
]); | ||
} | ||
|
||
/** | ||
* Find all the annotations of given type in the function's PHPDoc comment. | ||
* | ||
* @param string $name | ||
* @param int $index The index of the function token | ||
* | ||
* @return Annotation[] | ||
*/ | ||
protected function findAnnotations($name, Tokens $tokens, $index) | ||
{ | ||
do { | ||
$index = $tokens->getPrevNonWhitespace($index); | ||
} while ($tokens[$index]->isGivenKind([ | ||
T_COMMENT, | ||
T_ABSTRACT, | ||
T_FINAL, | ||
T_PRIVATE, | ||
T_PROTECTED, | ||
T_PUBLIC, | ||
T_STATIC, | ||
])); | ||
|
||
if (!$tokens[$index]->isGivenKind(T_DOC_COMMENT)) { | ||
return []; | ||
} | ||
|
||
$namespacesAnalyzer = new NamespacesAnalyzer(); | ||
$namespace = $namespacesAnalyzer->getNamespaceAt($tokens, $index); | ||
|
||
$namespaceUsesAnalyzer = new NamespaceUsesAnalyzer(); | ||
$namespaceUses = $namespaceUsesAnalyzer->getDeclarationsInNamespace($tokens, $namespace); | ||
|
||
$doc = new DocBlock( | ||
$tokens[$index]->getContent(), | ||
$namespace, | ||
$namespaceUses | ||
); | ||
|
||
return $doc->getAnnotationsOfType($name); | ||
} | ||
|
||
/** | ||
* @param string $type | ||
* @param bool $isNullable | ||
* | ||
* @return Token[] | ||
*/ | ||
protected function createTypeDeclarationTokens($type, $isNullable) | ||
{ | ||
static $specialTypes = [ | ||
'array' => [CT::T_ARRAY_TYPEHINT, 'array'], | ||
'callable' => [T_CALLABLE, 'callable'], | ||
]; | ||
|
||
$newTokens = []; | ||
|
||
if (true === $isNullable) { | ||
$newTokens[] = new Token([CT::T_NULLABLE_TYPE, '?']); | ||
} | ||
|
||
if (isset($specialTypes[$type])) { | ||
$newTokens[] = new Token($specialTypes[$type]); | ||
} else { | ||
foreach (explode('\\', $type) as $nsIndex => $value) { | ||
if (0 === $nsIndex && '' === $value) { | ||
continue; | ||
} | ||
|
||
if (0 < $nsIndex) { | ||
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']); | ||
} | ||
$newTokens[] = new Token([T_STRING, $value]); | ||
} | ||
} | ||
|
||
return $newTokens; | ||
} | ||
|
||
/** | ||
* @return null|array | ||
*/ | ||
protected function getCommonTypeFromAnnotation(Annotation $annotation) | ||
{ | ||
$typesExpression = $annotation->getTypeExpression(); | ||
|
||
$commonType = $typesExpression->getCommonType(); | ||
$isNullable = $typesExpression->allowsNull(); | ||
|
||
if (null === $commonType) { | ||
return null; | ||
} | ||
|
||
if ($isNullable && (\PHP_VERSION_ID < 70100 || 'void' === $commonType)) { | ||
return null; | ||
} | ||
|
||
if ('static' === $commonType) { | ||
$commonType = 'self'; | ||
} | ||
|
||
if (isset($this->skippedTypes[$commonType])) { | ||
return null; | ||
} | ||
|
||
if (isset($this->versionSpecificTypes[$commonType]) && \PHP_VERSION_ID < $this->versionSpecificTypes[$commonType]) { | ||
return null; | ||
} | ||
|
||
if (isset($this->scalarTypes[$commonType])) { | ||
if (false === $this->configuration['scalar_types']) { | ||
return null; | ||
} | ||
} elseif (1 !== Preg::match($this->classRegex, $commonType)) { | ||
return null; | ||
} | ||
|
||
return [$commonType, $isNullable]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.