savinmikhail / annotate_throws_rector
Rector rule to add missing @throws tags for direct throws and inter-class propagation
Package info
github.com/savinmikhail/AnnotateThrowsRector
Type:rector-extension
pkg:composer/savinmikhail/annotate_throws_rector
v0.1.5
2026-06-21 04:40 UTC
Requires
- php: >=8.2
- nikic/php-parser: ^4.19.4 || ^5.1
- phpstan/phpstan: ^2.1.5
- rector/rector: ^2.1.4
- symplify/rule-doc-generator-contracts: ^11.2
Requires (Dev)
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.68.5
- icanhazstring/composer-unused: ^0.8.11
- maglnet/composer-require-checker: ^4.15
- phpunit/phpunit: ^10.5.45
- phpyh/coding-standard: ^2.6.2
- savinmikhail/dist-size-optimizer: ^0.2.7
README
Rector extension that adds missing @throws tags for:
- direct
throwexpressions in class methods - propagation through same-class calls like
$this->foo(),self::foo(),static::foo() - propagation through inter-class calls when the callee already has
@throws
Current MVP scope:
- class methods only
- inter-class propagation relies on existing callee docblocks
- repeated Rector runs can gradually converge the call graph across files
- caught exceptions inside
try/catchare not propagated - unchecked/wide exceptions are skipped by default
- existing
@throwstags are preserved and deduplicated - no removal of stale
@throwstags yet
Install
composer require --dev savinmikhail/annotate_throws_rector
Configure
<?php declare(strict_types=1); use Rector\Config\RectorConfig; use SavinMikhail\AnnotateThrowsRector\AnnotateThrowsRector; return RectorConfig::configure() ->phpstanConfig(__DIR__ . '/phpstan.neon') ->withRules([ AnnotateThrowsRector::class, ]);
If you want to include unchecked exceptions too:
<?php declare(strict_types=1); use Rector\Config\RectorConfig; use SavinMikhail\AnnotateThrowsRector\AnnotateThrowsRector; return RectorConfig::configure() ->phpstanConfig(__DIR__ . '/phpstan.neon') ->withConfiguredRule(AnnotateThrowsRector::class, [ AnnotateThrowsRector::INCLUDE_UNCHECKED => true, AnnotateThrowsRector::EXCLUDED_EXCEPTION_CLASSES => [ \Throwable::class, \Exception::class, \Error::class, \RuntimeException::class, \LogicException::class, ], ]);
When phpstanConfig() is provided, the rule reuses PHPStan checked/unchecked exception policy. The local EXCLUDED_EXCEPTION_CLASSES list is applied on top.
Example
final class InterviewRunner { public function process(): void { $this->normalize(); } public function normalize(): void { throw new \JsonException('Boom'); } }
becomes
final class InterviewRunner { /** * @throws \JsonException */ public function process(): void { $this->normalize(); } /** * @throws \JsonException */ public function normalize(): void { throw new \JsonException('Boom'); } }