77use PHPStan \Type \NeverType ;
88use PHPStan \Type \ObjectType ;
99use PHPStan \Type \Type ;
10+ use PHPStan \Type \TypeCombinator ;
1011use PHPStan \Type \TypeUtils ;
1112use PHPStan \Type \TypeWithClassName ;
1213use PHPStan \Type \VerbosityLevel ;
@@ -24,7 +25,7 @@ public function __construct(ExceptionTypeResolver $exceptionTypeResolver)
2425 /**
2526 * @param Type|null $throwType
2627 * @param ThrowPoint[] $throwPoints
27- * @return array<int, array{string, Node\Expr|Node\Stmt}>
28+ * @return array<int, array{string, Node\Expr|Node\Stmt, int|null }>
2829 */
2930 public function check (?Type $ throwType , array $ throwPoints ): array
3031 {
@@ -53,11 +54,55 @@ public function check(?Type $throwType, array $throwPoints): array
5354 continue ;
5455 }
5556
56- $ classes [] = [$ throwPointType ->describe (VerbosityLevel::typeOnly ()), $ throwPoint ->getNode ()];
57+ $ classes [] = [$ throwPointType ->describe (VerbosityLevel::typeOnly ()), $ throwPoint ->getNode (), $ this -> getNewCatchPosition ( $ throwPointType , $ throwPoint -> getNode ()) ];
5758 }
5859 }
5960
6061 return $ classes ;
6162 }
6263
64+ private function getNewCatchPosition (Type $ throwPointType , Node $ throwPointNode ): ?int
65+ {
66+ if ($ throwPointType instanceof TypeWithClassName) {
67+ // to get rid of type subtraction
68+ $ throwPointType = new ObjectType ($ throwPointType ->getClassName ());
69+ }
70+ $ tryCatch = $ this ->findTryCatch ($ throwPointNode );
71+ if ($ tryCatch === null ) {
72+ return null ;
73+ }
74+
75+ $ position = 0 ;
76+ foreach ($ tryCatch ->catches as $ catch ) {
77+ $ type = TypeCombinator::union (...array_map (static function (Node \Name $ class ): ObjectType {
78+ return new ObjectType ($ class ->toString ());
79+ }, $ catch ->types ));
80+ if (!$ throwPointType ->isSuperTypeOf ($ type )->yes ()) {
81+ continue ;
82+ }
83+
84+ $ position ++;
85+ }
86+
87+ return $ position ;
88+ }
89+
90+ private function findTryCatch (Node $ node ): ?Node \Stmt \TryCatch
91+ {
92+ if ($ node instanceof Node \FunctionLike) {
93+ return null ;
94+ }
95+
96+ if ($ node instanceof Node \Stmt \TryCatch) {
97+ return $ node ;
98+ }
99+
100+ $ parent = $ node ->getAttribute ('parent ' );
101+ if ($ parent === null ) {
102+ return null ;
103+ }
104+
105+ return $ this ->findTryCatch ($ parent );
106+ }
107+
63108}
0 commit comments