Skip to content

Type confusion regression from #9373 #9968

@lstrojny

Description

@lstrojny

Introduced in ad5bf62

Before: no error

After:

ERROR: MixedArgument - src/Builder/FilterBuilder.php:30:36 - Argument 1 of preg_replace cannot be mixed, expecting array<array-key, string>|string (see https://psalm.dev/030)
        $harmonized = preg_replace(self::ANY_NEWLINE, Newline::UNIX->value, $text);

It’s pretty hard to build a minimal reproduction case because it seems to be only happening if autoloading is involved. So I stripped down the library to a minimal reproduction case and this is the branch: https://github.com/lstrojny/uffff/tree/dev/psalm-bug-repro

It only consists of two files:

src/Builder/FilterBuilder.php

<?php

namespace Uffff\Builder;

class FilterBuilder
{
    /**
     * @return callable(string):string
     */
    public function build(): callable
    {
        return static fn(string $v) => $v;
    }
}

enum Newline: string
{
    case UNIX = "\n";
    case WINDOWS = "\r\n";
    case MAC = "\r";
}

class HarmonizeNewlines
{
    // Order matters, Windows must come first
    private const ANY_NEWLINE = '/(?:' . Newline::WINDOWS->value . '|' . Newline::MAC->value . '|' . Newline::UNIX->value . ')/';

    public function __invoke(string $text): string
    {
        $harmonized = preg_replace(self::ANY_NEWLINE, Newline::UNIX->value, $text);

        assert(is_string($harmonized));

        return $harmonized;
    }
}

src/functions.php:

<?php

declare(strict_types=1);

namespace Foo;

use Uffff\Builder\FilterBuilder;

function unicode(string $text): string
{
    $filter = (new FilterBuilder())->build();

    return $filter($text);
}

The two files are covered by composer autoloading like this:

    "autoload": {
        "psr-4": {
            "Uffff\\": "src/"
        },
        "files": [
            "src/functions.php"
        ]
    },

The issue does not happen if:

  • The namespace changes
  • The FilterBuilder is removed and instead we try to use HarmonizeNewlines directly in functions.php

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions