← All results

pure-closure

Group: Advanced PHPDoc types · Category: ecosystem · File: phpdoc_advanced_fallback_pure_closure.php

A Closure with no side effects — pure-callable narrowed to the class. The two constraints fail independently: a callable string fails the Closure half without any purity analysis, while an echoing closure fails only the half that requires looking inside the body. An analyzer that never infers purity for a closure literal cannot construct a value of this type at all, so it reports the valid probe and the @return body as well. That is not an expectation of the test — it shows up under false positives, which is the honest place for "rejects everything". References: - PHPStan TypeNodeResolver pure-closure resolves to ClosureType::createPure()

Analyzer results

AnalyzerVersionResultDiagnostics
phan 6.0.7Phan 6.0.7 php-ast version 1.1.3 PHP version used to run Phan: 8.5.9 Unrecognized
phpdoc_advanced_fallback_pure_closure.php:25: Saw a token Phan may have failed to parse after '* @return pure-closure': after pure, saw '-' [PhanUnextractableAnnotationSuffix]
phpdoc_advanced_fallback_pure_closure.php:27: Return type of returnsPureClosure() is undeclared type \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure [PhanUndeclaredTypeReturnType]
phpdoc_advanced_fallback_pure_closure.php:29: Returning (fn) of type Closure(int):int but returnsPureClosure() is declared to return \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure (no real type) (the inferred real return type has nothing in common with the declared phpdoc return type) [PhanTypeMismatchReturnProbablyReal]
phpdoc_advanced_fallback_pure_closure.php:37: Saw possibly unextractable annotation for a fragment of comment '* @param pure-closure $value': after pure, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName]
phpdoc_advanced_fallback_pure_closure.php:37: Saw a token Phan may have failed to parse after '* @param pure-closure $value': after pure, saw '-' [PhanUnextractableAnnotationSuffix]
phpdoc_advanced_fallback_pure_closure.php:39: Parameter $value has undeclared type \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure [PhanUndeclaredTypeParameter]
phpdoc_advanced_fallback_pure_closure.php:44: Argument 1 ($value) is returnsPureClosure() of type \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure but \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsClosure() takes \Closure defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_pure_closure.php:32 [PhanTypeMismatchArgument]
phpdoc_advanced_fallback_pure_closure.php:47: Argument 1 ($value) is (fn) of type Closure(int):int but \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure() takes \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_pure_closure.php:39 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
phpdoc_advanced_fallback_pure_closure.php:50: Argument 1 ($value) is 'strlen' of type string but \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure() takes \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_pure_closure.php:39 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
phpdoc_advanced_fallback_pure_closure.php:56: Argument 1 ($value) is $impureClosure of type Closure():void but \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure() takes \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_pure_closure.php:39 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
  • Recognition: spelling not resolved — reported on declaration line(s) 25, 27, 37, 39
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 29: Expected valid value to be accepted, got ["Returning (fn) of type Closure(int):int but returnsPureClosure() is declared to return \\Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure (no real type) (the inferred real return type has nothing in common with the declared phpdoc return type) [PhanTypeMismatchReturnProbablyReal]"]
Line 44: Expected valid value to be accepted, got ["Argument 1 ($value) is returnsPureClosure() of type \\Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure but \\Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsClosure() takes \\Closure defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_pure_closure.php:32 [PhanTypeMismatchArgument]"]
Line 47: Expected valid value to be accepted, got ["Argument 1 ($value) is (fn) of type Closure(int):int but \\Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsPureClosure() takes \\Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_pure_closure.php:39 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]"]
phpstan 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 Incidental (2/2) reported Lv.5+
phpdoc_advanced_fallback_pure_closure.php:29: Function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure() should return pure-Closure but returns Closure(int): int. [identifier=return.type] [reported-from-level=3]
phpdoc_advanced_fallback_pure_closure.php:47: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, Closure(int): int given. [identifier=argument.type] [reported-from-level=5]
phpdoc_advanced_fallback_pure_closure.php:50: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, 'strlen' given. [identifier=argument.type] [reported-from-level=5]
phpdoc_advanced_fallback_pure_closure.php:56: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, Closure(): void given. [identifier=argument.type] [reported-from-level=5]
With strict-rules
phpdoc_advanced_fallback_pure_closure.php:29: Function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure() should return pure-Closure but returns Closure(int): int. [identifier=return.type]
phpdoc_advanced_fallback_pure_closure.php:47: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, Closure(int): int given. [identifier=argument.type]
phpdoc_advanced_fallback_pure_closure.php:50: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, 'strlen' given. [identifier=argument.type]
phpdoc_advanced_fallback_pure_closure.php:56: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, Closure(): void given. [identifier=argument.type]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported — incidental, since values the type admits were also rejected on line(s) 29, 47
Expectation diff
Line 29: Expected valid value to be accepted, got ["Function Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\returnsPureClosure() should return pure-Closure but returns Closure(int): int. [identifier=return.type] [reported-from-level=3]"]
Line 47: Expected valid value to be accepted, got ["Parameter #1 $value of function Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsPureClosure expects pure-Closure, Closure(int): int given. [identifier=argument.type] [reported-from-level=5]"]
psalm 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac
next: 7.0.0-beta19
Unrecognized (pzoom≠)
phpdoc_advanced_fallback_pure_closure.php:27: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure in docblock for Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure [InvalidDocblock]
phpdoc_advanced_fallback_pure_closure.php:27: Method Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure does not have a return type, expecting pure-Closure(int):int [MissingReturnType]
phpdoc_advanced_fallback_pure_closure.php:37: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure in docblock for Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure [InvalidDocblock]
phpdoc_advanced_fallback_pure_closure.php:39: Parameter $value has no provided type [MissingParamType]
phpdoc_advanced_fallback_pure_closure.php:44: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsClosure cannot be mixed, expecting Closure [MixedArgument]
pzoom (Psalm port)
phpdoc_advanced_fallback_pure_closure.php:50: Argument 1 of acceptsPureClosure expects Closure, but pure-callable(string):($string is '' ? 0 : ($string is non-empty-string ? int<1, max> : 0|int<1, max>)) provided [ArgumentTypeCoercion]
  • Recognition: spelling not resolved — reported on declaration line(s) 27, 37, 39
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 44: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsClosure cannot be mixed, expecting Closure [MixedArgument]"]
mago 1.46.0mago 1.46.0 Incidental (2/2)
phpdoc_advanced_fallback_pure_closure.php:29: Invalid return type for function `Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure`: expected `(pure-closure(...mixed): mixed)`, but found `(closure(int): int)`. [invalid-return-statement]
phpdoc_advanced_fallback_pure_closure.php:47: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure`: expected `(pure-closure(...mixed): mixed)`, but found `(closure(int): int)`. [invalid-argument]
phpdoc_advanced_fallback_pure_closure.php:50: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure`: expected `(pure-closure(...mixed): mixed)`, but found `string('strlen')`. [invalid-argument]
phpdoc_advanced_fallback_pure_closure.php:56: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure`: expected `(pure-closure(...mixed): mixed)`, but found `(closure(): void)`. [invalid-argument]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported — incidental, since values the type admits were also rejected on line(s) 29, 47
Expectation diff
Line 29: Expected valid value to be accepted, got ["Invalid return type for function `Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\returnsPureClosure`: expected `(pure-closure(...mixed): mixed)`, but found `(closure(int): int)`. [invalid-return-statement]"]
Line 47: Expected valid value to be accepted, got ["Invalid argument type for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsPureClosure`: expected `(pure-closure(...mixed): mixed)`, but found `(closure(int): int)`. [invalid-argument]"]
mir 0.70.1mir 0.70.1 Partly enforced (1/2)
phpdoc_advanced_fallback_pure_closure.php:50: InvalidArgument: Argument $value of acceptsPureClosure() expects 'Closure', got '"strlen"' [MIR0201]
  • Recognition: spelling resolved
  • Enforcement: 1/2 of the expected violations reported
