[BetterPhpDocParser] Handle parseString() got ShouldNotHappenException (#5299)

* Fixes #5267

* cs fix

* use true for 3rd parameter is_a

* try use method_exists

* patch in BetterPhpDocParser

* clean

* clean

* [ci-review] Rector Rectify

* [ci-review] Rector Rectify

* phpstan

* phpstan

* phpstan

* add test case

* better solution

* phpstan

* phpstan

* clean up

* clean up

* phpstan

* clean up

* final touch: use ObjectType check instead

* better approach

* ensure $phpDocInfo->getVarTagValueNode() instanceof VarTagValueNode as well

* clean up

* use of the VarTagValueNode

* use of the VarTagValueNode

* use of the VarTagValueNode

* [ci-review] Rector Rectify

* phpstan

Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
Abdul Malik Ikhsan 2021-01-24 23:46:06 +07:00 committed by GitHub
parent 13380b5b16
commit 31f357d6c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 36 deletions

View File

@ -76,11 +76,6 @@ final class BetterPhpDocParser extends PhpDocParser
*/
private $classAnnotationMatcher;
/**
* @var Lexer
*/
private $lexer;
/**
* @var AnnotationContentResolver
*/
@ -101,7 +96,6 @@ final class BetterPhpDocParser extends PhpDocParser
MultilineSpaceFormatPreserver $multilineSpaceFormatPreserver,
CurrentNodeProvider $currentNodeProvider,
ClassAnnotationMatcher $classAnnotationMatcher,
Lexer $lexer,
AnnotationContentResolver $annotationContentResolver,
PHPUnitDataProviderDocNodeFactory $phpUnitDataProviderDocNodeFactory,
array $phpDocNodeFactories = []
@ -114,21 +108,12 @@ final class BetterPhpDocParser extends PhpDocParser
$this->multilineSpaceFormatPreserver = $multilineSpaceFormatPreserver;
$this->currentNodeProvider = $currentNodeProvider;
$this->classAnnotationMatcher = $classAnnotationMatcher;
$this->lexer = $lexer;
$this->annotationContentResolver = $annotationContentResolver;
$this->phpUnitDataProviderDocNodeFactory = $phpUnitDataProviderDocNodeFactory;
$this->setPhpDocNodeFactories($phpDocNodeFactories);
}
public function parseString(string $docBlock): PhpDocNode
{
$tokens = $this->lexer->tokenize($docBlock);
$tokenIterator = new TokenIterator($tokens);
return parent::parse($tokenIterator);
}
/**
* @return AttributeAwarePhpDocNode|PhpDocNode
*/

View File

@ -10,11 +10,12 @@ use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\Trait_;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use Rector\BetterPhpDocParser\PhpDocParser\BetterPhpDocParser;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeCollector\NodeCollector\ParsedNodeCollector;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
@ -22,7 +23,6 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\Collector\TraitNodeScopeCollector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use ReflectionProperty;
/**
* @see \Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\NameTypeResolver\NameTypeResolverTest
@ -44,11 +44,6 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
*/
private $nodeNameResolver;
/**
* @var BetterPhpDocParser
*/
private $betterPhpDocParser;
/**
* @var StaticTypeMapper
*/
@ -60,7 +55,6 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
private $traitNodeScopeCollector;
public function __construct(
BetterPhpDocParser $betterPhpDocParser,
NodeNameResolver $nodeNameResolver,
ParsedNodeCollector $parsedNodeCollector,
StaticTypeMapper $staticTypeMapper,
@ -68,7 +62,6 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
) {
$this->parsedNodeCollector = $parsedNodeCollector;
$this->nodeNameResolver = $nodeNameResolver;
$this->betterPhpDocParser = $betterPhpDocParser;
$this->staticTypeMapper = $staticTypeMapper;
$this->traitNodeScopeCollector = $traitNodeScopeCollector;
}
@ -150,20 +143,17 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
return new MixedType();
}
// property is used
$reflectionProperty = new ReflectionProperty($varObjectType->getClassName(), $propertyName);
if (! $reflectionProperty->getDocComment()) {
$phpDocInfo = $propertyFetch->getAttribute(AttributeKey::PHP_DOC_INFO);
if (! $phpDocInfo instanceof PhpDocInfo) {
return $varObjectType;
}
$tagValueNode = $phpDocInfo->getVarTagValueNode();
if (! $tagValueNode instanceof VarTagValueNode) {
return new MixedType();
}
$phpDocNode = $this->betterPhpDocParser->parseString((string) $reflectionProperty->getDocComment());
$varTagValues = $phpDocNode->getVarTagValues();
if (! isset($varTagValues[0])) {
return new MixedType();
}
$typeNode = $varTagValues[0]->type;
$typeNode = $tagValueNode->type;
if (! $typeNode instanceof TypeNode) {
return new MixedType();
}

View File

@ -8,6 +8,7 @@ use Nette\Utils\Json;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Scalar\String_;
use Rector\CodingStyle\NodeAnalyzer\ImplodeAnalyzer;
@ -70,7 +71,7 @@ final class JsonArrayFactory
): ?Expr {
if ($node instanceof Array_ && count($node->items) === 1) {
$onlyItem = $node->items[0];
if ($onlyItem === null) {
if (! $onlyItem instanceof ArrayItem) {
throw new ShouldNotHappenException();
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Tests\Issues\Issue5267\DoNotThrowShouldNotHappenException;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class DoNotThrowShouldNotHappenExceptionTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
// bin/rector process ... --config config/some_config.php
protected function provideConfigFileInfo(): SmartFileInfo
{
return new SmartFileInfo(__DIR__ . '/config/some_config.php');
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Tests\Issues\Issue5267\DoNotThrowShouldNotHappenException\Fixture;
use Rector\Core\Tests\Issues\Issue5267\DoNotThrowShouldNotHappenException\Source\Xyz;
class SkipThrowShouldNotHappenException
{
public function fails(Xyz $xyz): void
{
$xyz->abc->get('xxx');
}
}
?>

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Tests\Issues\Issue5267\DoNotThrowShouldNotHappenException\Source;
class Xyz
{
/**
* @var Xyz
*/
public $abc;
public function get(string $s): string
{
return $s;
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, [SetList::DEAD_CODE]);
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
};