mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-18 01:57:13 +00:00
Merge pull request #370 from phpDocumentor/retry-parser-docblocs
Fix docblock tag descriptions
This commit is contained in:
@@ -4,5 +4,9 @@ parameters:
|
|||||||
- '#Method phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory::createTag\(\) should return phpDocumentor\\Reflection\\DocBlock\\Tag but returns mixed#'
|
- '#Method phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory::createTag\(\) should return phpDocumentor\\Reflection\\DocBlock\\Tag but returns mixed#'
|
||||||
- "#Strict comparison using !== between array{'name', 'type'} and array{'name', 'type'} will always evaluate to false#"
|
- "#Strict comparison using !== between array{'name', 'type'} and array{'name', 'type'} will always evaluate to false#"
|
||||||
- '#Call to static method Webmozart\\Assert\\Assert::implementsInterface\(\) with class-string#'
|
- '#Call to static method Webmozart\\Assert\\Assert::implementsInterface\(\) with class-string#'
|
||||||
|
- '#Class PHPStan\\PhpDocParser\\Lexer\\Lexer does not have a constructor and must be instantiated without any parameters\.#'
|
||||||
|
- '#Class PHPStan\\PhpDocParser\\Parser\\ConstExprParser constructor invoked with 3 parameters, 0\-1 required\.#'
|
||||||
|
- '#Class PHPStan\\PhpDocParser\\Parser\\PhpDocParser constructor invoked with 6 parameters, 2\-3 required\.#'
|
||||||
|
- '#Class PHPStan\\PhpDocParser\\Parser\\TypeParser constructor invoked with 3 parameters\, 0\-1 required\.#'
|
||||||
paths:
|
paths:
|
||||||
- src
|
- src
|
||||||
|
|||||||
@@ -48,6 +48,12 @@
|
|||||||
</errorLevel>
|
</errorLevel>
|
||||||
</NoInterfaceProperties>
|
</NoInterfaceProperties>
|
||||||
|
|
||||||
|
<TooManyArguments>
|
||||||
|
<errorLevel type="info">
|
||||||
|
<file name="src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php"/>
|
||||||
|
</errorLevel>
|
||||||
|
</TooManyArguments>
|
||||||
|
|
||||||
<RedundantConditionGivenDocblockType>
|
<RedundantConditionGivenDocblockType>
|
||||||
<errorLevel type="info">
|
<errorLevel type="info">
|
||||||
<!-- Psalm manage to infer a more precise type than PHPStan. notNull assert is needed for PHPStan but
|
<!-- Psalm manage to infer a more precise type than PHPStan. notNull assert is needed for PHPStan but
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ use PHPStan\PhpDocParser\Parser\TokenIterator;
|
|||||||
use PHPStan\PhpDocParser\Parser\TypeParser;
|
use PHPStan\PhpDocParser\Parser\TypeParser;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
|
use function ltrim;
|
||||||
use function property_exists;
|
use function property_exists;
|
||||||
|
use function rtrim;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory class creating tags using phpstan's parser
|
* Factory class creating tags using phpstan's parser
|
||||||
@@ -42,18 +44,28 @@ class AbstractPHPStanFactory implements Factory
|
|||||||
|
|
||||||
public function __construct(PHPStanFactory ...$factories)
|
public function __construct(PHPStanFactory ...$factories)
|
||||||
{
|
{
|
||||||
$this->lexer = new Lexer();
|
$this->lexer = new Lexer(true);
|
||||||
$constParser = new ConstExprParser();
|
$constParser = new ConstExprParser(true, true, ['lines' => true, 'indexes' => true]);
|
||||||
$this->parser = new PhpDocParser(new TypeParser($constParser), $constParser);
|
$this->parser = new PhpDocParser(
|
||||||
|
new TypeParser($constParser, true, ['lines' => true, 'indexes' => true]),
|
||||||
|
$constParser,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
['lines' => true, 'indexes' => true],
|
||||||
|
true
|
||||||
|
);
|
||||||
$this->factories = $factories;
|
$this->factories = $factories;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(string $tagLine, ?TypeContext $context = null): Tag
|
public function create(string $tagLine, ?TypeContext $context = null): Tag
|
||||||
{
|
{
|
||||||
$tokens = new TokenIterator($this->lexer->tokenize($tagLine));
|
$tokens = $this->tokenizeLine($tagLine);
|
||||||
$ast = $this->parser->parseTag($tokens);
|
$ast = $this->parser->parseTag($tokens);
|
||||||
if (property_exists($ast->value, 'description') === true) {
|
if (property_exists($ast->value, 'description') === true) {
|
||||||
$ast->value->setAttribute('description', $ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END));
|
$ast->value->setAttribute(
|
||||||
|
'description',
|
||||||
|
$ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($context === null) {
|
if ($context === null) {
|
||||||
@@ -75,4 +87,36 @@ class AbstractPHPStanFactory implements Factory
|
|||||||
$ast->name
|
$ast->name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solve the issue with the lexer not tokenizing the line correctly
|
||||||
|
*
|
||||||
|
* This method is a workaround for the lexer that includes newline tokens with spaces. For
|
||||||
|
* phpstan this isn't an issue, as it doesn't do a lot of things with the indentation of descriptions.
|
||||||
|
* But for us is important to keep the indentation of the descriptions, so we need to fix the lexer output.
|
||||||
|
*/
|
||||||
|
private function tokenizeLine(string $tagLine): TokenIterator
|
||||||
|
{
|
||||||
|
$tokens = $this->lexer->tokenize($tagLine);
|
||||||
|
$fixed = [];
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
if (($token[1] === Lexer::TOKEN_PHPDOC_EOL) && rtrim($token[0], " \t") !== $token[0]) {
|
||||||
|
$fixed[] = [
|
||||||
|
rtrim($token[Lexer::VALUE_OFFSET], " \t"),
|
||||||
|
Lexer::TOKEN_PHPDOC_EOL,
|
||||||
|
$token[2] ?? null,
|
||||||
|
];
|
||||||
|
$fixed[] = [
|
||||||
|
ltrim($token[Lexer::VALUE_OFFSET], "\n\r"),
|
||||||
|
Lexer::TOKEN_HORIZONTAL_WS,
|
||||||
|
($token[2] ?? null) + 1,
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fixed[] = $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TokenIterator($fixed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ DOC;
|
|||||||
false,
|
false,
|
||||||
new Description(
|
new Description(
|
||||||
'{' . "\n" .
|
'{' . "\n" .
|
||||||
'Optional. Array or string of arguments for installing a package. Default empty array.' . "\n" .
|
' Optional. Array or string of arguments for installing a package. Default empty array.' . "\n" .
|
||||||
"\n" .
|
"\n" .
|
||||||
' @type string $source Required path to the package source. Default empty.' . "\n" .
|
' @type string $source Required path to the package source. Default empty.' . "\n" .
|
||||||
' @type string $destination Required path to a folder to install the package in.' . "\n" .
|
' @type string $destination Required path to a folder to install the package in.' . "\n" .
|
||||||
@@ -364,4 +364,71 @@ DOC;
|
|||||||
$docblock
|
$docblock
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIndentationIsKept(): void
|
||||||
|
{
|
||||||
|
$docComment = <<<DOC
|
||||||
|
/**
|
||||||
|
* Registers the script module if no script module with that script module
|
||||||
|
* identifier has already been registered.
|
||||||
|
*
|
||||||
|
* @since 6.5.0
|
||||||
|
*
|
||||||
|
* @param array \$deps {
|
||||||
|
* Optional. List of dependencies.
|
||||||
|
*
|
||||||
|
* @type string|array ...$0 {
|
||||||
|
* An array of script module identifiers of the dependencies of this script
|
||||||
|
* module. The dependencies can be strings or arrays. If they are arrays,
|
||||||
|
* they need an `id` key with the script module identifier, and can contain
|
||||||
|
* an `import` key with either `static` or `dynamic`. By default,
|
||||||
|
* dependencies that don't contain an `import` key are considered static.
|
||||||
|
*
|
||||||
|
* @type string \$id The script module identifier.
|
||||||
|
* @type string \$import Optional. Import type. May be either `static` or
|
||||||
|
* `dynamic`. Defaults to `static`.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
DOC;
|
||||||
|
|
||||||
|
$factory = DocBlockFactory::createInstance();
|
||||||
|
$docblock = $factory->create($docComment);
|
||||||
|
|
||||||
|
self::assertEquals(
|
||||||
|
new DocBlock(
|
||||||
|
'Registers the script module if no script module with that script module
|
||||||
|
identifier has already been registered.',
|
||||||
|
new Description(
|
||||||
|
''
|
||||||
|
),
|
||||||
|
[
|
||||||
|
new Since('6.5.0', new Description('')),
|
||||||
|
new Param(
|
||||||
|
'deps',
|
||||||
|
new Array_(new Mixed_()),
|
||||||
|
false,
|
||||||
|
new Description("{
|
||||||
|
Optional. List of dependencies.
|
||||||
|
|
||||||
|
@type string|array ...$0 {
|
||||||
|
An array of script module identifiers of the dependencies of this script
|
||||||
|
module. The dependencies can be strings or arrays. If they are arrays,
|
||||||
|
they need an `id` key with the script module identifier, and can contain
|
||||||
|
an `import` key with either `static` or `dynamic`. By default,
|
||||||
|
dependencies that don't contain an `import` key are considered static.
|
||||||
|
|
||||||
|
@type string \$id The script module identifier.
|
||||||
|
@type string \$import Optional. Import type. May be either `static` or
|
||||||
|
`dynamic`. Defaults to `static`.
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
new Context('\\')
|
||||||
|
),
|
||||||
|
$docblock
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user