@@ -58,22 +58,13 @@ public function process(File $phpcsFile, $stackPtr)
5858 $ tokens = $ phpcsFile ->getTokens ();
5959 $ returnTypePtr = $ this ->getDocReturnTypePtr ($ phpcsFile , $ stackPtr );
6060 $ returnTypeString = $ tokens [$ returnTypePtr ]['content ' ];
61- $ returnTypes = explode ('| ' , $ returnTypeString );
62-
63- foreach ($ returnTypes as $ returnType ) {
64- $ returnType = trim ($ returnType );
65- if (in_array ($ returnType , $ this ->returnTypeScalarWhitelist , true )) {
66- continue ;
67- }
68- if (in_array ($ returnType , $ this ->returnTypeClassWhitelist , true )) {
69- continue ;
70- }
71- if ($ this ->isStartingWithUppercaseLetter ($ returnType )) {
72- continue ;
73- }
7461
62+ try {
63+ $ this ->checkReturnTypeShape ($ returnTypeString );
64+ }
65+ catch (\InvalidArgumentException $ exception ) {
7566 $ phpcsFile ->addError (
76- sprintf ( ' Return type "%s" is discouraged ' , $ returnType ),
67+ $ exception -> getMessage ( ),
7768 $ returnTypePtr ,
7869 'ProhibitedReturnType '
7970 );
@@ -145,4 +136,50 @@ private function isStartingWithUppercaseLetter($singleCharacter)
145136
146137 return false ;
147138 }
139+
140+ /**
141+ * @param string $subject The return type string to check.
142+ * @return void
143+ */
144+ private function checkReturnTypeShape (string $ subject )
145+ {
146+ preg_match_all ('#(?<separator>\s*\|\s*)?(?<atom>[^<>\|]+)(?<generic><(?<nested>.*)>)?# ' , $ subject , $ matches );
147+
148+ if (implode ('' , $ matches [0 ]) !== $ subject ) {
149+ throw new \InvalidArgumentException ('Invalid structure in return type " ' . $ subject . '" ' );
150+ }
151+
152+ if (strpos ($ matches ['separator ' ][0 ], '| ' ) !== false ) {
153+ throw new \InvalidArgumentException ('Missing return type in first alternative of type " ' . $ subject . '" ' );
154+ }
155+
156+ foreach ($ matches ['nested ' ] as $ index => $ match ) {
157+ if (!empty ($ matches ['generic ' ][$ index ])) {
158+ if (trim ($ matches ['atom ' ][$ index ]) !== 'array ' ) {
159+ throw new \InvalidArgumentException ('Unexpected generic specification in type " ' . $ matches [0 ][$ index ] . '" ' );
160+ }
161+
162+ $ match = trim ($ match );
163+ if ($ match === '' ) {
164+ throw new \InvalidArgumentException ('Generic specification may not be empty in type " ' . $ matches [0 ][$ index ] . '" ' );
165+ }
166+
167+ $ this ->checkReturnTypeShape ($ match );
168+ }
169+
170+ // Check if atom is in whitelist.
171+ $ returnType = trim ($ matches ['atom ' ][$ index ]);
172+ if (in_array ($ returnType , $ this ->returnTypeScalarWhitelist , true )) {
173+ continue ;
174+ }
175+ if (in_array ($ returnType , $ this ->returnTypeClassWhitelist , true )) {
176+ continue ;
177+ }
178+ if ($ this ->isStartingWithUppercaseLetter ($ returnType )) {
179+ continue ;
180+ }
181+
182+ throw new \InvalidArgumentException ('Return type " ' . $ returnType . '" is discouraged ' );
183+ }
184+ }
148185}
0 commit comments