77use PHPStan \Node \MethodReturnStatementsNode ;
88use PHPStan \Rules \Rule ;
99use PHPStan \Rules \RuleErrorBuilder ;
10+ use PHPStan \Type \FileTypeMapper ;
1011use function sprintf ;
1112
1213/**
@@ -16,8 +17,10 @@ final class TooWideMethodThrowTypeRule implements Rule
1617{
1718
1819 public function __construct (
20+ private FileTypeMapper $ fileTypeMapper ,
1921 private TooWideThrowTypeCheck $ check ,
2022 private bool $ checkProtectedAndPublicMethods ,
23+ private bool $ tooWideImplicitThrows ,
2124 )
2225 {
2326 }
@@ -49,8 +52,34 @@ public function processNode(Node $node, Scope $scope): array
4952 return [];
5053 }
5154
55+ $ unusedThrowClasses = $ this ->check ->check ($ throwType , $ statementResult ->getThrowPoints ());
56+ if (!$ this ->tooWideImplicitThrows ) {
57+ $ docComment = $ node ->getDocComment ();
58+ if ($ docComment === null ) {
59+ return [];
60+ }
61+
62+ $ classReflection = $ node ->getClassReflection ();
63+ $ resolvedPhpDoc = $ this ->fileTypeMapper ->getResolvedPhpDoc (
64+ $ scope ->getFile (),
65+ $ classReflection ->getName (),
66+ $ scope ->isInTrait () ? $ scope ->getTraitReflection ()->getName () : null ,
67+ $ method ->getName (),
68+ $ docComment ->getText (),
69+ );
70+
71+ if ($ resolvedPhpDoc ->getThrowsTag () === null ) {
72+ return [];
73+ }
74+
75+ $ explicitThrowType = $ resolvedPhpDoc ->getThrowsTag ()->getType ();
76+ if ($ explicitThrowType ->equals ($ throwType )) {
77+ return [];
78+ }
79+ }
80+
5281 $ errors = [];
53- foreach ($ this -> check -> check ( $ throwType , $ statementResult -> getThrowPoints ()) as $ throwClass ) {
82+ foreach ($ unusedThrowClasses as $ throwClass ) {
5483 $ errors [] = RuleErrorBuilder::message (sprintf (
5584 'Method %s::%s() has %s in PHPDoc @throws tag but it \'s not thrown. ' ,
5685 $ method ->getDeclaringClass ()->getDisplayName (),
0 commit comments