[CodingStyle] Drop AnnotateThrowablesRector to narrow code scope, designed for custom community rule (#5528)

This commit is contained in:
Tomas Votruba 2021-02-13 15:19:19 +01:00 committed by GitHub
parent d0fb658dad
commit 6ce792c70a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 0 additions and 1575 deletions

View File

@ -195,7 +195,6 @@
"rules/coding-style/tests/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/Foo.php",
"rules/coding-style/tests/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/Function_/count.php",
"rules/coding-style/tests/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/YetAnotherClass.php",
"rules/coding-style/tests/Rector/Throw_/AnnotateThrowablesRector/Source/i_throw_an_exception.func.php",
"rules/dead-code/tests/Rector/MethodCall/RemoveDefaultArgumentValueRector/Source/UserDefined.php",
"rules/naming/tests/ValueObjectFactory/PropertyRenameFactory/Fixture/skip_some_class.php.inc",
"rules/renaming/tests/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Source/ChangeMeAnotherNamespace.php",

View File

@ -1,73 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocManipulator;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use Symplify\SimplePhpDocParser\SimplePhpDocParser;
/**
* @see \Rector\BetterPhpDocParser\Tests\PhpDocManipulator\PhpDocTagsFinderTest
*/
final class PhpDocTagsFinder
{
/**
* @var SimplePhpDocParser
*/
private $simplePhpDocParser;
public function __construct(SimplePhpDocParser $simplePhpDocParser)
{
$this->simplePhpDocParser = $simplePhpDocParser;
}
/**
* @return string[]
*/
public function extractTrowsTypesFromDocBlock(string $docBlock): array
{
$simplePhpDocNode = $this->simplePhpDocParser->parseDocBlock($docBlock);
return $this->resolveTypes($simplePhpDocNode->getThrowsTagValues());
}
/**
* @param ThrowsTagValueNode[]|ReturnTagValueNode[] $tagValueNodes
* @return string[]
*/
private function resolveTypes(array $tagValueNodes): array
{
$types = [];
foreach ($tagValueNodes as $tagValueNode) {
$typeNode = $tagValueNode->type;
if ($typeNode instanceof IdentifierTypeNode) {
$types[] = $typeNode->name;
}
if ($typeNode instanceof UnionTypeNode) {
$types = array_merge($types, $this->resolveUnionType($typeNode));
}
}
return $types;
}
/**
* @return string[]
*/
private function resolveUnionType(UnionTypeNode $unionTypeNode): array
{
$types = [];
foreach ($unionTypeNode->types as $unionedType) {
if ($unionedType instanceof IdentifierTypeNode) {
$types[] = $unionedType->name;
}
}
return $types;
}
}

View File

@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\Tests\PhpDocManipulator;
use PhpParser\Node\Stmt\Nop;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagsFinder;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\HttpKernel\RectorKernel;
use Symplify\PackageBuilder\Testing\AbstractKernelTestCase;
use Symplify\SmartFileSystem\SmartFileSystem;
final class PhpDocTagsFinderTest extends AbstractKernelTestCase
{
/**
* @var PhpDocTagsFinder
*/
private $phpDocTagsFinder;
/**
* @var SmartFileSystem
*/
private $smartFileSystem;
/**
* @var CurrentNodeProvider
*/
private $currentNodeProvider;
protected function setUp(): void
{
$this->bootKernel(RectorKernel::class);
$this->phpDocTagsFinder = $this->getService(PhpDocTagsFinder::class);
$this->smartFileSystem = $this->getService(SmartFileSystem::class);
// required for parser
$this->currentNodeProvider = $this->getService(CurrentNodeProvider::class);
$this->currentNodeProvider->setNode(new Nop());
}
public function test(): void
{
$docContent = $this->smartFileSystem->readFile(__DIR__ . '/Source/doc_block_throws.txt');
$throwsTags = $this->phpDocTagsFinder->extractTrowsTypesFromDocBlock($docContent);
$this->assertCount(3, $throwsTags);
$this->assertSame(['A', 'B', 'C'], $throwsTags);
}
}

View File

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\DocBlock;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocTagNode;
final class ThrowsFactory
{
public function crateDocTagNodeFromClass(string $throwableClass): AttributeAwarePhpDocTagNode
{
$throwsTagValueNode = new ThrowsTagValueNode(new IdentifierTypeNode($throwableClass), '');
return new AttributeAwarePhpDocTagNode('@throws', $throwsTagValueNode);
}
}

View File

@ -1,63 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\NodeAnalyzer;
use PhpParser\Node\Stmt\Throw_;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
final class ThrowAnalyzer
{
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
public function __construct(NodeTypeResolver $nodeTypeResolver)
{
$this->nodeTypeResolver = $nodeTypeResolver;
}
/**
* @return string[]
*/
public function resolveThrownTypes(Throw_ $throw): array
{
$thrownType = $this->nodeTypeResolver->getStaticType($throw->expr);
if ($thrownType instanceof MixedType) {
return [];
}
if ($thrownType instanceof UnionType) {
$types = [];
foreach ($thrownType->getTypes() as $unionedType) {
$types[] = $this->resolveClassFromType($unionedType);
}
return $types;
}
$class = $this->resolveClassFromType($thrownType);
return [$class];
}
private function resolveClassFromType(Type $thrownType): string
{
if ($thrownType instanceof ShortenedObjectType) {
return $thrownType->getFullyQualifiedName();
}
if ($thrownType instanceof TypeWithClassName) {
return $thrownType->getClassName();
}
throw new ShouldNotHappenException();
}
}

View File

@ -1,293 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Rector\Throw_;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Throw_;
use Rector\CodingStyle\DocBlock\ThrowsFactory;
use Rector\CodingStyle\NodeAnalyzer\ThrowAnalyzer;
use Rector\Core\PhpParser\Node\Value\ClassResolver;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Reflection\ClassMethodReflectionHelper;
use Rector\Core\Reflection\FunctionAnnotationResolver;
use ReflectionFunction;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\AnnotateThrowablesRectorTest
*/
final class AnnotateThrowablesRector extends AbstractRector
{
/**
* @var string[]
*/
private $throwablesToAnnotate = [];
/**
* @var FunctionAnnotationResolver
*/
private $functionAnnotationResolver;
/**
* @var ClassMethodReflectionHelper
*/
private $classMethodReflectionHelper;
/**
* @var ClassResolver
*/
private $classResolver;
/**
* @var ThrowAnalyzer
*/
private $throwAnalyzer;
/**
* @var ThrowsFactory
*/
private $throwsFactory;
public function __construct(
ClassMethodReflectionHelper $classMethodReflectionHelper,
ClassResolver $classResolver,
FunctionAnnotationResolver $functionAnnotationResolver,
ThrowAnalyzer $throwAnalyzer,
ThrowsFactory $throwsFactory
) {
$this->functionAnnotationResolver = $functionAnnotationResolver;
$this->classMethodReflectionHelper = $classMethodReflectionHelper;
$this->classResolver = $classResolver;
$this->throwAnalyzer = $throwAnalyzer;
$this->throwsFactory = $throwsFactory;
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Throw_::class, FuncCall::class, MethodCall::class];
}
/**
* From this method documentation is generated.
*/
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Adds @throws DocBlock comments to methods that thrwo \Throwables.', [
new CodeSample(
// code before
<<<'CODE_SAMPLE'
class RootExceptionInMethodWithDocblock
{
/**
* This is a comment.
*
* @param int $code
*/
public function throwException(int $code)
{
throw new \RuntimeException('', $code);
}
}
CODE_SAMPLE
,
// code after
<<<'CODE_SAMPLE'
class RootExceptionInMethodWithDocblock
{
/**
* This is a comment.
*
* @param int $code
* @throws \RuntimeException
*/
public function throwException(int $code)
{
throw new \RuntimeException('', $code);
}
}
CODE_SAMPLE
),
]
);
}
/**
* @param Throw_|MethodCall|FuncCall $node
*/
public function refactor(Node $node): ?Node
{
$this->throwablesToAnnotate = [];
if ($this->hasThrowablesToAnnotate($node)) {
$this->annotateThrowables($node);
return $node;
}
return null;
}
/**
* @param Throw_|MethodCall|FuncCall $node
*/
private function hasThrowablesToAnnotate(Node $node): bool
{
$foundThrowables = 0;
if ($node instanceof Throw_) {
$foundThrowables = $this->analyzeStmtThrow($node);
}
if ($node instanceof FuncCall) {
$foundThrowables = $this->analyzeStmtFuncCall($node);
}
if ($node instanceof MethodCall) {
$foundThrowables = $this->analyzeStmtMethodCall($node);
}
return $foundThrowables > 0;
}
/**
* @param Throw_|MethodCall|FuncCall $node
*/
private function annotateThrowables(Node $node): void
{
$callee = $this->identifyCaller($node);
if (! $callee instanceof Node) {
return;
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($callee);
foreach ($this->throwablesToAnnotate as $throwableToAnnotate) {
$phpDocTagNode = $this->throwsFactory->crateDocTagNodeFromClass($throwableToAnnotate);
$phpDocInfo->addPhpDocTagNode($phpDocTagNode);
}
}
private function analyzeStmtThrow(Throw_ $throw): int
{
$foundThrownThrowables = $this->throwAnalyzer->resolveThrownTypes($throw);
$alreadyAnnotatedThrowables = $this->extractAlreadyAnnotatedThrowables($throw);
return $this->diffThrowsTypes($foundThrownThrowables, $alreadyAnnotatedThrowables);
}
private function analyzeStmtFuncCall(FuncCall $funcCall): int
{
$functionFqn = $this->getName($funcCall);
if ($functionFqn === null) {
return 0;
}
$reflectionFunction = new ReflectionFunction($functionFqn);
$throwsTypes = $this->functionAnnotationResolver->extractFunctionAnnotatedThrows($reflectionFunction);
$alreadyAnnotatedThrowsTypes = $this->extractAlreadyAnnotatedThrowables($funcCall);
return $this->diffThrowsTypes($throwsTypes, $alreadyAnnotatedThrowsTypes);
}
private function analyzeStmtMethodCall(MethodCall $methodCall): int
{
$foundThrowsTypes = $this->identifyThrownThrowablesInMethodCall($methodCall);
$alreadyAnnotatedThrowsTypes = $this->extractAlreadyAnnotatedThrowables($methodCall);
return $this->diffThrowsTypes($foundThrowsTypes, $alreadyAnnotatedThrowsTypes);
}
/**
* @param Throw_|MethodCall|FuncCall $node
*/
private function identifyCaller(Node $node): ?Node
{
return $this->betterNodeFinder->findFirstAncestorInstancesOf($node, [ClassMethod::class, Function_::class]);
}
/**
* @param Throw_|MethodCall|FuncCall $node
* @return string[]
*/
private function extractAlreadyAnnotatedThrowables(Node $node): array
{
$callee = $this->identifyCaller($node);
if (! $callee instanceof Node) {
return [];
}
$callePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($callee);
return $callePhpDocInfo->getThrowsClassNames();
}
/**
* @param string[] $foundThrownThrowables
* @param string[] $alreadyAnnotatedThrowables
*/
private function diffThrowsTypes(array $foundThrownThrowables, array $alreadyAnnotatedThrowables): int
{
$normalizeNamespace = static function (string $class): string {
$class = ltrim($class, '\\');
return '\\' . $class;
};
$foundThrownThrowables = array_map($normalizeNamespace, $foundThrownThrowables);
$alreadyAnnotatedThrowables = array_map($normalizeNamespace, $alreadyAnnotatedThrowables);
$filterClasses = static function (string $class): bool {
return class_exists($class) || interface_exists($class);
};
$foundThrownThrowables = array_filter($foundThrownThrowables, $filterClasses);
$alreadyAnnotatedThrowables = array_filter($alreadyAnnotatedThrowables, $filterClasses);
$this->throwablesToAnnotate = array_diff($foundThrownThrowables, $alreadyAnnotatedThrowables);
return count($this->throwablesToAnnotate);
}
/**
* @return class-string[]
*/
private function identifyThrownThrowablesInMethodCall(MethodCall $methodCall): array
{
$fullyQualified = $this->classResolver->getClassFromMethodCall($methodCall);
$methodName = $methodCall->name;
if (! $fullyQualified instanceof FullyQualified) {
return [];
}
if (! $methodName instanceof Identifier) {
return [];
}
return $this->extractMethodThrows($fullyQualified, $methodName);
}
/**
* @return class-string[]
*/
private function extractMethodThrows(FullyQualified $fullyQualified, Identifier $identifier): array
{
$method = $identifier->name;
$class = $this->getName($fullyQualified);
if ($class === null) {
return [];
}
return $this->classMethodReflectionHelper->extractTagsFromMethodDocBlock($class, $method);
}
}

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector;
use Iterator;
use Rector\CodingStyle\Rector\Throw_\AnnotateThrowablesRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class AnnotateThrowablesRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return AnnotateThrowablesRector::class;
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
/**
* This is a comment.
*
* @param int $code
*/
function throwCustomExceptionInFunctionWithDocblock(int $code)
{
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException('', $code);
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
/**
* This is a comment.
*
* @param int $code
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
function throwCustomExceptionInFunctionWithDocblock(int $code)
{
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException('', $code);
}
?>

View File

@ -1,24 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
function throwCustomExceptionInFunctionWithoutDocblock()
{
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException();
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
function throwCustomExceptionInFunctionWithoutDocblock()
{
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException();
}
?>

View File

@ -1,30 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
class CustomExceptionInMethodWithoutDocblock
{
public function throwException()
{
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException();
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
class CustomExceptionInMethodWithoutDocblock
{
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
public function throwException()
{
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException();
}
}
?>

View File

@ -1,33 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\ExceptionsFactoryMethodWithReturnDocblock;
function throwWithFactoryMethodWithReturnDocblock()
{
$exceptionFactory = new ExceptionsFactoryMethodWithReturnDocblock();
throw $exceptionFactory->createException(1);
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\ExceptionsFactoryMethodWithReturnDocblock;
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird
* @throws \RuntimeException
*/
function throwWithFactoryMethodWithReturnDocblock()
{
$exceptionFactory = new ExceptionsFactoryMethodWithReturnDocblock();
throw $exceptionFactory->createException(1);
}
?>

View File

@ -1,31 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\ExceptionsFactoryStaticMethodWithReturnDocblock;
function throwWithFactoryStaticMethodWithReturnDocblock()
{
throw ExceptionsFactoryStaticMethodWithReturnDocblock::createException(1);
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\ExceptionsFactoryStaticMethodWithReturnDocblock;
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird
* @throws \RuntimeException
*/
function throwWithFactoryStaticMethodWithReturnDocblock()
{
throw ExceptionsFactoryStaticMethodWithReturnDocblock::createException(1);
}
?>

View File

@ -1,32 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
/**
* This is a comment.
*
* @param int $code
*/
function throwRootExceptionInFunctionWithDocblock(int $code)
{
throw new \RuntimeException('', $code);
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
/**
* This is a comment.
*
* @param int $code
* @throws \RuntimeException
*/
function throwRootExceptionInFunctionWithDocblock(int $code)
{
throw new \RuntimeException('', $code);
}
?>

View File

@ -1,24 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
function throwRootExceptionInFunctionWithoutDocblock()
{
throw new \RuntimeException();
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
/**
* @throws \RuntimeException
*/
function throwRootExceptionInFunctionWithoutDocblock()
{
throw new \RuntimeException();
}
?>

View File

@ -1,38 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
class RootExceptionInMethodWithDocblock
{
/**
* This is a comment.
*
* @param int $code
*/
public function throwException(int $code)
{
throw new \RuntimeException('', $code);
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
class RootExceptionInMethodWithDocblock
{
/**
* This is a comment.
*
* @param int $code
* @throws \RuntimeException
*/
public function throwException(int $code)
{
throw new \RuntimeException('', $code);
}
}
?>

View File

@ -1,11 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
function throwCustomExceptionAlreadyAnnotatedInFunction()
{
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException();
}

View File

@ -1,13 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
/**
* @throws TheException
*/
function skipCustomImportedExceptionAlreadyAnnotatedInFunction()
{
throw new TheException();
}

View File

@ -1,16 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class SkipCustomImportedExceptionAlreadyAnnotatedInMethod
{
/**
* @throws TheException
*/
public function throwException()
{
throw new TheException();
}
}

View File

@ -1,11 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\ExceptionsFactoryMethodNothingAnnotated;
function throwWithFactoryMethodNotAnnotated()
{
$factory = new ExceptionsFactoryMethodNothingAnnotated();
throw $factory->isThis(1);
}

View File

@ -1,12 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\ExceptionsFactoryStaticMethodNothingAnnotated;
function throwWithFactoryStaticMethodNotAnnotated()
{
throw ExceptionsFactoryStaticMethodNothingAnnotated::createException(1);
}
?>

View File

@ -1,13 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use \RuntimeException;
/**
* @throws RuntimeException
*/
function throwRootImportedExceptionAlreadyAnnotatedInFunction()
{
throw new RuntimeException();
}

View File

@ -1,16 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use \RuntimeException;
class RootImportedExceptionAlreadyAnnotatedInMethod
{
/**
* @throws RuntimeException
*/
public function throwException()
{
throw new RuntimeException();
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use function Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\i_throw_an_exception;
class UseOfAFunctionThatThrowsAnException
{
public function iUseAFunctionThatMayThrowAnException()
{
return i_throw_an_exception();
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use function Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\i_throw_an_exception;
class UseOfAFunctionThatThrowsAnException
{
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird
*/
public function iUseAFunctionThatMayThrowAnException()
{
return i_throw_an_exception();
}
}
?>

View File

@ -1,42 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use function Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\i_throw_an_exception;
use \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class UseOfAFunctionThatThrowsAnExceptionAlreadyAnnotated
{
/**
* @throws TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
*/
public function iUseAFunctionThatMayThrowAnException()
{
return i_throw_an_exception();
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use function Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\i_throw_an_exception;
use \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class UseOfAFunctionThatThrowsAnExceptionAlreadyAnnotated
{
/**
* @throws TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird
*/
public function iUseAFunctionThatMayThrowAnException()
{
return i_throw_an_exception();
}
}
?>

View File

@ -1,60 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class A
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
}
class UseAClassWithAMethodThatMayThrowAnException
{
/**
* @param A $theClass
*/
public function thisMethodUsesTheClassWithTheMethodThatTrhowsAnException(A $theClass)
{
$aVariable = $theClass->thisMethodThrowsAnException();
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class A
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
}
class UseAClassWithAMethodThatMayThrowAnException
{
/**
* @param A $theClass
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
public function thisMethodUsesTheClassWithTheMethodThatTrhowsAnException(A $theClass)
{
$aVariable = $theClass->thisMethodThrowsAnException();
}
}
?>

View File

@ -1,41 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\MethodThatMayThrowAnException;
class UseOfAMethodThatMayThrowAnException
{
/**
* @return int
*/
public function iUseAMethodThatMayThrowAnException():int
{
$class = new MethodThatMayThrowAnException();
return $class->mayThrowAnException(1);
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\MethodThatMayThrowAnException;
class UseOfAMethodThatMayThrowAnException
{
/**
* @return int
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
*/
public function iUseAMethodThatMayThrowAnException():int
{
$class = new MethodThatMayThrowAnException();
return $class->mayThrowAnException(1);
}
}
?>

View File

@ -1,42 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\MethodThatMayThrowAnException;
class UseOfAMethodThatMayThrowAnExceptionAlreadyAnnotated
{
/**
* @return int
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
public function iUseAMethodThatMayThrowAnException():int
{
$class = new MethodThatMayThrowAnException();
return $class->mayThrowAnException(1);
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\MethodThatMayThrowAnException;
class UseOfAMethodThatMayThrowAnExceptionAlreadyAnnotated
{
/**
* @return int
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
*/
public function iUseAMethodThatMayThrowAnException():int
{
$class = new MethodThatMayThrowAnException();
return $class->mayThrowAnException(1);
}
}
?>

View File

@ -1,56 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class ThisA
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
}
class UseOfThisInherited extends ThisA
{
public function thisMethodUsesAThisThatThrowsAnException()
{
$this->thisMethodThrowsAnException();
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class ThisA
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
}
class UseOfThisInherited extends ThisA
{
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
public function thisMethodUsesAThisThatThrowsAnException()
{
$this->thisMethodThrowsAnException();
}
}
?>

View File

@ -1,50 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class UseOfThisSameClass
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
public function thisMethodUsesAThisThatThrowsAnException()
{
$this->thisMethodThrowsAnException();
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class UseOfThisSameClass
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
public function thisMethodUsesAThisThatThrowsAnException()
{
$this->thisMethodThrowsAnException();
}
}
?>

View File

@ -1,50 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class UseOfThisSameClassWithAssignment
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
public function thisMethodUsesAThisThatThrowsAnException()
{
$assign = $this->thisMethodThrowsAnException();
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Fixture;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
class UseOfThisSameClassWithAssignment
{
/**
* @throws TheException
*/
public function thisMethodThrowsAnException():string
{
throw new TheException('');
}
/**
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException
*/
public function thisMethodUsesAThisThatThrowsAnException()
{
$assign = $this->thisMethodThrowsAnException();
}
}
?>

View File

@ -1,8 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions;
class TheException extends \Exception {
}

View File

@ -1,7 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions;
class TheExceptionTheSecond extends \Exception {}

View File

@ -1,7 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions;
class TheExceptionTheThird extends \Exception {}

View File

@ -1,15 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird;
class ExceptionsFactoryMethodNothingAnnotated
{
public function isThis(int $code)
{
return new TheException();
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird;
class ExceptionsFactoryMethodWithReturnDocblock
{
/**
* This is the DocComment of createException().
*
* @param int $code
*
* @return TheException|TheExceptionTheSecond|TheExceptionTheThird|\RuntimeException
*/
public function createException(int $code)
{
switch ($code) {
case 1:
return new TheException();
case 2:
return new TheExceptionTheSecond();
case 3:
return new TheExceptionTheThird();
default:
return new \RuntimeException();
}
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird;
class ExceptionsFactoryStaticMethodNothingAnnotated
{
public static function createException(int $code)
{
switch ($code) {
case 1:
return new TheException();
case 2:
return new TheExceptionTheSecond();
case 3:
return new TheExceptionTheThird();
default:
return new \RuntimeException();
}
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird;
class ExceptionsFactoryStaticMethodWithReturnDocblock
{
/**
* This is the DocComment of createException().
*
* @param int $code
*
* @return TheException|TheExceptionTheSecond|TheExceptionTheThird|\RuntimeException
*/
public static function createException(int $code)
{
switch ($code) {
case 1:
return new TheException();
case 2:
return new TheExceptionTheSecond();
case 3:
return new TheExceptionTheThird();
default:
return new \RuntimeException();
}
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond;
class MethodThatMayThrowAnException
{
/**
* @param int $code
*
* @return int
* @throws TheException
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond
*/
public function mayThrowAnException(int $code):int
{
switch ($code) {
case 1:
throw new TheException('');
case 2:
throw new TheExceptionTheSecond('');
default:
return $code;
}
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheException;
use Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheSecond;
/**
* @param null|string $switch
* @throws TheException
* @throws TheExceptionTheSecond
* @throws \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird
*/
function i_throw_an_exception(?string $switch = null):bool
{
switch ($switch) {
case 'two':
throw new TheExceptionTheSecond("I'm a function that throws an exception.");
case 'third':
throw new \Rector\CodingStyle\Tests\Rector\Throw_\AnnotateThrowablesRector\Source\Exceptions\TheExceptionTheThird("I'm a function that throws an exception.");
default:
throw new TheException("I'm a function that throws an exception.");
}
}

View File

@ -1,58 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Reflection;
use Nette\Utils\Reflection;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagsFinder;
use ReflectionMethod;
final class ClassMethodReflectionHelper
{
/**
* @var ClassMethodReflectionFactory
*/
private $classMethodReflectionFactory;
/**
* @var PhpDocTagsFinder
*/
private $phpDocTagsFinder;
public function __construct(
ClassMethodReflectionFactory $classMethodReflectionFactory,
PhpDocTagsFinder $phpDocTagsFinder
) {
$this->classMethodReflectionFactory = $classMethodReflectionFactory;
$this->phpDocTagsFinder = $phpDocTagsFinder;
}
/**
* @return array<class-string>
*/
public function extractTagsFromMethodDocBlock(string $class, string $method): array
{
$reflectedMethod = $this->classMethodReflectionFactory->createReflectionMethodIfExists($class, $method);
if (! $reflectedMethod instanceof ReflectionMethod) {
return [];
}
$docComment = $reflectedMethod->getDocComment();
if (! is_string($docComment)) {
return [];
}
$throwsTypes = $this->phpDocTagsFinder->extractTrowsTypesFromDocBlock($docComment);
$classes = [];
foreach ($throwsTypes as $returnTag) {
/** @var class-string $className */
$className = Reflection::expandClassName($returnTag, $reflectedMethod->getDeclaringClass());
$classes[] = $className;
}
return $classes;
}
}

View File

@ -1,100 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Reflection;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagsFinder;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\Core\PhpParser\Parser\FunctionLikeParser;
use ReflectionFunction;
final class FunctionAnnotationResolver
{
/**
* @var ClassNaming
*/
private $classNaming;
/**
* @var FunctionLikeParser
*/
private $functionLikeParser;
/**
* @var PhpDocTagsFinder
*/
private $phpDocTagsFinder;
public function __construct(
ClassNaming $classNaming,
FunctionLikeParser $functionLikeParser,
PhpDocTagsFinder $phpDocTagsFinder
) {
$this->functionLikeParser = $functionLikeParser;
$this->classNaming = $classNaming;
$this->phpDocTagsFinder = $phpDocTagsFinder;
}
/**
* @return mixed[]
*/
public function extractFunctionAnnotatedThrows(ReflectionFunction $reflectionFunction): array
{
$docComment = $reflectionFunction->getDocComment();
if (! is_string($docComment)) {
return [];
}
$throwsTypes = $this->phpDocTagsFinder->extractTrowsTypesFromDocBlock($docComment);
return $this->expandAnnotatedClasses($reflectionFunction, $throwsTypes);
}
/**
* @param mixed[] $classNames
* @return mixed[]
*/
private function expandAnnotatedClasses(ReflectionFunction $reflectionFunction, array $classNames): array
{
$namespace = $this->functionLikeParser->parseFunction($reflectionFunction);
if (! $namespace instanceof Namespace_) {
return [];
}
$uses = $this->getUses($namespace);
$expandedClasses = [];
foreach ($classNames as $className) {
$shortClassName = $this->classNaming->getShortName($className);
$expandedClasses[] = $uses[$shortClassName] ?? $className;
}
return $expandedClasses;
}
/**
* @return string[]
*/
private function getUses(Namespace_ $namespace): array
{
$uses = [];
foreach ($namespace->stmts as $stmt) {
if (! $stmt instanceof Use_) {
continue;
}
$use = $stmt->uses[0];
if (! $use instanceof UseUse) {
continue;
}
$parts = $use->name->parts;
$uses[$parts[count($parts) - 1]] = implode('\\', $parts);
}
return $uses;
}
}