@@ -63,6 +63,8 @@ class CallableType implements CompoundType, CallableParametersAcceptor
63
63
64
64
private TemplateTypeMap $ resolvedTemplateTypeMap ;
65
65
66
+ private TrinaryLogic $ isPure ;
67
+
66
68
/**
67
69
* @api
68
70
* @param array<int, ParameterReflection>|null $parameters
@@ -75,13 +77,15 @@ public function __construct(
75
77
?TemplateTypeMap $ templateTypeMap = null ,
76
78
?TemplateTypeMap $ resolvedTemplateTypeMap = null ,
77
79
private array $ templateTags = [],
80
+ ?TrinaryLogic $ isPure = null ,
78
81
)
79
82
{
80
83
$ this ->parameters = $ parameters ?? [];
81
84
$ this ->returnType = $ returnType ?? new MixedType ();
82
85
$ this ->isCommonCallable = $ parameters === null && $ returnType === null ;
83
86
$ this ->templateTypeMap = $ templateTypeMap ?? TemplateTypeMap::createEmpty ();
84
87
$ this ->resolvedTemplateTypeMap = $ resolvedTemplateTypeMap ?? TemplateTypeMap::createEmpty ();
88
+ $ this ->isPure = $ isPure ?? TrinaryLogic::createMaybe ();
85
89
}
86
90
87
91
/**
@@ -92,6 +96,11 @@ public function getTemplateTags(): array
92
96
return $ this ->templateTags ;
93
97
}
94
98
99
+ public function isPure (): TrinaryLogic
100
+ {
101
+ return $ this ->isPure ;
102
+ }
103
+
95
104
/**
96
105
* @return string[]
97
106
*/
@@ -146,7 +155,7 @@ public function isSuperTypeOf(Type $type): TrinaryLogic
146
155
private function isSuperTypeOfInternal (Type $ type , bool $ treatMixedAsAny ): AcceptsResult
147
156
{
148
157
$ isCallable = new AcceptsResult ($ type ->isCallable (), []);
149
- if ($ isCallable ->no () || $ this -> isCommonCallable ) {
158
+ if ($ isCallable ->no ()) {
150
159
return $ isCallable ;
151
160
}
152
161
@@ -155,6 +164,19 @@ private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny): Accep
155
164
$ scope = new OutOfClassScope ();
156
165
}
157
166
167
+ if ($ this ->isCommonCallable ) {
168
+ if ($ this ->isPure ()->yes ()) {
169
+ $ typePure = TrinaryLogic::createYes ();
170
+ foreach ($ type ->getCallableParametersAcceptors ($ scope ) as $ variant ) {
171
+ $ typePure = $ typePure ->and ($ variant ->isPure ());
172
+ }
173
+
174
+ return $ isCallable ->and (new AcceptsResult ($ typePure , []));
175
+ }
176
+
177
+ return $ isCallable ;
178
+ }
179
+
158
180
$ variantsResult = null ;
159
181
foreach ($ type ->getCallableParametersAcceptors ($ scope ) as $ variant ) {
160
182
$ isSuperType = CallableTypeHelper::isParametersAcceptorSuperTypeOf ($ this , $ variant , $ treatMixedAsAny );
@@ -221,6 +243,7 @@ function (): string {
221
243
$ this ->templateTypeMap ,
222
244
$ this ->resolvedTemplateTypeMap ,
223
245
$ this ->templateTags ,
246
+ $ this ->isPure ,
224
247
);
225
248
226
249
return $ printer ->print ($ selfWithoutParameterNames ->toPhpDocNode ());
@@ -247,11 +270,16 @@ public function getThrowPoints(): array
247
270
248
271
public function getImpurePoints (): array
249
272
{
273
+ $ pure = $ this ->isPure ();
274
+ if ($ pure ->yes ()) {
275
+ return [];
276
+ }
277
+
250
278
return [
251
279
new SimpleImpurePoint (
252
280
'functionCall ' ,
253
281
'call to a callable ' ,
254
- false ,
282
+ $ pure -> no () ,
255
283
),
256
284
];
257
285
}
@@ -414,6 +442,7 @@ public function traverse(callable $cb): Type
414
442
$ this ->templateTypeMap ,
415
443
$ this ->resolvedTemplateTypeMap ,
416
444
$ this ->templateTags ,
445
+ $ this ->isPure ,
417
446
);
418
447
}
419
448
@@ -463,6 +492,7 @@ public function traverseSimultaneously(Type $right, callable $cb): Type
463
492
$ this ->templateTypeMap ,
464
493
$ this ->resolvedTemplateTypeMap ,
465
494
$ this ->templateTags ,
495
+ $ this ->isPure ,
466
496
);
467
497
}
468
498
@@ -599,7 +629,7 @@ public function getFiniteTypes(): array
599
629
public function toPhpDocNode (): TypeNode
600
630
{
601
631
if ($ this ->isCommonCallable ) {
602
- return new IdentifierTypeNode ('callable ' );
632
+ return new IdentifierTypeNode ($ this -> isPure ()-> yes () ? ' pure-callable ' : 'callable ' );
603
633
}
604
634
605
635
$ parameters = [];
@@ -623,7 +653,7 @@ public function toPhpDocNode(): TypeNode
623
653
}
624
654
625
655
return new CallableTypeNode (
626
- new IdentifierTypeNode ('callable ' ),
656
+ new IdentifierTypeNode ($ this -> isPure -> yes () ? ' pure-callable ' : 'callable ' ),
627
657
$ parameters ,
628
658
$ this ->returnType ->toPhpDocNode (),
629
659
$ templateTags ,
@@ -639,6 +669,10 @@ public static function __set_state(array $properties): Type
639
669
(bool ) $ properties ['isCommonCallable ' ] ? null : $ properties ['parameters ' ],
640
670
(bool ) $ properties ['isCommonCallable ' ] ? null : $ properties ['returnType ' ],
641
671
$ properties ['variadic ' ],
672
+ $ properties ['templateTypeMap ' ],
673
+ $ properties ['resolvedTemplateTypeMap ' ],
674
+ $ properties ['templateTags ' ],
675
+ $ properties ['isPure ' ],
642
676
);
643
677
}
644
678
0 commit comments