10
10
use PHPStan \Type \ClosureType ;
11
11
use PHPStan \Type \ErrorType ;
12
12
use PHPStan \Type \Generic \TemplateMixedType ;
13
+ use PHPStan \Type \IntersectionType ;
13
14
use PHPStan \Type \MixedType ;
14
15
use PHPStan \Type \NeverType ;
15
16
use PHPStan \Type \NullType ;
@@ -303,6 +304,20 @@ public function findTypeToCheck(
303
304
return new FoundTypeResult (new ErrorType (), [], [], null );
304
305
}
305
306
$ type = $ scope ->getType ($ var );
307
+
308
+ return $ this ->findTypeToCheckImplementation ($ scope , $ var , $ type , $ unknownClassErrorPattern , $ unionTypeCriteriaCallback , true );
309
+ }
310
+
311
+ /** @param callable(Type $type): bool $unionTypeCriteriaCallback */
312
+ private function findTypeToCheckImplementation (
313
+ Scope $ scope ,
314
+ Expr $ var ,
315
+ Type $ type ,
316
+ string $ unknownClassErrorPattern ,
317
+ callable $ unionTypeCriteriaCallback ,
318
+ bool $ isTopLevel = false ,
319
+ ): FoundTypeResult
320
+ {
306
321
if (!$ this ->checkNullables && !$ type ->isNull ()->yes ()) {
307
322
$ type = TypeCombinator::removeNull ($ type );
308
323
}
@@ -345,27 +360,33 @@ public function findTypeToCheck(
345
360
if ($ type instanceof MixedType || $ type instanceof NeverType) {
346
361
return new FoundTypeResult (new ErrorType (), [], [], null );
347
362
}
348
- if ($ type instanceof StaticType) {
349
- $ type = $ type ->getStaticObjectType ();
363
+ if (!$ this ->newRuleLevelHelper ) {
364
+ if ($ isTopLevel && $ type instanceof StaticType) {
365
+ $ type = $ type ->getStaticObjectType ();
366
+ }
350
367
}
351
368
352
369
$ errors = [];
353
- $ directClassNames = $ type ->getObjectClassNames ();
354
370
$ hasClassExistsClass = false ;
355
- foreach ($ directClassNames as $ referencedClass ) {
356
- if ($ this ->reflectionProvider ->hasClass ($ referencedClass )) {
357
- $ classReflection = $ this ->reflectionProvider ->getClass ($ referencedClass );
358
- if (!$ classReflection ->isTrait ()) {
371
+ $ directClassNames = [];
372
+
373
+ if ($ isTopLevel ) {
374
+ $ directClassNames = $ type ->getObjectClassNames ();
375
+ foreach ($ directClassNames as $ referencedClass ) {
376
+ if ($ this ->reflectionProvider ->hasClass ($ referencedClass )) {
377
+ $ classReflection = $ this ->reflectionProvider ->getClass ($ referencedClass );
378
+ if (!$ classReflection ->isTrait ()) {
379
+ continue ;
380
+ }
381
+ }
382
+
383
+ if ($ scope ->isInClassExists ($ referencedClass )) {
384
+ $ hasClassExistsClass = true ;
359
385
continue ;
360
386
}
361
- }
362
387
363
- if ($ scope ->isInClassExists ($ referencedClass )) {
364
- $ hasClassExistsClass = true ;
365
- continue ;
388
+ $ errors [] = RuleErrorBuilder::message (sprintf ($ unknownClassErrorPattern , $ referencedClass ))->line ($ var ->getLine ())->discoveringSymbolsTip ()->build ();
366
389
}
367
-
368
- $ errors [] = RuleErrorBuilder::message (sprintf ($ unknownClassErrorPattern , $ referencedClass ))->line ($ var ->getLine ())->discoveringSymbolsTip ()->build ();
369
390
}
370
391
371
392
if (count ($ errors ) > 0 || $ hasClassExistsClass ) {
@@ -376,28 +397,76 @@ public function findTypeToCheck(
376
397
return new FoundTypeResult (new ErrorType (), [], [], null );
377
398
}
378
399
379
- if (
380
- (
381
- !$ this ->checkUnionTypes
382
- && $ type instanceof UnionType
383
- && !$ type instanceof BenevolentUnionType
384
- ) || (
385
- !$ this ->checkBenevolentUnionTypes
386
- && $ type instanceof BenevolentUnionType
387
- )
388
- ) {
389
- $ newTypes = [];
400
+ if ($ this ->newRuleLevelHelper ) {
401
+ if ($ type instanceof UnionType) {
402
+ $ shouldFilterUnion = (
403
+ !$ this ->checkUnionTypes
404
+ && !$ type instanceof BenevolentUnionType
405
+ ) || (
406
+ !$ this ->checkBenevolentUnionTypes
407
+ && $ type instanceof BenevolentUnionType
408
+ );
390
409
391
- foreach ($ type ->getTypes () as $ innerType ) {
392
- if (!$ unionTypeCriteriaCallback ($ innerType )) {
393
- continue ;
410
+ $ newTypes = [];
411
+
412
+ foreach ($ type ->getTypes () as $ innerType ) {
413
+ if ($ shouldFilterUnion && !$ unionTypeCriteriaCallback ($ innerType )) {
414
+ continue ;
415
+ }
416
+
417
+ $ newTypes [] = $ this ->findTypeToCheckImplementation (
418
+ $ scope ,
419
+ $ var ,
420
+ $ innerType ,
421
+ $ unknownClassErrorPattern ,
422
+ $ unionTypeCriteriaCallback ,
423
+ )->getType ();
394
424
}
395
425
396
- $ newTypes [] = $ innerType ;
426
+ if (count ($ newTypes ) > 0 ) {
427
+ return new FoundTypeResult (TypeCombinator::union (...$ newTypes ), $ directClassNames , [], null );
428
+ }
397
429
}
398
430
399
- if (count ($ newTypes ) > 0 ) {
400
- return new FoundTypeResult (TypeCombinator::union (...$ newTypes ), $ directClassNames , [], null );
431
+ if ($ type instanceof IntersectionType) {
432
+ $ newTypes = [];
433
+
434
+ foreach ($ type ->getTypes () as $ innerType ) {
435
+ $ newTypes [] = $ this ->findTypeToCheckImplementation (
436
+ $ scope ,
437
+ $ var ,
438
+ $ innerType ,
439
+ $ unknownClassErrorPattern ,
440
+ $ unionTypeCriteriaCallback ,
441
+ )->getType ();
442
+ }
443
+
444
+ return new FoundTypeResult (TypeCombinator::intersect (...$ newTypes ), $ directClassNames , [], null );
445
+ }
446
+ } else {
447
+ if (
448
+ (
449
+ !$ this ->checkUnionTypes
450
+ && $ type instanceof UnionType
451
+ && !$ type instanceof BenevolentUnionType
452
+ ) || (
453
+ !$ this ->checkBenevolentUnionTypes
454
+ && $ type instanceof BenevolentUnionType
455
+ )
456
+ ) {
457
+ $ newTypes = [];
458
+
459
+ foreach ($ type ->getTypes () as $ innerType ) {
460
+ if (!$ unionTypeCriteriaCallback ($ innerType )) {
461
+ continue ;
462
+ }
463
+
464
+ $ newTypes [] = $ innerType ;
465
+ }
466
+
467
+ if (count ($ newTypes ) > 0 ) {
468
+ return new FoundTypeResult (TypeCombinator::union (...$ newTypes ), $ directClassNames , [], null );
469
+ }
401
470
}
402
471
}
403
472
0 commit comments