Bugfix: resolve issue with multiline descriptions

The phpstan parser is not consuming the full description when parsing
docblocks with a more complex description. For them it's mostlikely not an
issue as phpstan doesn't use the descriptions. But it will also parse
the descriptions into unexpected tags. This could be an advantage but is
not according to the phpdoc spec.

Our own tokenizer is already tokenizing the docblocks into the correct parts.
So all we needed to do is assume all remaining tokens in the phpstan ast belong
to the description. From there our own code is able to handle this as before in
v5.3.

fixes #365
This commit is contained in:
Jaapio
2024-05-08 20:49:40 +02:00
parent 2f7b34c2ee
commit 518be131c0
10 changed files with 157 additions and 11 deletions
@@ -23,6 +23,8 @@ use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use RuntimeException;
use function property_exists;
/**
* Factory class creating tags using phpstan's parser
*
@@ -48,8 +50,11 @@ class AbstractPHPStanFactory implements Factory
public function create(string $tagLine, ?TypeContext $context = null): Tag
{
$tokens = $this->lexer->tokenize($tagLine);
$ast = $this->parser->parseTag(new TokenIterator($tokens));
$tokens = new TokenIterator($this->lexer->tokenize($tagLine));
$ast = $this->parser->parseTag($tokens);
if (property_exists($ast->value, 'description') === true) {
$ast->value->setAttribute('description', $ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END));
}
if ($context === null) {
$context = new TypeContext('');
+7 -1
View File
@@ -19,6 +19,7 @@ use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
use Webmozart\Assert\Assert;
use function is_string;
use function sprintf;
use function trim;
@@ -68,11 +69,16 @@ final class ParamFactory implements PHPStanFactory
);
}
$description = $tagValue->getAttribute('description');
if (is_string($description) === false) {
$description = $tagValue->description;
}
return new Param(
trim($tagValue->parameterName, '$'),
$this->typeResolver->createType($tagValue->type ?? new IdentifierTypeNode('mixed'), $context),
$tagValue->isVariadic,
$this->descriptionFactory->create($tagValue->description, $context),
$this->descriptionFactory->create($description, $context),
$tagValue->isReference
);
}
@@ -13,6 +13,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use Webmozart\Assert\Assert;
use function is_string;
use function trim;
/**
@@ -34,10 +35,15 @@ final class PropertyFactory implements PHPStanFactory
$tagValue = $node->value;
Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
$description = $tagValue->getAttribute('description');
if (is_string($description) === false) {
$description = $tagValue->description;
}
return new Property(
trim($tagValue->propertyName, '$'),
$this->typeResolver->createType($tagValue->type, $context),
$this->descriptionFactory->create($tagValue->description, $context)
$this->descriptionFactory->create($description, $context)
);
}
@@ -13,6 +13,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use Webmozart\Assert\Assert;
use function is_string;
use function trim;
/**
@@ -34,10 +35,15 @@ final class PropertyReadFactory implements PHPStanFactory
$tagValue = $node->value;
Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
$description = $tagValue->getAttribute('description');
if (is_string($description) === false) {
$description = $tagValue->description;
}
return new PropertyRead(
trim($tagValue->propertyName, '$'),
$this->typeResolver->createType($tagValue->type, $context),
$this->descriptionFactory->create($tagValue->description, $context)
$this->descriptionFactory->create($description, $context)
);
}
@@ -13,6 +13,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use Webmozart\Assert\Assert;
use function is_string;
use function trim;
/**
@@ -34,10 +35,15 @@ final class PropertyWriteFactory implements PHPStanFactory
$tagValue = $node->value;
Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
$description = $tagValue->getAttribute('description');
if (is_string($description) === false) {
$description = $tagValue->description;
}
return new PropertyWrite(
trim($tagValue->propertyName, '$'),
$this->typeResolver->createType($tagValue->type, $context),
$this->descriptionFactory->create($tagValue->description, $context)
$this->descriptionFactory->create($description, $context)
);
}
+8 -1
View File
@@ -13,6 +13,8 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use Webmozart\Assert\Assert;
use function is_string;
/**
* @internal This class is not part of the BC promise of this library.
*/
@@ -32,9 +34,14 @@ final class ReturnFactory implements PHPStanFactory
$tagValue = $node->value;
Assert::isInstanceOf($tagValue, ReturnTagValueNode::class);
$description = $tagValue->getAttribute('description');
if (is_string($description) === false) {
$description = $tagValue->description;
}
return new Return_(
$this->typeResolver->createType($tagValue->type, $context),
$this->descriptionFactory->create($tagValue->description, $context)
$this->descriptionFactory->create($description, $context)
);
}
+7 -1
View File
@@ -13,6 +13,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use Webmozart\Assert\Assert;
use function is_string;
use function trim;
/**
@@ -34,10 +35,15 @@ final class VarFactory implements PHPStanFactory
$tagValue = $node->value;
Assert::isInstanceOf($tagValue, VarTagValueNode::class);
$description = $tagValue->getAttribute('description');
if (is_string($description) === false) {
$description = $tagValue->description;
}
return new Var_(
trim($tagValue->variableName, '$'),
$this->typeResolver->createType($tagValue->type, $context),
$this->descriptionFactory->create($tagValue->description, $context)
$this->descriptionFactory->create($description, $context)
);
}