phpantom 0.9.0phpantom_lsp 0.9.0 Unrecognized
phpdoc_advanced_fallback_pure_closure.php:25: Class 'Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure' not found [unknown_class]
phpdoc_advanced_fallback_pure_closure.php:29: Return type Closure is incompatible with declared return type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure [type_mismatch_return]
phpdoc_advanced_fallback_pure_closure.php:37: Class 'Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure' not found [unknown_class]
phpdoc_advanced_fallback_pure_closure.php:47: Argument 1 ($value) expects Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure, got Closure [type_mismatch_argument]
phpdoc_advanced_fallback_pure_closure.php:50: Argument 1 ($value) expects Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure, got 'strlen' [type_mismatch_argument]
phpdoc_advanced_fallback_pure_closure.php:56: Argument 1 ($value) expects Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure, got Closure [type_mismatch_argument]
  • Recognition: spelling not resolved — reported on declaration line(s) 25, 37
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 29: Expected valid value to be accepted, got ["Return type Closure is incompatible with declared return type Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure-closure [type_mismatch_return]"]
Line 47: Expected valid value to be accepted, got ["Argument 1 ($value) expects Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure-closure, got Closure [type_mismatch_argument]"]
intelephense 1.18.5intelephense 1.18.5 Widened to Closure

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/2 of the expected violations reported
phpy 0.2.0phpy 0.2.0 Unrecognized
phpdoc_advanced_fallback_pure_closure.php:25: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure'
phpdoc_advanced_fallback_pure_closure.php:29: Return value of Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure, callable returned
phpdoc_advanced_fallback_pure_closure.php:37: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure'
phpdoc_advanced_fallback_pure_closure.php:47: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure, callable given
phpdoc_advanced_fallback_pure_closure.php:50: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure, string given
phpdoc_advanced_fallback_pure_closure.php:56: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure, callable given
  • Recognition: spelling not resolved — reported on declaration line(s) 25, 37
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 29: Expected valid value to be accepted, got ["Return value of Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\returnsPureClosure() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure-closure, callable returned"]
Line 47: Expected valid value to be accepted, got ["Argument '1' passed to Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsPureClosure() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure-closure, callable given"]
qodana 262.8665.325Qodana 262.8665.325 Unrecognized
phpdoc_advanced_fallback_pure_closure.php:25: Return type in PHPDoc does not match the actual return type [PhpReturnDocTypeMismatchInspection]
phpdoc_advanced_fallback_pure_closure.php:25: Undefined class 'pure-closure' [PhpUndefinedClassInspection]
phpdoc_advanced_fallback_pure_closure.php:29: Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection]
phpdoc_advanced_fallback_pure_closure.php:37: Undefined class 'pure-closure' [PhpUndefinedClassInspection]
phpdoc_advanced_fallback_pure_closure.php:44: Parameter 'returnsPureClosure()' type is not compatible with declaration [PhpParamsInspection]
phpdoc_advanced_fallback_pure_closure.php:47: Parameter 'static fn (int $value): int => $value + 1' type is not compatible with declaration [PhpParamsInspection]
phpdoc_advanced_fallback_pure_closure.php:50: Parameter ''strlen'' type is not compatible with declaration [PhpParamsInspection]
phpdoc_advanced_fallback_pure_closure.php:56: Parameter '$impureClosure' type is not compatible with declaration [PhpParamsInspection]
  • Recognition: spelling not resolved — reported on declaration line(s) 25, 37
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 29: Expected valid value to be accepted, got ["Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection]"]
Line 44: Expected valid value to be accepted, got ["Parameter 'returnsPureClosure()' type is not compatible with declaration [PhpParamsInspection]"]
Line 47: Expected valid value to be accepted, got ["Parameter 'static fn (int $value): int => $value + 1' type is not compatible with declaration [PhpParamsInspection]"]
noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Unrecognized ⚠ 1 false positive
phpdoc_advanced_fallback_pure_closure.php:12: Class or interface named \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure does not exist [undefinedClass]
phpdoc_advanced_fallback_pure_closure.php:37: Class or interface named \Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure does not exist [undefinedClass]
phpdoc_advanced_fallback_pure_closure.php:56: potentially not safe call in function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure signature of param value [notSafeCall]
  • Recognition: spelling not resolved — reported on declaration line(s) 37
  • Enforcement: 1/2 of the expected violations reported — incidental, since the spelling was not resolved
  • False positives: line(s) 12
