diff --git a/src/Rule/ForbidCheckedExceptionInCallableRule.php b/src/Rule/ForbidCheckedExceptionInCallableRule.php index e7b68c6..4d54c98 100644 --- a/src/Rule/ForbidCheckedExceptionInCallableRule.php +++ b/src/Rule/ForbidCheckedExceptionInCallableRule.php @@ -175,7 +175,7 @@ public function processFirstClassCallable( $errors = array_merge($errors, $this->processCall($scope, $callerType, $methodName, $line, $nodeHash)); } - if ($callNode instanceof FuncCall && $callNode->name instanceof Name) { + if ($callNode instanceof FuncCall && $callNode->name instanceof Name && $this->reflectionProvider->hasFunction($callNode->name, $scope)) { $functionReflection = $this->reflectionProvider->getFunction($callNode->name, $scope); $errors = array_merge($errors, $this->processThrowType($functionReflection->getThrowType(), $scope, $line, $nodeHash)); } @@ -427,7 +427,7 @@ private function whitelistAllowedCallables(CallLike $node, Scope $scope): void } elseif ($node instanceof FuncCall && $node->name instanceof Name) { $callerType = null; - $methodReflection = $this->reflectionProvider->getFunction($node->name, $scope); + $methodReflection = $this->getFunctionReflection($node->name, $scope); } elseif ($node instanceof FuncCall && $this->isFirstClassCallableOrClosureOrArrowFunction($node->name)) { // immediately called callable syntax $this->allowedCallables[spl_object_hash($node->name)] = true; @@ -534,4 +534,11 @@ private function buildError( return $builder->build(); } + private function getFunctionReflection(Name $functionName, Scope $scope): ?FunctionReflection + { + return $this->reflectionProvider->hasFunction($functionName, $scope) + ? $this->reflectionProvider->getFunction($functionName, $scope) + : null; + } + } diff --git a/src/Rule/ForbidCustomFunctionsRule.php b/src/Rule/ForbidCustomFunctionsRule.php index 88e4cd3..13a0980 100644 --- a/src/Rule/ForbidCustomFunctionsRule.php +++ b/src/Rule/ForbidCustomFunctionsRule.php @@ -171,6 +171,10 @@ private function validateCallOverExpr(array $methodNames, Expr $expr, Scope $sco */ private function validateMethod(array $methodNames, string $className): array { + if (!$this->reflectionProvider->hasClass($className)) { + return []; + } + $errors = []; foreach ($this->reflectionProvider->getClass($className)->getAncestors() as $ancestor) { diff --git a/src/Rule/ForbidEnumInFunctionArgumentsRule.php b/src/Rule/ForbidEnumInFunctionArgumentsRule.php index 81232b0..ebe474f 100644 --- a/src/Rule/ForbidEnumInFunctionArgumentsRule.php +++ b/src/Rule/ForbidEnumInFunctionArgumentsRule.php @@ -7,6 +7,7 @@ use PhpParser\Node\Name; use PHPStan\Analyser\ArgumentsNormalizer; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\IdentifierRuleError; @@ -86,7 +87,12 @@ public function processNode(Node $node, Scope $scope): array $wrongArguments = []; - $functionReflection = $this->reflectionProvider->getFunction($node->name, $scope); + $functionReflection = $this->getFunctionReflection($node->name, $scope); + + if ($functionReflection === null) { + return []; + } + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs($scope, $node->getArgs(), $functionReflection->getVariants()); $funcCall = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node); @@ -150,4 +156,11 @@ private function containsEnum(Type $type): bool return $type->isEnum()->yes(); } + private function getFunctionReflection(Name $functionName, Scope $scope): ?FunctionReflection + { + return $this->reflectionProvider->hasFunction($functionName, $scope) + ? $this->reflectionProvider->getFunction($functionName, $scope) + : null; + } + } diff --git a/tests/Rule/data/unknown-symbol.php b/tests/Rule/data/unknown-symbol.php new file mode 100644 index 0000000..f12a502 --- /dev/null +++ b/tests/Rule/data/unknown-symbol.php @@ -0,0 +1,17 @@ +aMethod(); + $b->bMethod(); +} + diff --git a/tests/UnknownSymbolTest.php b/tests/UnknownSymbolTest.php new file mode 100644 index 0000000..b50e765 --- /dev/null +++ b/tests/UnknownSymbolTest.php @@ -0,0 +1,58 @@ +runAnalyser([__DIR__ . '/Rule/data/unknown-symbol.php']); + + $internalErrors = []; + + foreach ($errors as $error) { + if ($error->getIdentifier() === 'phpstan.internal') { + $internalErrors[] = $error->getMessage(); + } + } + + self::assertSame([], $internalErrors); + } + + /** + * @param list $filePaths + * @return list + */ + private function runAnalyser(array $filePaths): array + { + $analyser = self::getContainer()->getByType(Analyser::class); // @phpstan-ignore phpstanApi.classConstant + $finalizer = self::getContainer()->getByType(AnalyserResultFinalizer::class); // @phpstan-ignore phpstanApi.classConstant + + $normalizedFilePaths = array_map(fn (string $path): string => $this->getFileHelper()->normalizePath($path), $filePaths); + + $analyserResult = $analyser->analyse($normalizedFilePaths); + $analyserResult = $finalizer->finalize($analyserResult, true, false)->getAnalyserResult(); + + return $analyserResult->getErrors(); + } + +}