File tree Expand file tree Collapse file tree 5 files changed +156
-0
lines changed
Expand file tree Collapse file tree 5 files changed +156
-0
lines changed Original file line number Diff line number Diff line change @@ -2231,6 +2231,30 @@ private function createForExpr(
22312231 }
22322232 }
22332233
2234+ if (
2235+ $ expr instanceof FuncCall
2236+ && !$ expr ->name instanceof Name
2237+ ) {
2238+ $ nameType = $ scope ->getType ($ expr ->name );
2239+ if ($ nameType ->isCallable ()->yes ()) {
2240+ $ isPure = null ;
2241+ foreach ($ nameType ->getCallableParametersAcceptors ($ scope ) as $ variant ) {
2242+ $ variantIsPure = $ variant ->isPure ();
2243+ $ isPure = $ isPure === null ? $ variantIsPure : $ isPure ->and ($ variantIsPure );
2244+ }
2245+
2246+ if ($ isPure !== null ) {
2247+ if ($ isPure ->no ()) {
2248+ return new SpecifiedTypes ([], []);
2249+ }
2250+
2251+ if (!$ this ->rememberPossiblyImpureFunctionValues && !$ isPure ->yes ()) {
2252+ return new SpecifiedTypes ([], []);
2253+ }
2254+ }
2255+ }
2256+ }
2257+
22342258 if (
22352259 $ expr instanceof MethodCall
22362260 && $ expr ->name instanceof Node \Identifier
Original file line number Diff line number Diff line change 1+ <?php declare (strict_types = 1 );
2+
3+ namespace Bug12686 ;
4+
5+ use function PHPStan \Testing \assertType ;
6+
7+ /** @phpstan-impure */
8+ $ f = function (): bool {
9+ return (bool ) rand (0 ,1 );
10+ };
11+
12+ if ($ f ()) {
13+ assertType ('bool ' , $ f ());
14+ }
15+
16+ // Pure closure should still have narrowing
17+ $ h = function (): bool {
18+ return true ;
19+ };
20+
21+ if ($ h ()) {
22+ assertType ('true ' , $ h ());
23+ }
24+
25+ // Multiple callable parameter acceptors (union of closures)
26+ // When one variant is impure, the combined result should be impure
27+ /** @phpstan-impure */
28+ $ impure = function (): bool {
29+ return (bool ) rand (0 , 1 );
30+ };
31+
32+ $ pure = function (): bool {
33+ return true ;
34+ };
35+
36+ if (rand (0 , 1 )) {
37+ $ g = $ impure ;
38+ } else {
39+ $ g = $ pure ;
40+ }
41+
42+ if ($ g ()) {
43+ assertType ('bool ' , $ g ());
44+ }
45+
46+ // Multiple callable parameter acceptors where all are pure
47+ $ pure1 = function (): bool {
48+ return true ;
49+ };
50+
51+ $ pure2 = function (): bool {
52+ return true ;
53+ };
54+
55+ if (rand (0 , 1 )) {
56+ $ p = $ pure1 ;
57+ } else {
58+ $ p = $ pure2 ;
59+ }
60+
61+ if ($ p ()) {
62+ assertType ('true ' , $ p ());
63+ }
Original file line number Diff line number Diff line change 1+ <?php declare (strict_types = 1 );
2+
3+ namespace Bug3770 ;
4+
5+ use function PHPStan \Testing \assertType ;
6+
7+ // PHPDoc on closures should be respected for purity
8+
9+ /** @phpstan-impure */
10+ $ f = static function (string $ input ): bool {
11+ return strlen ($ input ) > rand (0 , 10 );
12+ };
13+
14+ if ($ f ('hello ' )) {
15+ // Should not narrow to true because closure is impure
16+ assertType ('bool ' , $ f ('hello ' ));
17+ }
18+
19+ // Closure with @phpstan-pure should allow narrowing
20+ /** @phpstan-pure */
21+ $ g = static function (string $ input ): bool {
22+ return strlen ($ input ) > 5 ;
23+ };
24+
25+ if ($ g ('hello world ' )) {
26+ assertType ('true ' , $ g ('hello world ' ));
27+ }
Original file line number Diff line number Diff line change 1+ <?php declare (strict_types = 1 );
2+
3+ namespace Bug6822 ;
4+
5+ use function PHPStan \Testing \assertType ;
6+
7+ // Closures marked as @phpstan-impure should not have their return type narrowed
8+
9+ /** @phpstan-impure */
10+ $ closure = function (): bool {
11+ return (bool ) rand (0 , 1 );
12+ };
13+
14+ assertType ('bool ' , $ closure ());
15+
16+ if ($ closure ()) {
17+ assertType ('bool ' , $ closure ());
18+
19+ if ($ closure ()) { // should not be reported as "always true"
20+ echo 'yes ' ;
21+ }
22+ }
23+
24+ // Same with an explicit impure closure assigned to a variable
25+ /** @phpstan-impure */
26+ $ impureFn = function (): int {
27+ return rand (0 , 100 );
28+ };
29+
30+ if ($ impureFn () > 50 ) {
31+ assertType ('int<0, 100> ' , $ impureFn ());
32+
33+ if ($ impureFn () > 50 ) { // should not be reported as "always true"
34+ echo 'yes ' ;
35+ }
36+ }
Original file line number Diff line number Diff line change @@ -231,4 +231,10 @@ public function testBug4284(): void
231231 ]);
232232 }
233233
234+ public function testBug6822 (): void
235+ {
236+ $ this ->treatPhpDocTypesAsCertain = true ;
237+ $ this ->analyse ([__DIR__ . '/../../Analyser/nsrt/bug-6822.php ' ], []);
238+ }
239+
234240}
You can’t perform that action at this time.
0 commit comments