Skip to content

Commit d63cbe4

Browse files
committed
added IgnoreErrorExtension for runtime type validation pattern (function(Type ...$p) {})(...$args)
1 parent 417d50e commit d63cbe4

6 files changed

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

src/Tester/TypeAssert.php

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

55
use PhpParser\Node;
66
use PhpParser\Node\Name;
7+
use PHPStan\Analyser\Analyser;
78
use PHPStan\Analyser\NodeScopeResolver;
89
use PHPStan\Analyser\Scope;
910
use PHPStan\Analyser\ScopeContext;
@@ -71,6 +72,30 @@ static function (Node $node, Scope $scope) use (&$asserts): void {
7172
}
7273

7374

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

0 commit comments

Comments
 (0)