Skip to content

Commit d97a47c

Browse files
authored
Fix "types as the only types passed to this method" false positive on generic parameter types (#69)
* Fix reproducer * fix * cs * fix rector
1 parent d4ef55b commit d97a47c

File tree

3 files changed

+49
-20
lines changed

3 files changed

+49
-20
lines changed

src/Collector/ClassMethod/PublicClassMethodParamTypesCollector.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ public function processNode(Node $node, Scope $scope): ?array
7373

7474
$printedParamTypesString = $this->collectorMetadataPrinter->printParamTypesToString(
7575
$node,
76-
$classReflection->getName()
76+
$classReflection,
77+
$scope
7778
);
7879
return [$classReflection->getName(), $methodName, $printedParamTypesString, $node->getLine()];
7980
}

src/Printer/CollectorMetadataPrinter.php

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PhpParser\Node\UnionType;
1616
use PhpParser\PrettyPrinter\Standard;
1717
use PHPStan\Analyser\Scope;
18+
use PHPStan\Reflection\ClassReflection;
1819
use PHPStan\Reflection\ExtendedMethodReflection;
1920
use PHPStan\Reflection\ParametersAcceptorSelector;
2021
use PHPStan\Type\ClosureType;
@@ -88,29 +89,56 @@ public function printArgTypesAsString(
8889
return implode('|', $stringArgTypes);
8990
}
9091

91-
public function printParamTypesToString(ClassMethod $classMethod, string $className): string
92-
{
92+
public function printParamTypesToString(
93+
ClassMethod $classMethod,
94+
ClassReflection $classReflection,
95+
Scope $scope
96+
): string {
97+
$className = $classReflection->getName();
98+
99+
$parametersReflection = [];
100+
if ($classReflection->hasMethod($classMethod->name->name)) {
101+
$methodReflection = $classReflection->getMethod($classMethod->name->name, $scope);
102+
$variants = $methodReflection->getVariants();
103+
if (count($variants) === 1) {
104+
$parametersReflection = $variants[0]->getParameters();
105+
}
106+
}
107+
93108
$printedParamTypes = [];
94-
foreach ($classMethod->params as $param) {
109+
foreach ($classMethod->params as $i => $param) {
95110
if ($param->type === null) {
96111
$printedParamTypes[] = '';
97112
continue;
98113
}
99114

100-
$paramType = $this->transformSelfToClassName($param->type, $className);
101-
if ($paramType instanceof NullableType) {
102-
// unite to phpstan type
103-
$paramType = new UnionType([$paramType->type, new Identifier('null')]);
115+
$phpdocType = null;
116+
if (array_key_exists($i, $parametersReflection)) {
117+
$paramphpdocType = $parametersReflection[$i]->getPhpDocType();
118+
if (! $paramphpdocType instanceof MixedType) {
119+
$phpdocType = $paramphpdocType;
120+
}
104121
}
105122

106-
if ($paramType instanceof UnionType || $paramType instanceof NodeIntersectionType) {
107-
$paramType = $this->resolveSortedTypes($paramType, $className);
108-
}
123+
if ($phpdocType instanceof Type) {
124+
$printedParamType = $this->printTypeToString($phpdocType);
125+
} else {
126+
$paramType = $this->transformSelfToClassName($param->type, $className);
127+
128+
if ($paramType instanceof NullableType) {
129+
// unite to phpstan type
130+
$paramType = new UnionType([$paramType->type, new Identifier('null')]);
131+
}
109132

110-
$printedParamType = $this->standard->prettyPrint([$paramType]);
111-
$printedParamType = str_replace('\Closure', 'callable', $printedParamType);
112-
$printedParamType = ltrim($printedParamType, '\\');
113-
$printedParamType = str_replace('|\\', '|', $printedParamType);
133+
if ($paramType instanceof UnionType || $paramType instanceof NodeIntersectionType) {
134+
$paramType = $this->resolveSortedTypes($paramType, $className);
135+
}
136+
137+
$printedParamType = $this->standard->prettyPrint([$paramType]);
138+
$printedParamType = str_replace('\Closure', 'callable', $printedParamType);
139+
$printedParamType = ltrim($printedParamType, '\\');
140+
$printedParamType = str_replace('|\\', '|', $printedParamType);
141+
}
114142

115143
// to avoid DateTime vs DateTimeImmutable vs DateTimeInterface conflicts
116144
$printedParamType = $this->normalizeDateTime($printedParamType);

tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/Generics/SkipPassedGenerics.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ class User
1515
public function doFoo(GenericA $g):void
1616
{
1717
}
18-
}
1918

20-
/** @param GenericA<string> $g */
21-
function doFoo($g):void {
22-
$user = new User();
23-
$user->doFoo($g);
19+
/** @param GenericA<string> $g */
20+
function doBar($g):void {
21+
$this->doFoo($g);
22+
}
2423
}
24+

0 commit comments

Comments
 (0)