Skip to content

Commit

Permalink
Expose param name for constructor checks
Browse files Browse the repository at this point in the history
  • Loading branch information
erickskrauch committed Oct 2, 2023
1 parent 1d47a1c commit 64c19ab
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 15 deletions.
14 changes: 8 additions & 6 deletions src/Rule/YiiConfigHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,21 +186,24 @@ public function validateConstructorArgs(Type $object, ConstantArrayType $config,

/** @var list<\PHPStan\Rules\IdentifierRuleError> $paramSearchErrors */
$paramSearchErrors = [];
/** @var \PHPStan\Reflection\ParameterReflection[] $foundParams */
/** @var list<array{class-string, \PHPStan\Reflection\ParameterReflection, string}> $foundParams */
$foundParams = [];
/** @var \PHPStan\Reflection\ClassReflection $classReflection */
foreach ($object->getObjectClassReflections() as $classReflection) {
$constructorParams = ParametersAcceptorSelector::selectSingle($classReflection->getConstructor()->getVariants())->getParameters();
$param = null;
$paramIndex = 0;

// TODO: prevent direct pass of 'config' param to constructor args (\yii\base\Configurable)

if (is_int($paramName)) {
$param = $constructorParams[$paramName] ?? null;
$paramIndex = $paramName;
} else {
foreach ($constructorParams as $constructorParam) {
foreach ($constructorParams as $j => $constructorParam) {
if ($constructorParam->getName() === $paramName) {
$param = $constructorParam;
$paramIndex = $j;
break;
}
}
Expand All @@ -217,7 +220,7 @@ public function validateConstructorArgs(Type $object, ConstantArrayType $config,
continue;
}

$foundParams[$classReflection->getName()] = $param;
$foundParams[] = [$classReflection->getName(), $param, sprintf('#%s $%s', $paramIndex + 1, $param->getName())];
}

if (empty($foundParams)) {
Expand All @@ -239,15 +242,14 @@ public function validateConstructorArgs(Type $object, ConstantArrayType $config,
/** @var list<\PHPStan\Rules\IdentifierRuleError> $typeCheckErrors */
$typeCheckErrors = [];
$accepted = false;
foreach ($foundParams as $className => $constructorParam) {
// TODO: expose param name
foreach ($foundParams as [$className, $constructorParam, $strToReport]) {
$paramType = $constructorParam->getType();
$result = $this->ruleLevelHelper->acceptsWithReason($paramType, $paramValue, $scope->isDeclareStrictTypes());
if (!$result->result) {
$level = VerbosityLevel::getRecommendedLevelByType($paramType, $paramValue);
$typeCheckErrors[] = RuleErrorBuilder::message(sprintf(
'Parameter %s of class %s constructor expects %s, %s given.',
$paramStrToReport,
$strToReport,
$className,
$paramType->describe($level),
$paramValue->describe($level),
Expand Down
18 changes: 9 additions & 9 deletions tests/Rule/CreateObjectRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ public function testRule(): void {
$this->analyse([__DIR__ . '/_data/create_object_array_valid.php'], []);
$this->analyse([__DIR__ . '/_data/create_object_array_union_type_valid.php'], []);
$this->analyse([__DIR__ . '/_data/create_object_classname_invalid.php'], [
['Parameter #1 of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 6],
['Parameter #2 of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 6],
['Parameter $stringArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 7],
['Parameter $intArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 7],
['Parameter #1 $stringArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 6],
['Parameter #2 $intArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 6],
['Parameter #1 $stringArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 7],
['Parameter #2 $intArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 7],
]);
$this->analyse([__DIR__ . '/_data/create_object_array_invalid.php'], [
['Parameter $stringArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 6],
['Parameter $intArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 6],
['Parameter #1 $stringArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 6],
['Parameter #2 $intArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 6],
['Property ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent::$publicStringProp (string) does not accept int.', 6],
[
'Property ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent::$publicArrayProp (array{key: string}) does not accept array{key: false}.',
Expand All @@ -46,15 +46,15 @@ public function testRule(): void {
['Property ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent::$readonlyFunctionProp is not writable.', 6],
]);
$this->analyse([__DIR__ . '/_data/create_object_array_with_indexed_args.php'], [
['Parameter #1 of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 6],
['Parameter #2 of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 6],
['Parameter #1 $stringArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects string, int given.', 6],
['Parameter #2 $intArg of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\MyComponent constructor expects int, string given.', 6],
]);
$this->analyse([__DIR__ . '/_data/create_object_array_with_missing_class.php'], [
['Configuration params array must have "class" or "__class" key', 4],
]);
$this->analyse([__DIR__ . '/_data/create_object_array_union_type_invalid.php'], [
["The config for ErickSkrauch\PHPStan\Yii2\Tests\Yii\Article|ErickSkrauch\PHPStan\Yii2\Tests\Yii\Comment is wrong: the property field doesn't exists", 10],
['Parameter #1 of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\BarComponent constructor expects DateTimeInterface, string given.', 17],
['Parameter #1 $dateTime of class ErickSkrauch\PHPStan\Yii2\Tests\Yii\BarComponent constructor expects DateTimeInterface, string given.', 17],
]);
}

Expand Down

0 comments on commit 64c19ab

Please sign in to comment.