Skip to content

Commit

Permalink
Add remove_inheritdoc option
Browse files Browse the repository at this point in the history
  • Loading branch information
julienfalque committed Sep 13, 2019
1 parent ccd2600 commit ab78266
Show file tree
Hide file tree
Showing 3 changed files with 850 additions and 34 deletions.
1 change: 1 addition & 0 deletions README.rst
Expand Up @@ -1122,6 +1122,7 @@ Choose from the list of available rules:

- ``allow_mixed`` (``bool``): whether type ``mixed`` without description is allowed
(``true``) or considered superfluous (``false``); defaults to ``false``
- ``remove_inheritdoc`` (``bool``): remove ``@inheritDoc`` tags; defaults to ``false``

* **no_trailing_comma_in_list_call** [@Symfony, @PhpCsFixer]

Expand Down
188 changes: 154 additions & 34 deletions src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.php
Expand Up @@ -67,6 +67,14 @@ class Foo {
public function doFoo(Bar $bar, $baz): Baz {}
}
', new VersionSpecification(70000)),
new CodeSample('<?php
class Foo {
/**
* @inheritDoc
*/
public function doFoo(Bar $bar, $baz) {}
}
', ['remove_inheritdoc' => true]),
]
);
}
Expand Down Expand Up @@ -105,46 +113,27 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
continue;
}

$functionIndex = $this->findDocumentedFunction($tokens, $index);
if (null === $functionIndex) {
continue;
}

$docBlock = new DocBlock($token->getContent());
$content = $initialContent = $token->getContent();

$openingParenthesisIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
$closingParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingParenthesisIndex);
$documentedElementIndex = $this->findDocumentedElement($tokens, $index);

$argumentsInfo = $this->getArgumentsInfo(
$tokens,
$openingParenthesisIndex + 1,
$closingParenthesisIndex - 1
);

foreach ($docBlock->getAnnotationsOfType('param') as $annotation) {
if (0 === Preg::match('/@param(?:\s+[^\$]\S+)?\s+(\$\S+)/', $annotation->getContent(), $matches)) {
continue;
}
if (null === $documentedElementIndex) {
continue;
}

$argumentName = $matches[1];
$token = $tokens[$documentedElementIndex];

if (
!isset($argumentsInfo[$argumentName])
|| $this->annotationIsSuperfluous($annotation, $argumentsInfo[$argumentName], $shortNames)
) {
$annotation->remove();
}
if ($token->isGivenKind(T_FUNCTION)) {
$content = $this->fixFunctionDocComment($content, $tokens, $index, $shortNames);
}

$returnTypeInfo = $this->getReturnTypeInfo($tokens, $closingParenthesisIndex);

foreach ($docBlock->getAnnotationsOfType('return') as $annotation) {
if ($this->annotationIsSuperfluous($annotation, $returnTypeInfo, $shortNames)) {
$annotation->remove();
}
if ($this->configuration['remove_inheritdoc']) {
$content = $this->removeSuperfluousInheritDoc($content);
}

$tokens[$index] = new Token([T_DOC_COMMENT, $docBlock->getContent()]);
if ($content !== $initialContent) {
$tokens[$index] = new Token([T_DOC_COMMENT, $content]);
}
}
}

Expand All @@ -158,22 +147,97 @@ protected function createConfigurationDefinition()
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
(new FixerOptionBuilder('remove_inheritdoc', 'Remove `@inheritDoc` tags'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}

private function findDocumentedFunction(Tokens $tokens, $index)
/**
* @param Tokens $tokens
* @param int $docCommentIndex
*
* @return null|int
*/
private function findDocumentedElement(Tokens $tokens, $docCommentIndex)
{
$index = $docCommentIndex;

do {
$index = $tokens->getNextMeaningfulToken($index);

if (null === $index || $tokens[$index]->isGivenKind(T_FUNCTION)) {
if (null === $index || $tokens[$index]->isGivenKind([T_FUNCTION, T_CLASS, T_INTERFACE])) {
return $index;
}
} while ($tokens[$index]->isGivenKind([T_ABSTRACT, T_FINAL, T_STATIC, T_PRIVATE, T_PROTECTED, T_PUBLIC]));

$index = $tokens->getNextMeaningfulToken($docCommentIndex);

$kindsBeforeProperty = [T_STATIC, T_PRIVATE, T_PROTECTED, T_PUBLIC];

if (!$tokens[$index]->isGivenKind($kindsBeforeProperty)) {
return null;
}

do {
$index = $tokens->getNextMeaningfulToken($index);

if ($tokens[$index]->isGivenKind(T_VARIABLE)) {
return $index;
}
} while ($tokens[$index]->isGivenKind($kindsBeforeProperty));

return null;
}

/**
* @param string $content
* @param Tokens $tokens
* @param int $functionIndex
* @param array $shortNames
*
* @return string
*/
private function fixFunctionDocComment($content, Tokens $tokens, $functionIndex, array $shortNames)
{
$docBlock = new DocBlock($content);

$openingParenthesisIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
$closingParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingParenthesisIndex);

$argumentsInfo = $this->getArgumentsInfo(
$tokens,
$openingParenthesisIndex + 1,
$closingParenthesisIndex - 1
);

foreach ($docBlock->getAnnotationsOfType('param') as $annotation) {
if (0 === Preg::match('/@param(?:\s+[^\$]\S+)?\s+(\$\S+)/', $annotation->getContent(), $matches)) {
continue;
}

$argumentName = $matches[1];

if (
!isset($argumentsInfo[$argumentName])
|| $this->annotationIsSuperfluous($annotation, $argumentsInfo[$argumentName], $shortNames)
) {
$annotation->remove();
}
}

$returnTypeInfo = $this->getReturnTypeInfo($tokens, $closingParenthesisIndex);

foreach ($docBlock->getAnnotationsOfType('return') as $annotation) {
if ($this->annotationIsSuperfluous($annotation, $returnTypeInfo, $shortNames)) {
$annotation->remove();
}
}

return $docBlock->getContent();
}

/**
* @param Tokens $tokens
* @param int $start
Expand Down Expand Up @@ -327,4 +391,60 @@ function ($type) use ($symbolShortNames) {

return $normalized;
}

/**
* @param string $docComment
*
* @return string
*/
private function removeSuperfluousInheritDoc($docComment)
{
return Preg::replace('~
# $1: before @inheritDoc tag
(
# beginning of comment or a PHPDoc tag
(?:
^/\*\*
(?:
\R
[ \t]*(?:\*[ \t]*)?
)*?
|
@\N+
)
# empty comment lines
(?:
\R
[ \t]*(?:\*[ \t]*?)?
)*
)
# spaces before @inheritDoc tag
[ \t]*
# @inheritDoc tag
(?:@inheritDocs?|\{@inheritDocs?\})
# $2: after @inheritDoc tag
(
# empty comment lines
(?:
\R
[ \t]*(?:\*[ \t]*)?
)*
# a PHPDoc tag or end of comment
(?:
@\N+
|
(?:
\R
[ \t]*(?:\*[ \t]*)?
)*
[ \t]*\*/$
)
)
~ix', '$1$2', $docComment);
}
}

0 comments on commit ab78266

Please sign in to comment.