Skip to content

Commit

Permalink
Don't parse invalid doccomments
Browse files Browse the repository at this point in the history
  • Loading branch information
kukulich committed Jun 25, 2023
1 parent e76dadc commit ea34bea
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 64 deletions.
16 changes: 9 additions & 7 deletions SlevomatCodingStandard/Helpers/AnnotationHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,15 @@ static function () use ($phpcsFile, $docCommentOpenPointer, $name): array {
} else {
$parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer);

foreach ($parsedDocComment->getNode()->getTags() as $node) {
$annotationStartPointer = self::getStartPointer($phpcsFile, $parsedDocComment->getOpenPointer(), $node);
$annotations[] = new Annotation(
$node,
$annotationStartPointer,
self::getEndPointer($phpcsFile, $parsedDocComment, $annotationStartPointer, $node)
);
if ($parsedDocComment !== null) {
foreach ($parsedDocComment->getNode()->getTags() as $node) {
$annotationStartPointer = self::getStartPointer($phpcsFile, $parsedDocComment->getOpenPointer(), $node);
$annotations[] = new Annotation(
$node,
$annotationStartPointer,
self::getEndPointer($phpcsFile, $parsedDocComment, $annotationStartPointer, $node)
);
}
}
}

Expand Down
33 changes: 23 additions & 10 deletions SlevomatCodingStandard/Helpers/DocCommentHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PHP_CodeSniffer\Files\File;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
use PHPStan\PhpDocParser\Parser\ParserException;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use function array_merge;
use function count;
Expand Down Expand Up @@ -124,6 +125,10 @@ public static function hasInheritdocAnnotation(File $phpcsFile, int $pointer): b

$parsedDocComment = self::parseDocComment($phpcsFile, $docCommentOpenPointer);

if ($parsedDocComment === null) {
return false;
}

foreach ($parsedDocComment->getNode()->children as $child) {
if ($child instanceof PhpDocTextNode && strtolower($child->text) === '{@inheritdoc}') {
return true;
Expand Down Expand Up @@ -227,6 +232,10 @@ public static function isInline(File $phpcsFile, int $docCommentOpenPointer): bo

$parsedDocComment = self::parseDocComment($phpcsFile, $docCommentOpenPointer);

if ($parsedDocComment === null) {
return false;
}

foreach ($parsedDocComment->getNode()->getTags() as $annotation) {
if (preg_match('~^@(?:(?:phpstan|psalm)-)?var~i', $annotation->name) === 1) {
return true;
Expand All @@ -236,24 +245,28 @@ public static function isInline(File $phpcsFile, int $docCommentOpenPointer): bo
return false;
}

public static function parseDocComment(File $phpcsFile, int $docCommentOpenPointer): ParsedDocComment
public static function parseDocComment(File $phpcsFile, int $docCommentOpenPointer): ?ParsedDocComment
{
return SniffLocalCache::getAndSetIfNotCached(
$phpcsFile,
sprintf('parsed-doc-comment-%d', $docCommentOpenPointer),
static function () use ($phpcsFile, $docCommentOpenPointer): ParsedDocComment {
static function () use ($phpcsFile, $docCommentOpenPointer): ?ParsedDocComment {
$docComment = self::getDocComment($phpcsFile, $docCommentOpenPointer);

$docCommentTokens = new TokenIterator(PhpDocParserHelper::getLexer()->tokenize($docComment));

$parsedDocComment = PhpDocParserHelper::getParser()->parse($docCommentTokens);

return new ParsedDocComment(
$docCommentOpenPointer,
$phpcsFile->getTokens()[$docCommentOpenPointer]['comment_closer'],
$parsedDocComment,
$docCommentTokens
);
try {
$parsedDocComment = PhpDocParserHelper::getParser()->parse($docCommentTokens);

return new ParsedDocComment(
$docCommentOpenPointer,
$phpcsFile->getTokens()[$docCommentOpenPointer]['comment_closer'],
$parsedDocComment,
$docCommentTokens
);
} catch (ParserException $e) {
return null;
}
}
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,58 +708,61 @@ private function getReferences(File $phpcsFile, int $openTagPointer): array
}

$parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer);
$annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer);

foreach ($annotations as $annotation) {
/** @var list<IdentifierTypeNode> $identifierTypeNodes */
$identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class);
if ($parsedDocComment !== null) {
$annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer);

foreach ($identifierTypeNodes as $typeHintNode) {
$typeHint = $typeHintNode->name;
foreach ($annotations as $annotation) {
/** @var list<IdentifierTypeNode> $identifierTypeNodes */
$identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class);

$lowercasedTypeHint = strtolower($typeHint);
if (
TypeHintHelper::isSimpleTypeHint($lowercasedTypeHint)
|| TypeHintHelper::isSimpleUnofficialTypeHints($lowercasedTypeHint)
|| !TypeHelper::isTypeName($typeHint)
) {
continue;
}
foreach ($identifierTypeNodes as $typeHintNode) {
$typeHint = $typeHintNode->name;

$reference = new stdClass();
$reference->source = self::SOURCE_ANNOTATION;
$reference->parsedDocComment = $parsedDocComment;
$reference->annotation = $annotation;
$reference->nameNode = $typeHintNode;
$reference->name = $typeHint;
$reference->type = ReferencedName::TYPE_CLASS;
$reference->startPointer = $annotation->getStartPointer();
$reference->endPointer = null;
$reference->isClass = true;
$reference->isConstant = false;
$reference->isFunction = false;

$references[] = $reference;
}
$lowercasedTypeHint = strtolower($typeHint);
if (
TypeHintHelper::isSimpleTypeHint($lowercasedTypeHint)
|| TypeHintHelper::isSimpleUnofficialTypeHints($lowercasedTypeHint)
|| !TypeHelper::isTypeName($typeHint)
) {
continue;
}

$reference = new stdClass();
$reference->source = self::SOURCE_ANNOTATION;
$reference->parsedDocComment = $parsedDocComment;
$reference->annotation = $annotation;
$reference->nameNode = $typeHintNode;
$reference->name = $typeHint;
$reference->type = ReferencedName::TYPE_CLASS;
$reference->startPointer = $annotation->getStartPointer();
$reference->endPointer = null;
$reference->isClass = true;
$reference->isConstant = false;
$reference->isFunction = false;

$references[] = $reference;
}

/** @var list<ConstFetchNode> $constantFetchNodes */
$constantFetchNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), ConstFetchNode::class);

foreach ($constantFetchNodes as $constantFetchNode) {
$reference = new stdClass();
$reference->source = self::SOURCE_ANNOTATION_CONSTANT_FETCH;
$reference->parsedDocComment = $parsedDocComment;
$reference->annotation = $annotation;
$reference->constantFetchNode = $constantFetchNode;
$reference->name = $constantFetchNode->className;
$reference->type = ReferencedName::TYPE_CLASS;
$reference->startPointer = $annotation->getStartPointer();
$reference->endPointer = null;
$reference->isClass = true;
$reference->isConstant = false;
$reference->isFunction = false;

$references[] = $reference;
/** @var list<ConstFetchNode> $constantFetchNodes */
$constantFetchNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), ConstFetchNode::class);

