rectorphp/rector · error · ShouldNotHappenException

"%s" is deprecated, as match(true) with nested conditions is

Error message

"%s" is deprecated, as match(true) with nested conditions is often less readable than the original ternary

What it means

NestedTernaryToMatchRector used to rewrite assignments containing nested ternaries into match (true) expressions. It is deprecated because match(true) with nested conditions is often LESS readable than the original ternary chain, especially for non-trivial conditions. refactor() now throws ShouldNotHappenException on every Assign node; the class was kept as a placeholder so deprecated registrations abort loudly.

Source

Thrown at rules/CodingStyle/Rector/Assign/NestedTernaryToMatchRector.php:56

        };
    }
}
CODE_SAMPLE
)]);
    }
    /**
     * @return array<class-string<Node>>
     */
    public function getNodeTypes(): array
    {
        return [Assign::class];
    }
    /**
     * @param Assign $node
     */
    public function refactor(Node $node): ?Assign
    {
        throw new ShouldNotHappenException(sprintf('"%s" is deprecated, as match(true) with nested conditions is often less readable than the original ternary', self::class));
    }
}

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Remove NestedTernaryToMatchRector::class from rector.php and imported sets, then re-run vendor/bin/rector dry-run
  2. Convert genuinely hairy nested ternaries to match (true) manually, only where it improves clarity
  3. Alternatively flatten deep ternaries into if/elseif or a lookup map, case by case
  4. grep -r NestedTernaryToMatchRector across configs to eliminate every registration

Example fix

// before (rector.php)
->withRules([
    \Rector\CodingStyle\Rector\Assign\NestedTernaryToMatchRector::class,
]);
// code:
$level = $score > 90 ? 'high' : ($score > 50 ? 'mid' : 'low');

// after (rector.php)
// rule removed
// code rewritten by hand where it helps:
$level = match (true) {
    $score > 90 => 'high',
    $score > 50 => 'mid',
    default => 'low',
};
Defensive patterns

Strategy: validation

Validate before calling

$deprecated = ['NestedTernaryToMatchRector'];
foreach (glob(__DIR__ . '/rector*.php') as $config) {
    $src = file_get_contents($config);
    foreach ($deprecated as $rule) {
        if (str_contains($src, $rule)) {
            fwrite(STDERR, "Remove deprecated rule {$rule} from {$config}\n");
            exit(1);
        }
    }
}

Prevention

When it happens

Trigger: Registering rules/CodingStyle/Rector/Assign/NestedTernaryToMatchRector.php in config and running rector over any file with an assignment statement (the rule matches all Assign nodes, so effectively every file). The throw comes from rules/CodingStyle/Rector/Assign/NestedTernaryToMatchRector.php:56.

Common situations: A rector.php with a hand-tuned CodingStyle rule list, or an imported set package, still names NestedTernaryToMatchRector after upgrading Rector. Because Assign matches almost every PHP file, the exception surfaces on the first file processed and is frequently misread as a Rector internals bug.

Related errors


AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21). Data as JSON: /api/errors/67ae4d9e53891b76. Report an issue: GitHub.