Skip to content

[TypeDeclarationDocblocks] Skip contradicting @param array when local call type conflicts with default value - #8381

Merged
TomasVotruba merged 1 commit into
mainfrom
fix-array-docblock-param-default-contradiction
Aug 26, 2026
Merged

[TypeDeclarationDocblocks] Skip contradicting @param array when local call type conflicts with default value#8381
TomasVotruba merged 1 commit into
mainfrom
fix-array-docblock-param-default-contradiction

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

Fixes rectorphp/rector#9864

ClassMethodArrayDocblockParamFromLocalCallsRector inferred a @param from local call sites that could contradict the callee's own default value.

When an argument is a plain array variable, PHPStan flow-narrows it (e.g. after $row['state'] && ...) to non-empty-array&hasOffsetValue(...). That narrowed type leaked into the generated docblock, producing a non-empty-array component on a param that declares = [] - self-contradictory, and a source of PHPStan false positives (notably with #[\Override]).

Before

class Minimal
{
    public function callee(array $row = [])
    {
        return $row;
    }

    public function caller(array $row)
    {
        return $row['state'] && $this->callee($row);
    }

    public function caller2()
    {
        return $this->callee(['state' => 5, 'name' => 'x']);
    }
}
 class Minimal
 {
+    /**
+     * @param non-empty-array|array<string, int>|array<string, string> $row
+     */
     public function callee(array $row = [])

After

The default value is united into the inferred type, so the non-empty-array narrowing collapses to a plain mixed array, which is then correctly recognized as valueless and skipped - no docblock added.

Genuinely useful cases are unaffected: a literal call still narrows a defaulted param.

public function go()
{
    $this->run(['item1', 'item2']);
}

private function run(array $items = [])
{
}
+    /**
+     * @param string[] $items
+     */
     private function run(array $items = [])

Changes

  • ClassMethodArrayDocblockParamFromLocalCallsRector - unite the resolved param type with the default value type so the docblock can never contradict the signature.
  • NodeDocblockTypeDecorator::isArrayMixed() - also treat a mixed-keyed mixed array as valueless (previously only integer-keyed), so the collapsed mixed[] is skipped instead of emitted.
  • Two fixtures: the skipped contradiction case, and a defaulted param that still narrows from a literal call.

@TomasVotruba
TomasVotruba merged commit 22353e2 into main Aug 26, 2026
44 checks passed
@TomasVotruba
TomasVotruba deleted the fix-array-docblock-param-default-contradiction branch August 26, 2026 07:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect behavior of ClassMethodArrayDocblockParamFromLocalCallsRector

1 participant