Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,15 @@ public function process(File $phpcsFile, $stackPointer): void

$docBlockStartIndex = $tokens[$docBlockEndIndex]['comment_opener'];

$paramCount = 0;
// Index signature entries by parameter name so partial or out-of-order
// @param lists don't cause positional mismatches, which previously
// produced false-positive "missing type" fixes that fought
// DocBlockParamTypeMismatchSniff in an infinite fixer loop.
$signatureByName = [];
foreach ($methodSignature as $sigEntry) {
$signatureByName[$sigEntry['variable']] = $sigEntry;
}

for ($i = $docBlockStartIndex + 1; $i < $docBlockEndIndex; $i++) {
if ($tokens[$i]['type'] !== 'T_DOC_COMMENT_TAG') {
continue;
Expand All @@ -65,12 +73,6 @@ public function process(File $phpcsFile, $stackPointer): void
continue;
}

if (empty($methodSignature[$paramCount])) {
continue;
}
$methodSignatureValue = $methodSignature[$paramCount];
$paramCount++;

$classNameIndex = $i + 2;

if ($tokens[$classNameIndex]['type'] !== 'T_DOC_COMMENT_STRING') {
Expand All @@ -83,15 +85,21 @@ public function process(File $phpcsFile, $stackPointer): void
continue;
}

if (empty($methodSignatureValue['typehint']) && empty($methodSignatureValue['default'])) {
continue;
}

/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode $valueNode */
$valueNode = static::getValueNode($tokens[$i]['content'], $content);
if ($valueNode instanceof InvalidTagValueNode || $valueNode instanceof TypelessParamTagValueNode) {
return;
}

if (!isset($signatureByName[$valueNode->parameterName])) {
continue;
}
$methodSignatureValue = $signatureByName[$valueNode->parameterName];

if (empty($methodSignatureValue['typehint']) && empty($methodSignatureValue['default'])) {
continue;
}

$parts = $this->valueNodeParts($valueNode);

// We skip for mixed
Expand Down
14 changes: 14 additions & 0 deletions tests/_data/DocBlockParamAllowDefaultValue/after.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,18 @@ public function multipleUnion(int|string|null $value): void
public function arrayShape(array $array = null): void
{
}

/**
* Partial doc block - only $row and $errors are documented, skipping intermediate
* parameters. Positional matching would wrongly map docblock's $errors to the $accountId
* signature slot and try to add `int` to the list<string> type, fighting
* DocBlockParamTypeMismatchSniff in an infinite fixer loop. Must be matched by name.
*
* @param array<string, string> $row
* @param list<string> $errors
* @return void
*/
public function partialDocBlock(array $row, int $accountId, array &$errors): void
{
}
}
14 changes: 14 additions & 0 deletions tests/_data/DocBlockParamAllowDefaultValue/before.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,18 @@ public function multipleUnion(int|string|null $value): void
public function arrayShape(array $array = null): void
{
}

/**
* Partial doc block - only $row and $errors are documented, skipping intermediate
* parameters. Positional matching would wrongly map docblock's $errors to the $accountId
* signature slot and try to add `int` to the list<string> type, fighting
* DocBlockParamTypeMismatchSniff in an infinite fixer loop. Must be matched by name.
*
* @param array<string, string> $row
* @param list<string> $errors
* @return void
*/
public function partialDocBlock(array $row, int $accountId, array &$errors): void
{
}
}
Loading