Skip to content

Commit 1fa5521

Browse files
phpstan-botclaude
andcommitted
Make TypeTraverserMapMemoizedRule a regular rule
TypeTraverser::map() and mapMemoized() are @api, so extensions get the same guidance. Tests keep using map() where they exercise it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 580f63b commit 1fa5521

6 files changed

Lines changed: 12 additions & 18 deletions

File tree

‎build/phpstan.neon‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ parameters:
8181
- '#^Dynamic call to static method PHPUnit\\Framework\\\S+\(\)\.$#'
8282
- '#should be contravariant with parameter \$node \(PhpParser\\Node\) of method PHPStan\\Rules\\Rule<PhpParser\\Node>::processNode\(\)$#'
8383
- '#Variable property access on PhpParser\\Node#'
84+
-
85+
identifier: phpstanApi.typeTraverserMap
86+
path: ../tests/* # tests exercise TypeTraverser::map() itself
87+
reportUnmatched: false
8488
-
8589
identifier: shipmonk.deadMethod
8690
message: '#^Unused .*?::__construct#' # likely used in DIC
@@ -208,7 +212,6 @@ rules:
208212
- PHPStan\Build\MemoizationPropertyRule
209213
- PHPStan\Build\OrChainIdenticalComparisonToInArrayRule
210214
- PHPStan\Build\NoPhpVersionInjectionInScopeAwareExtensionRule
211-
- PHPStan\Build\TypeTraverserMapMemoizedRule
212215

213216
services:
214217
-

build/PHPStan/Build/TypeTraverserMapMemoizedRule.php renamed to src/Rules/Api/TypeTraverserMapMemoizedRule.php

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php declare(strict_types = 1);
22

3-
namespace PHPStan\Build;
3+
namespace PHPStan\Rules\Api;
44

55
use PhpParser\Node;
66
use PhpParser\Node\Expr;
@@ -28,21 +28,19 @@
2828
use PhpParser\Node\Stmt\Unset_;
2929
use PhpParser\NodeFinder;
3030
use PHPStan\Analyser\Scope;
31-
use PHPStan\File\FileHelper;
31+
use PHPStan\DependencyInjection\RegisteredRule;
3232
use PHPStan\Rules\IdentifierRuleError;
3333
use PHPStan\Rules\Rule;
3434
use PHPStan\Rules\RuleErrorBuilder;
3535
use PHPStan\Type\TypeTraverser;
3636
use function array_unique;
3737
use function array_values;
3838
use function count;
39-
use function dirname;
4039
use function implode;
4140
use function in_array;
4241
use function is_string;
4342
use function spl_object_id;
4443
use function sprintf;
45-
use function str_starts_with;
4644

4745
/**
4846
* Keeps the choice between TypeTraverser::map() and TypeTraverser::mapMemoized()
@@ -64,13 +62,10 @@
6462
*
6563
* @implements Rule<StaticCall>
6664
*/
65+
#[RegisteredRule(level: 0)]
6766
final class TypeTraverserMapMemoizedRule implements Rule
6867
{
6968

70-
public function __construct(private FileHelper $fileHelper, private bool $skipTests = true)
71-
{
72-
}
73-
7469
public function getNodeType(): string
7570
{
7671
return StaticCall::class;
@@ -87,10 +82,6 @@ public function processNode(Node $node, Scope $scope): array
8782
return [];
8883
}
8984

90-
if ($this->skipTests && str_starts_with($this->fileHelper->normalizePath($scope->getFile()), $this->fileHelper->normalizePath(dirname(__DIR__, 3) . '/tests'))) {
91-
return [];
92-
}
93-
9485
// the result is not used when the call is a statement of its own;
9586
// the body of an arrow function also counts as one, which errs towards allowing mapMemoized()
9687
$error = $this->checkCall($node, $scope->isInFirstLevelStatement());
@@ -123,7 +114,7 @@ private function checkCall(StaticCall $call, bool $isResultUnused): ?IdentifierR
123114
'Callback of TypeTraverser::mapMemoized() %s, so it might depend on where or how many times a Type instance occurs. Use TypeTraverser::map() instead.',
124115
implode(', ', $reasons),
125116
))
126-
->identifier('phpstan.typeTraverserMapMemoized')
117+
->identifier('phpstanApi.typeTraverserMapMemoized')
127118
->line($call->getStartLine())
128119
->build();
129120
}
@@ -133,7 +124,7 @@ private function checkCall(StaticCall $call, bool $isResultUnused): ?IdentifierR
133124
}
134125

135126
return RuleErrorBuilder::message('Callback of TypeTraverser::map() does not depend on where or how many times a Type instance occurs. Use TypeTraverser::mapMemoized() instead.')
136-
->identifier('phpstan.typeTraverserMap')
127+
->identifier('phpstanApi.typeTraverserMap')
137128
->line($call->getStartLine())
138129
->fixNode($call, static function (StaticCall $node): StaticCall {
139130
$node->name = new Identifier('mapMemoized');

‎src/Type/TypeTraverser.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public static function map(Type $type, TypeTraverserCallable|callable $cb): Type
6262
* Only for callbacks whose result and side effects do not depend on
6363
* where in the traversed type, or how many times, the type occurs.
6464
*
65+
* @api
6566
* @param TypeTraverserCallable|callable(Type $type, callable(Type): Type $traverse): Type $cb
6667
*/
6768
public static function mapMemoized(Type $type, TypeTraverserCallable|callable $cb): Type

tests/PHPStan/Build/TypeTraverserMapMemoizedRuleTest.php renamed to tests/PHPStan/Rules/Api/TypeTraverserMapMemoizedRuleTest.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<?php declare(strict_types = 1);
22

3-
namespace PHPStan\Build;
3+
namespace PHPStan\Rules\Api;
44

5-
use PHPStan\File\FileHelper;
65
use PHPStan\Rules\Rule;
76
use PHPStan\Testing\RuleTestCase;
87

@@ -14,7 +13,7 @@ final class TypeTraverserMapMemoizedRuleTest extends RuleTestCase
1413

1514
protected function getRule(): Rule
1615
{
17-
return new TypeTraverserMapMemoizedRule(self::getContainer()->getByType(FileHelper::class), false);
16+
return new TypeTraverserMapMemoizedRule();
1817
}
1918

2019
public function testRule(): void

tests/PHPStan/Build/data/type-traverser-map-memoized.php renamed to tests/PHPStan/Rules/Api/data/type-traverser-map-memoized.php

File renamed without changes.

tests/PHPStan/Build/data/type-traverser-map-memoized.php.fixed renamed to tests/PHPStan/Rules/Api/data/type-traverser-map-memoized.php.fixed

File renamed without changes.

0 commit comments

Comments
 (0)