Expectation diff
Line 12: Unexpected errors ["Class or interface named \\Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\pure-closure does not exist [undefinedClass]"]
steins 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) Enforced
phpdoc_advanced_fallback_pure_closure.php:50: argument "strlen" to acceptsPureClosure() violates declared @param pure-closure $value — declared contract violation [phpdoc.param-mismatch]
phpdoc_advanced_fallback_pure_closure.php:56: callable argument to acceptsPureClosure() violates declared @param pure-closure $value — the bound callable's inferred effect envelope is not pure [phpdoc.param-mismatch]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
phpstan-strict 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 Incidental (2/2)
phpdoc_advanced_fallback_pure_closure.php:29: Function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure() should return pure-Closure but returns Closure(int): int. [identifier=return.type]
phpdoc_advanced_fallback_pure_closure.php:47: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, Closure(int): int given. [identifier=argument.type]
phpdoc_advanced_fallback_pure_closure.php:50: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, 'strlen' given. [identifier=argument.type]
phpdoc_advanced_fallback_pure_closure.php:56: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure expects pure-Closure, Closure(): void given. [identifier=argument.type]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported — incidental, since values the type admits were also rejected on line(s) 29, 47
Expectation diff
Line 29: Expected valid value to be accepted, got ["Function Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\returnsPureClosure() should return pure-Closure but returns Closure(int): int. [identifier=return.type]"]
Line 47: Expected valid value to be accepted, got ["Parameter #1 $value of function Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsPureClosure expects pure-Closure, Closure(int): int given. [identifier=argument.type]"]
psalm-next 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 Unrecognized
phpdoc_advanced_fallback_pure_closure.php:27: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure in docblock for Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure [InvalidDocblock]
phpdoc_advanced_fallback_pure_closure.php:27: Method Conformance\Tests\PhpdocAdvancedFallbackPureClosure\returnsPureClosure does not have a return type, expecting pure-Closure(int):int [MissingReturnType]
phpdoc_advanced_fallback_pure_closure.php:37: Unrecognized type Conformance\Tests\PhpdocAdvancedFallbackPureClosure\pure-closure in docblock for Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsPureClosure [InvalidDocblock]
phpdoc_advanced_fallback_pure_closure.php:39: Parameter $value has no provided type [MissingParamType]
phpdoc_advanced_fallback_pure_closure.php:44: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackPureClosure\acceptsClosure cannot be mixed, expecting impure-Closure [MixedArgument]
  • Recognition: spelling not resolved — reported on declaration line(s) 27, 37, 39
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 44: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackPureClosure\\acceptsClosure cannot be mixed, expecting impure-Closure [MixedArgument]"]

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\PhpdocAdvancedFallbackPureClosure;

