Skip to content

Commit b98f8da

Browse files
committed
Better union method call handling
1 parent eae5974 commit b98f8da

File tree

2 files changed

+60
-10
lines changed

2 files changed

+60
-10
lines changed

src/Rules/ThrowsPhpDocRule.php

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@
2323
use PHPStan\Rules\Rule;
2424
use PHPStan\Type\ObjectType;
2525
use PHPStan\Type\Type;
26+
use PHPStan\Type\TypeCombinator;
2627
use PHPStan\Type\TypeUtils;
2728
use ReflectionMethod;
2829
use function array_diff;
2930
use function array_filter;
3031
use function array_map;
31-
use function array_merge;
32-
use function array_unique;
3332
use function count;
3433
use function is_string;
3534
use function sprintf;
@@ -113,25 +112,31 @@ public function enableCallPropagation(): Rule
113112
$targetType = $scope->getType($node->var);
114113
$targetClassNames = TypeUtils::getDirectClassNames($targetType);
115114

116-
$messages = [];
115+
$throwTypes = [];
117116
foreach ($targetClassNames as $targetClassName) {
118117
$targetClassReflection = $this->broker->getClass($targetClassName);
119118
if (!$targetClassReflection->hasMethod($methodName->toString())) {
120-
return [];
119+
continue;
121120
}
122121

123122
$targetMethodReflection = $targetClassReflection->getMethod($methodName->toString(), $scope);
124-
125123
if (!$targetMethodReflection instanceof ThrowableReflection) {
126-
return [];
124+
continue;
125+
}
126+
127+
$throwType = $targetMethodReflection->getThrowType();
128+
if ($throwType === null) {
129+
continue;
127130
}
128131

129-
$messages = array_merge($messages, $this->processThrowsTypes(
130-
$targetMethodReflection->getThrowType()
131-
));
132+
$throwTypes[] = $throwType;
133+
}
134+
135+
if (count($throwTypes) === 0) {
136+
return [];
132137
}
133138

134-
return array_unique($messages);
139+
return $this->processThrowsTypes(TypeCombinator::union(...$throwTypes));
135140
});
136141
}
137142

tests/src/Rules/data/throws-annotations.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ abstract class BaseRuntimeException extends RuntimeException {}
1111
abstract class BaseBlacklistedRuntimeException extends BaseRuntimeException {}
1212
class SomeRuntimeException extends BaseRuntimeException {}
1313
class NextRuntimeException extends BaseRuntimeException {}
14+
class ConcreteNextRuntimeException extends NextRuntimeException {}
1415
class SomeBlacklistedRuntimeException extends BaseRuntimeException {}
1516
class SomeInheritedBlacklistedRuntimeException extends BaseBlacklistedRuntimeException {}
1617
class CheckedException extends Exception {}
@@ -130,6 +131,16 @@ public function createNewInstance(): void
130131
new ThrowInConstructor(); // error: Missing @throws RuntimeException annotation
131132
}
132133

134+
/**
135+
* @throws NextRuntimeException
136+
*/
137+
public function callUnion(): void
138+
{
139+
/** @var UnionOne|UnionTwo|UnionThree $union */
140+
$union = getUnion();
141+
$union->foo(); // error: Missing @throws Pepakriz\PHPStanExceptionRules\Rules\Data\SomeRuntimeException annotation
142+
}
143+
133144
}
134145

135146
class ThrowInConstructor
@@ -145,6 +156,40 @@ public function __construct()
145156

146157
}
147158

159+
class UnionOne {
160+
161+
/**
162+
* @throws SomeRuntimeException
163+
*/
164+
public function foo(): void
165+
{
166+
throw new SomeRuntimeException();
167+
}
168+
169+
}
170+
171+
class UnionTwo {
172+
173+
/**
174+
* @throws NextRuntimeException
175+
*/
176+
public function foo(): void
177+
{
178+
throw new NextRuntimeException();
179+
}
180+
}
181+
182+
class UnionThree {
183+
184+
/**
185+
* @throws ConcreteNextRuntimeException
186+
*/
187+
public function foo(): void
188+
{
189+
throw new ConcreteNextRuntimeException();
190+
}
191+
}
192+
148193
class Issue6
149194
{
150195

0 commit comments

Comments
 (0)