Skip to content

Commit c6a7e72

Browse files
committed
added IgnoreErrorExtension for runtime type validation pattern (function(Type ...$p) {})(...$args)
1 parent 2d29643 commit c6a7e72

6 files changed

Lines changed: 107 additions & 0 deletions

File tree

extension.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ parameters:
22

33
includes:
44
- extension-narrowReturnType.neon
5+
6+
services:
7+
-
8+
class: Nette\PHPStan\Analyser\TypeValidationCallIgnoreExtension
9+
tags: [phpstan.ignoreErrorExtension]

phpstan.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ parameters:
1313
-
1414
identifiers: [phpstanApi.method]
1515
path: src/Type/NarrowReturnTypeResolver.php
16+
-
17+
identifiers: [phpstanApi.class, phpstanApi.method]
18+
path: src/Analyser/TypeValidationCallIgnoreExtension.php
19+
-
20+
identifiers: [phpstanApi.classConstant, phpstanApi.method]
21+
path: src/Testing/TypeAssert.php
1622

1723
includes:
1824
- extension.neon
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nette\PHPStan\Analyser;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\Closure;
9+
use PhpParser\Node\Expr\FuncCall;
10+
use PHPStan\Analyser\Error;
11+
use PHPStan\Analyser\IgnoreErrorExtension;
12+
use PHPStan\Analyser\Scope;
13+
use PHPStan\Node\NoopExpressionNode;
14+
15+
16+
/**
17+
* Suppresses 'expr.resultUnused' for the runtime type validation pattern (function(Type ...$p) {})(...$args).
18+
*/
19+
class TypeValidationCallIgnoreExtension implements IgnoreErrorExtension
20+
{
21+
public function shouldIgnore(Error $error, Node $node, Scope $scope): bool
22+
{
23+
if ($error->getIdentifier() !== 'expr.resultUnused') {
24+
return false;
25+
}
26+
27+
if (!$node instanceof NoopExpressionNode) {
28+
return false;
29+
}
30+
31+
$expr = $node->getOriginalExpr();
32+
if (!$expr instanceof FuncCall || !$expr->name instanceof Closure) {
33+
return false;
34+
}
35+
36+
$closure = $expr->name;
37+
if ($closure->stmts !== [] || $closure->params === []) {
38+
return false;
39+
}
40+
41+
foreach ($closure->params as $param) {
42+
if (!$param->variadic || $param->type === null) {
43+
return false;
44+
}
45+
}
46+
47+
return true;
48+
}
49+
}

src/Testing/TypeAssert.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use PhpParser\Node;
88
use PhpParser\Node\Name;
9+
use PHPStan\Analyser\Analyser;
910
use PHPStan\Analyser\NodeScopeResolver;
1011
use PHPStan\Analyser\Scope;
1112
use PHPStan\Analyser\ScopeContext;
@@ -73,6 +74,30 @@ static function (Node $node, Scope $scope) use (&$asserts): void {
7374
}
7475

7576

77+
/**
78+
* Analyses a PHP file and verifies that PHPStan reports no errors.
79+
* @param list<string> $configFiles
80+
*/
81+
public static function assertNoErrors(string $file, array $configFiles = []): void
82+
{
83+
$container = self::createContainer($configFiles);
84+
85+
$fileHelper = $container->getByType(FileHelper::class);
86+
$file = $fileHelper->normalizePath($file);
87+
88+
$container->getService('pathRoutingParser')->setAnalysedFiles([$file]);
89+
90+
$analyser = $container->getByType(Analyser::class);
91+
$result = $analyser->analyse([$file]);
92+
93+
$errors = array_map(
94+
static fn($e) => $e->getIdentifier() . ' on line ' . $e->getLine(),
95+
$result->getErrors(),
96+
);
97+
Assert::same([], $errors);
98+
}
99+
100+
76101
/**
77102
* @return array{expected: string, actual: string, line: int}|null
78103
*/
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
require __DIR__ . '/bootstrap.php';
6+
7+
use Nette\PHPStan\Testing\TypeAssert;
8+
9+
TypeAssert::assertNoErrors(__DIR__ . '/data/type-validation-call.php', [__DIR__ . '/../extension.neon']);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
6+
class Foo
7+
{
8+
/** @param string[] $items */
9+
public function setItems(array $items): void
10+
{
11+
(function (string ...$items) {})(...$items);
12+
}
13+
}

0 commit comments

Comments
 (0)