foreach ($constantFetchNodes as $constantFetchNode) {
$reference = new stdClass();
$reference->source = self::SOURCE_ANNOTATION_CONSTANT_FETCH;
$reference->parsedDocComment = $parsedDocComment;
$reference->annotation = $annotation;
$reference->constantFetchNode = $constantFetchNode;
$reference->name = $constantFetchNode->className;
$reference->type = ReferencedName::TYPE_CLASS;
$reference->startPointer = $annotation->getStartPointer();
$reference->endPointer = null;
$reference->isClass = true;
$reference->isConstant = false;
$reference->isFunction = false;

$references[] = $reference;
}
}
}

Expand Down
20 changes: 20 additions & 0 deletions tests/Helpers/DocCommentHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,26 @@ public function testIsInline(): void
}
}

public function testIsInlineWithInvalidDocComment(): void
{
self::assertFalse(
DocCommentHelper::isInline(
$this->getTestedCodeSnifferFile(),
$this->findPointerByLineAndType($this->getTestedCodeSnifferFile(), 135, T_DOC_COMMENT_OPEN_TAG)
)
);
}

public function testHasInheritdocAnnotationWithInvalidDocComment(): void
{
self::assertFalse(
DocCommentHelper::hasInheritdocAnnotation(
$this->getTestedCodeSnifferFile(),
$this->findPointerByLineAndType($this->getTestedCodeSnifferFile(), 129, T_DOC_COMMENT_OPEN_TAG)
)
);
}

private function getTestedCodeSnifferFile(): File
{
if ($this->testedCodeSnifferFile === null) {
Expand Down
12 changes: 12 additions & 0 deletions tests/Helpers/data/docComment.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,15 @@ class ClassWithAttribute
class ClassWithAttributes
{
}

/**** Invalid doccomment *****/
class WithInvalidDocComment
{

public function __construct()
{
/**** Invalid doccomment *****/
$var = 'var';
}

}

0 comments on commit ea34bea

Please sign in to comment.