diff --git a/build/phpstan.neon b/build/phpstan.neon index 4454a9d73c..97a076fab0 100644 --- a/build/phpstan.neon +++ b/build/phpstan.neon @@ -23,6 +23,7 @@ parameters: nodesByStringCountMax: 128 checkUninitializedProperties: true checkMissingCallableSignature: true + reportPossiblyNonexistentConstantArrayOffset: true excludePaths: - ../tests/*/data/* - ../tests/tmp/* diff --git a/src/Rules/Functions/PrintfParameterTypeRule.php b/src/Rules/Functions/PrintfParameterTypeRule.php index d91da4590e..68b79d0f00 100644 --- a/src/Rules/Functions/PrintfParameterTypeRule.php +++ b/src/Rules/Functions/PrintfParameterTypeRule.php @@ -8,6 +8,7 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleLevelHelper; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\BooleanType; use PHPStan\Type\ErrorType; use PHPStan\Type\FloatType; @@ -144,6 +145,10 @@ public function processNode(Node $node, Scope $scope): array continue; } + if (!array_key_exists($placeholder->acceptingType, $allowedTypeNameMap)) { + throw new ShouldNotHappenException(); + } + $errors[] = RuleErrorBuilder::message( sprintf( 'Parameter #%d of function %s is expected to be %s by placeholder #%d (%s), %s given.', diff --git a/src/Type/Php/ArgumentBasedFunctionReturnTypeExtension.php b/src/Type/Php/ArgumentBasedFunctionReturnTypeExtension.php index b039b62d00..990d54df47 100644 --- a/src/Type/Php/ArgumentBasedFunctionReturnTypeExtension.php +++ b/src/Type/Php/ArgumentBasedFunctionReturnTypeExtension.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\ArrayType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; @@ -44,6 +45,9 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type { + if (!array_key_exists($functionReflection->getName(), self::FUNCTION_NAMES)) { + throw new ShouldNotHappenException(); + } $argumentPosition = self::FUNCTION_NAMES[$functionReflection->getName()]; if (!isset($functionCall->getArgs()[$argumentPosition])) { diff --git a/src/Type/Php/HashFunctionsReturnTypeExtension.php b/src/Type/Php/HashFunctionsReturnTypeExtension.php index 7798d8f0e8..5ab5eead98 100644 --- a/src/Type/Php/HashFunctionsReturnTypeExtension.php +++ b/src/Type/Php/HashFunctionsReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Php\PhpVersion; use PHPStan\Reflection\FunctionReflection; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\AccessoryLowercaseStringType; use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; use PHPStan\Type\Constant\ConstantBooleanType; @@ -18,6 +19,7 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; +use function array_key_exists; use function array_map; use function count; use function hash_algos; @@ -101,7 +103,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, return null; } - $functionData = self::SUPPORTED_FUNCTIONS[strtolower($functionReflection->getName())]; + $lowerFunctionName = strtolower($functionReflection->getName()); + if (!array_key_exists($lowerFunctionName, self::SUPPORTED_FUNCTIONS)) { + throw new ShouldNotHappenException(); + } + $functionData = self::SUPPORTED_FUNCTIONS[$lowerFunctionName]; if (is_bool($functionData['binary'])) { $binaryType = new ConstantBooleanType($functionData['binary']); } elseif (isset($functionCall->getArgs()[$functionData['binary']])) { diff --git a/src/Type/Php/JsonThrowTypeExtension.php b/src/Type/Php/JsonThrowTypeExtension.php index 19cfcbaaea..bb5d21007c 100644 --- a/src/Type/Php/JsonThrowTypeExtension.php +++ b/src/Type/Php/JsonThrowTypeExtension.php @@ -8,10 +8,12 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\BitwiseFlagHelper; use PHPStan\Type\DynamicFunctionThrowTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use function array_key_exists; use function in_array; #[AutowiredService] @@ -50,6 +52,9 @@ public function getThrowTypeFromFunctionCall( Scope $scope, ): ?Type { + if (!array_key_exists($functionReflection->getName(), self::ARGUMENTS_POSITIONS)) { + throw new ShouldNotHappenException(); + } $argumentPosition = self::ARGUMENTS_POSITIONS[$functionReflection->getName()]; if (!isset($functionCall->getArgs()[$argumentPosition])) { return null; diff --git a/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php b/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php index 7d1242ffea..57e3b76710 100644 --- a/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php +++ b/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\Accessory\AccessoryLowercaseStringType; use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; @@ -196,6 +197,10 @@ private function getSubjectType( Scope $scope, ): ?Type { + if (!array_key_exists($functionReflection->getName(), self::FUNCTIONS_SUBJECT_POSITION)) { + throw new ShouldNotHappenException(); + } + $argumentPosition = self::FUNCTIONS_SUBJECT_POSITION[$functionReflection->getName()]; if (count($functionCall->getArgs()) <= $argumentPosition) { return null; diff --git a/src/Type/Php/StrCaseFunctionsReturnTypeExtension.php b/src/Type/Php/StrCaseFunctionsReturnTypeExtension.php index c965f8cd12..5f3b7f9ecb 100644 --- a/src/Type/Php/StrCaseFunctionsReturnTypeExtension.php +++ b/src/Type/Php/StrCaseFunctionsReturnTypeExtension.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\AccessoryLowercaseStringType; use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; @@ -19,10 +20,10 @@ use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; use function array_diff; +use function array_key_exists; use function array_map; use function count; use function in_array; -use function is_callable; use function mb_check_encoding; use const MB_CASE_LOWER; use const MB_CASE_UPPER; @@ -62,14 +63,15 @@ public function getTypeFromFunctionCall( $fnName = $functionReflection->getName(); $args = $functionCall->getArgs(); - if (count($args) < self::FUNCTIONS[$fnName]) { - return null; + if (!array_key_exists($fnName, self::FUNCTIONS)) { + throw new ShouldNotHappenException(); } + $requiredArgs = self::FUNCTIONS[$fnName]; - $argType = $scope->getType($args[0]->value); - if (!is_callable($fnName)) { + if (count($args) < $requiredArgs) { return null; } + $argType = $scope->getType($args[0]->value); $modes = []; $keepLowercase = false; diff --git a/src/Type/Php/StrContainingTypeSpecifyingExtension.php b/src/Type/Php/StrContainingTypeSpecifyingExtension.php index 2e24c9dcbd..097cad2a63 100644 --- a/src/Type/Php/StrContainingTypeSpecifyingExtension.php +++ b/src/Type/Php/StrContainingTypeSpecifyingExtension.php @@ -15,6 +15,7 @@ use PHPStan\Analyser\TypeSpecifierContext; use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\AccessoryLiteralStringType; use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; @@ -65,7 +66,11 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n $args = $node->getArgs(); if (count($args) >= 2) { - [$hackstackArg, $needleArg] = self::STR_CONTAINING_FUNCTIONS[strtolower($functionReflection->getName())]; + $lowerFunctionName = strtolower($functionReflection->getName()); + if (!array_key_exists($lowerFunctionName, self::STR_CONTAINING_FUNCTIONS)) { + throw new ShouldNotHappenException(); + } + [$hackstackArg, $needleArg] = self::STR_CONTAINING_FUNCTIONS[$lowerFunctionName]; $haystackType = $scope->getType($args[$hackstackArg]->value); $needleType = $scope->getType($args[$needleArg]->value)->toString();