2525 */
2626class DocblockTypesResolver extends NodeVisitorAbstract
2727{
28+ public const THROWS_TYPES_ATTRIBUTE = 'docblock_throws_types ' ;
29+
2830 private NameContext $ nameContext ;
2931
3032 private bool $ parseCustomAnnotations ;
@@ -117,6 +119,18 @@ private function resolveFunctionTypes(Node $node): void
117119 return ;
118120 }
119121
122+ $ this ->resolveParamTypes ($ node , $ docblock );
123+
124+ $ this ->resolveReturnValueType ($ node , $ docblock );
125+
126+ $ this ->resolveThrowsValueType ($ node , $ docblock );
127+ }
128+
129+ /**
130+ * @param Stmt\ClassMethod|Stmt\Function_|Expr\Closure|Expr\ArrowFunction $node
131+ */
132+ private function resolveParamTypes (Node $ node , Docblock $ docblock ): void
133+ {
120134 // extract param types from param tags
121135 foreach ($ node ->params as $ param ) {
122136 if (!$ this ->isTypeArray ($ param ->type )) { // not an array, nothing to do
@@ -136,19 +150,62 @@ private function resolveFunctionTypes(Node $node): void
136150
137151 $ param ->type = $ this ->resolveName (new Name ($ type ), Stmt \Use_::TYPE_NORMAL );
138152 }
153+ }
139154
140- // extract return type from return tag
141- if ($ this ->isTypeArray ($ node ->returnType )) {
142- $ type = $ docblock ->getReturnTagTypes ();
143- $ type = array_pop ($ type );
155+ /**
156+ * @param Stmt\ClassMethod|Stmt\Function_|Expr\Closure|Expr\ArrowFunction $node
157+ */
158+ private function resolveReturnValueType (Node $ node , Docblock $ docblock ): void
159+ {
160+ if (null === $ node ->returnType ) {
161+ return ;
162+ }
144163
145- // we ignore any type which is not a class
146- if (!$ this ->isTypeClass ($ type )) {
147- return ;
164+ if (!$ this ->isTypeArray ($ node ->returnType )) {
165+ return ;
166+ }
167+
168+ $ type = $ docblock ->getReturnTagTypes ();
169+ $ type = array_pop ($ type );
170+
171+ // we ignore any type which is not a class
172+ if (!$ this ->isTypeClass ($ type )) {
173+ return ;
174+ }
175+
176+ $ node ->returnType = $ this ->resolveName (new Name ($ type ), Stmt \Use_::TYPE_NORMAL );
177+ }
178+
179+ /**
180+ * @param Stmt\ClassMethod|Stmt\Function_|Expr\Closure|Expr\ArrowFunction $node
181+ */
182+ private function resolveThrowsValueType (Node $ node , Docblock $ docblock ): void
183+ {
184+ // extract throw types from throw tag
185+ $ throwValues = $ docblock ->getThrowTagsTypes ();
186+
187+ if (empty ($ throwValues )) {
188+ return ;
189+ }
190+
191+ $ docComment = $ node ->getDocComment ();
192+ $ docblockStartLine = $ docComment ? $ docComment ->getStartLine () : $ node ->getStartLine ();
193+
194+ $ throwsTypesResolved = [];
195+
196+ foreach ($ throwValues as ['type ' => $ throwValue , 'line ' => $ relativeLine ]) {
197+ if (str_starts_with ($ throwValue , '\\' )) {
198+ $ name = new FullyQualified (substr ($ throwValue , 1 ));
199+ } else {
200+ $ name = $ this ->resolveName (new Name ($ throwValue ), Stmt \Use_::TYPE_NORMAL );
148201 }
149202
150- $ node ->returnType = $ this ->resolveName (new Name ($ type ), Stmt \Use_::TYPE_NORMAL );
203+ $ name ->setAttribute ('startLine ' , $ docblockStartLine + $ relativeLine - 1 );
204+
205+ $ throwsTypesResolved [] = $ name ;
151206 }
207+
208+ $ node ->setAttribute (self ::THROWS_TYPES_ATTRIBUTE , $ throwsTypesResolved );
152209 }
153210
154211 /**
0 commit comments