Newlines and whitespaces were not handled as before. This caused
issues for some users because our indent-recuction was broken.
The cause seems to be an upstream issue in phpstan parser which
is not resolved yet. But this work around post processing the tokens
helps us to make it work as before.
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
Throw on invalid type definitions and unexpected type definitions.
Not all types resolved by phpstan's parser are valid for docblocks,
they might in a more complex type system but I do not see how these
types would ever apply to param tags.
The original php method preg_split might return `string[][]` in some
situations, however this should never be the case for this library. By
adding an extra assert, we should be more safe.
Psalm flags this condition as redundant:
```
ERROR: RedundantCondition - src/DocBlock/Tags/Return_.php:62:48 - "" can never contain non-empty-lowercase-string (see https://psalm.dev/122)
return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : '');
```
Based on the statement in the line above - `$type = $this->type ? '' . $this->type : 'mixed';` -, Psalm is correct and the `$type` variable can never be an empty string.
Psalm flags these type casts as redundant:
```
ERROR: RedundantCastGivenDocblockType - src/DocBlock/Tags/Author.php:80:23 - Redundant cast to string given docblock-provided type (see https://psalm.dev/263)
$authorName = (string) $this->authorName;
ERROR: RedundantCastGivenDocblockType - src/DocBlock/Tags/Example.php:150:21 - Redundant cast to string given docblock-provided type (see https://psalm.dev/263)
$filePath = (string) $this->filePath;
ERROR: RedundantCastGivenDocblockType - src/DocBlock/Tags/Link.php:74:17 - Redundant cast to string given docblock-provided type (see https://psalm.dev/263)
$link = (string) $this->link;
ERROR: RedundantCastGivenDocblockType - src/DocBlock/Tags/Method.php:228:23 - Redundant cast to string given docblock-provided type (see https://psalm.dev/263)
$methodName = (string) $this->methodName;
```
I have verified each and can confirm that these are redundant. They are probably a left-over from the time when the `__construct()` method in these classes did not yet have type declarations.
Correctly flagged by Psalm:
```
ERROR: PossiblyNullArgument - src\Utils.php:50:53 - Argument 3 of preg_split cannot be null, possibly null value provided (see https://psalm.dev/078)
$parts = php_preg_split($pattern, $subject, $limit, $flags);
```
The `$limit` argument of the PHP native `preg_split()` function is not nullable.
Ref: https://www.php.net/manual/en/function.preg-split
PHPStan flags the code within the `Source::__toString()` method:
```
------ -------------------------------------------------------------------------------------------
Line DocBlock\Tags\Source.php
------ -------------------------------------------------------------------------------------------
111 Result of || is always true.
114 Result of || is always true.
114 Result of || is always true.
------ -------------------------------------------------------------------------------------------
```
I have investigated this and can confirm that these flags are correct.
1. `$this->startingLine` is cast to an integer in the `__construct()` method (line 45) and subsequently cast to a string in `__toString()` (line 105).
This means that it can only ever be a non-empty ("truthy") string or the string '0', so the `$startingLine || $startingLine === '0'` condition used in two places is redundant.
2. `$this->lineCount` is either an integer or `null` after the `__construct()` method (line 46).
In the `__toString()` method, if the `lineCount` is an integer, it is effectively cast to a string by the concatenation with an empty string on line 107, while if the `lineCount` was `null`, it is turned into an empty string.
By changing the concatenation from concatenating with an empty string to concatenating with a one-space string, we can remove the ternary in the `return` statement checking for `$lineCount` being empty.
The existing unit tests already cover this code and still pass after this change.