66use PHP_CodeSniffer \Sniffs \Sniff ;
77use PHPStan \PhpDocParser \Ast \Type \ArrayShapeNode ;
88use PHPStan \PhpDocParser \Ast \Type \ArrayTypeNode ;
9+ use PHPStan \PhpDocParser \Ast \Type \CallableTypeNode ;
910use PHPStan \PhpDocParser \Ast \Type \GenericTypeNode ;
1011use PHPStan \PhpDocParser \Ast \Type \IdentifierTypeNode ;
1112use PHPStan \PhpDocParser \Ast \Type \IntersectionTypeNode ;
2324use SlevomatCodingStandard \Helpers \TokenHelper ;
2425use SlevomatCodingStandard \Helpers \TypeHintHelper ;
2526use function array_map ;
27+ use function array_unique ;
28+ use function array_values ;
2629use function count ;
2730use function in_array ;
2831use function sprintf ;
@@ -136,15 +139,7 @@ private function checkTypeHint(
136139
137140 $ typeNode = $ propertyAnnotation ->getType ();
138141
139- $ annotationContainsOneType = AnnotationTypeHelper::containsOneType ($ typeNode );
140- if (
141- !$ annotationContainsOneType
142- && !AnnotationTypeHelper::containsJustTwoTypes ($ typeNode )
143- ) {
144- return ;
145- }
146-
147- if ($ annotationContainsOneType ) {
142+ if (AnnotationTypeHelper::containsOneType ($ typeNode )) {
148143 /** @var ArrayTypeNode|ArrayShapeNode|GenericTypeNode|IdentifierTypeNode|ThisTypeNode $typeNode */
149144 $ typeNode = $ typeNode ;
150145 $ possibleTypeHint = $ typeNode instanceof ArrayTypeNode || $ typeNode instanceof ArrayShapeNode
@@ -153,35 +148,67 @@ private function checkTypeHint(
153148 $ nullableTypeHint = false ;
154149
155150 } else {
156- /** @var UnionTypeNode|IntersectionTypeNode $typeNode */
157- $ typeNode = $ typeNode ;
151+ $ possibleTypeHint = null ;
152+ $ nullableTypeHint = false ;
158153
159- if (
160- !AnnotationTypeHelper::containsNullType ($ typeNode )
161- && !AnnotationTypeHelper::containsTraversableType ($ typeNode , $ phpcsFile , $ propertyPointer , $ this ->getTraversableTypeHints ())
162- ) {
163- return ;
164- }
154+ if ($ typeNode instanceof UnionTypeNode && !AnnotationTypeHelper::containsJustTwoTypes ($ typeNode )) {
155+ $ typeHints = [];
156+ foreach ($ typeNode ->types as $ innerTypeNode ) {
157+ if (!($ innerTypeNode instanceof CallableTypeNode
158+ || $ innerTypeNode instanceof GenericTypeNode
159+ || $ innerTypeNode instanceof IdentifierTypeNode
160+ || $ innerTypeNode instanceof ThisTypeNode)
161+ ) {
162+ return ;
163+ }
164+
165+ $ typeHints [] = AnnotationTypeHelper::getTypeHintFromOneType ($ innerTypeNode );
166+ }
167+
168+ $ typeHints = array_values (array_unique ($ typeHints ));
165169
166- if (AnnotationTypeHelper::containsNullType ($ typeNode )) {
167- /** @var ArrayTypeNode|ArrayShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode $notNullTypeHintNode */
168- $ notNullTypeHintNode = AnnotationTypeHelper::getTypeFromNullableType ($ typeNode );
169- $ possibleTypeHint = $ notNullTypeHintNode instanceof ArrayTypeNode || $ notNullTypeHintNode instanceof ArrayShapeNode
170- ? 'array '
171- : AnnotationTypeHelper::getTypeHintFromOneType ($ notNullTypeHintNode );
172- $ nullableTypeHint = true ;
173- } else {
174- $ itemsSpecificationTypeHint = AnnotationTypeHelper::getItemsSpecificationTypeFromType ($ typeNode , $ this ->getTraversableTypeHints ());
175- if (!$ itemsSpecificationTypeHint instanceof ArrayTypeNode) {
170+ if (count ($ typeHints ) === 1 ) {
171+ $ possibleTypeHint = $ typeHints [0 ];
172+ $ nullableTypeHint = false ;
173+ } elseif (count ($ typeHints ) === 2 && ($ typeHints [0 ] === 'null ' || $ typeHints [1 ] === 'null ' )) {
174+ $ possibleTypeHint = $ typeHints [0 ] === 'null ' ? $ typeHints [1 ] : $ typeHints [0 ];
175+ $ nullableTypeHint = true ;
176+ } else {
176177 return ;
177178 }
179+ }
178180
179- $ possibleTypeHint = AnnotationTypeHelper::getTraversableTypeHintFromType ($ typeNode , $ this ->getTraversableTypeHints ());
180- $ nullableTypeHint = false ;
181+ if ($ possibleTypeHint === null ) {
182+ /** @var UnionTypeNode|IntersectionTypeNode $typeNode */
183+ $ typeNode = $ typeNode ;
181184
182- if (!TypeHintHelper::isTraversableType (TypeHintHelper::getFullyQualifiedTypeHint ($ phpcsFile , $ propertyPointer , $ possibleTypeHint ), $ this ->getTraversableTypeHints ())) {
185+ if (
186+ !AnnotationTypeHelper::containsNullType ($ typeNode )
187+ && !AnnotationTypeHelper::containsTraversableType ($ typeNode , $ phpcsFile , $ propertyPointer , $ this ->getTraversableTypeHints ())
188+ ) {
183189 return ;
184190 }
191+
192+ if (AnnotationTypeHelper::containsNullType ($ typeNode )) {
193+ /** @var ArrayTypeNode|ArrayShapeNode|IdentifierTypeNode|ThisTypeNode|GenericTypeNode $notNullTypeHintNode */
194+ $ notNullTypeHintNode = AnnotationTypeHelper::getTypeFromNullableType ($ typeNode );
195+ $ possibleTypeHint = $ notNullTypeHintNode instanceof ArrayTypeNode || $ notNullTypeHintNode instanceof ArrayShapeNode
196+ ? 'array '
197+ : AnnotationTypeHelper::getTypeHintFromOneType ($ notNullTypeHintNode );
198+ $ nullableTypeHint = true ;
199+ } else {
200+ $ itemsSpecificationTypeHint = AnnotationTypeHelper::getItemsSpecificationTypeFromType ($ typeNode , $ this ->getTraversableTypeHints ());
201+ if (!$ itemsSpecificationTypeHint instanceof ArrayTypeNode) {
202+ return ;
203+ }
204+
205+ $ possibleTypeHint = AnnotationTypeHelper::getTraversableTypeHintFromType ($ typeNode , $ this ->getTraversableTypeHints ());
206+ $ nullableTypeHint = false ;
207+
208+ if (!TypeHintHelper::isTraversableType (TypeHintHelper::getFullyQualifiedTypeHint ($ phpcsFile , $ propertyPointer , $ possibleTypeHint ), $ this ->getTraversableTypeHints ())) {
209+ return ;
210+ }
211+ }
185212 }
186213 }
187214
0 commit comments