/**
 * `pure-closure`
 *
 * A `Closure` with no side effects — `pure-callable` narrowed to the class. The
 * two constraints fail independently: a callable string fails the `Closure`
 * half without any purity analysis, while an echoing closure fails only the
 * half that requires looking inside the body.
 *
 * An analyzer that never infers purity for a closure literal cannot construct a
 * value of this type at all, so it reports the valid probe and the `@return`
 * body as well. That is not an expectation of the test — it shows up under
 * false positives, which is the honest place for "rejects everything".
 *
 * References:
 * - PHPStan TypeNodeResolver `pure-closure` resolves to ClosureType::createPure()
 */

/**
 * @return pure-closure
 */
function returnsPureClosure() // T: pure-closure
{
    return static fn (int $value): int => $value + 1; // V
}

function acceptsClosure(\Closure $value): void
{
}

/**
 * @param pure-closure $value
 */
function acceptsPureClosure($value): void // T: pure-closure
{
}

// A `pure-closure` value always satisfies a native `Closure` parameter.
acceptsClosure(returnsPureClosure()); // V

// A closure that only computes satisfies the parameter.
acceptsPureClosure(static fn (int $value): int => $value + 1); // V

// A callable string is callable, but it is not a Closure.
acceptsPureClosure('strlen'); // E?: a callable-string is not a Closure

// A Closure that echoes fails the other half.
$impureClosure = static function (): void {
    echo 'side effect';
};
acceptsPureClosure($impureClosure); // E?: a closure with side effects is not a pure-closure