rectorphp/rector · error · ShouldNotHappenException

"%s" rule is deprecated, as removing an annotation by name i

Error message

"%s" rule is deprecated, as removing an annotation by name is a coding standard concern, not an upgrade path; use a coding standard tool instead

What it means

RemoveAnnotationRector used to strip named annotations (e.g. a given @annotation tag) from classes, functions, properties and constants. It is deprecated because deleting an annotation by name is a coding-standard concern, not an upgrade path — Rector is scoped to migrations and quality refactors, not docblock hygiene. refactor() now throws ShouldNotHappenException on every matched node, and its configure() is an empty stub, so configuring annotation names no longer prevents the throw.

Source

Thrown at rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php:51

final class SomeClass
{
}
CODE_SAMPLE
, ['method'])]);
    }
    /**
     * @return array<class-string<Node>>
     */
    public function getNodeTypes(): array
    {
        return [ClassLike::class, FunctionLike::class, Property::class, ClassConst::class];
    }
    /**
     * @param ClassLike|FunctionLike|Property|ClassConst $node
     */
    public function refactor(Node $node): ?Node
    {
        throw new ShouldNotHappenException(sprintf('"%s" rule is deprecated, as removing an annotation by name is a coding standard concern, not an upgrade path; use a coding standard tool instead', self::class));
    }
    /**
     * @param mixed[] $configuration
     */
    public function configure(array $configuration): void
    {
    }
}

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Remove RemoveAnnotationRector::class (and its ->configure() call) from rector.php / imported sets and re-run dry-run
  2. Strip the annotations with a coding-standard or docblock tool (PHP-CS-Fixer custom fixer, PHPStan-PHPDoc tooling, or a one-off script)
  3. Do a project-wide search-and-replace (regex over docblocks) for the specific tag name in a dedicated commit
  4. If tag removal must be automated per project, write a custom Rector rule that deletes the phpdoc node

Example fix

// before (rector.php)
->withRules([
    \Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector::class,
])
->withConfiguredRule(
    \Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector::class,
    ['annotation_to_remove' => ['author']]
);

// after
// rule + configuration removed from rector.php
// one-off regex instead: /**\n \* @author .*\n */  -> delete via script
Defensive patterns

Strategy: validation

Validate before calling

$deprecated = ['RemoveAnnotationRector'];
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/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php (often together with ->configure(['annotation_to_remove' => ...])) and running rector over any file with a class, function, property or class constant. The throw fires at rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php:51 immediately.

Common situations: Projects removing a framework-specific tag (e.g. @deprecated-era @internal sweeps, PHPUnit @expectedException cleanups) kept this rule in rector.php; after upgrading, both the rule AND its configuration silently stopped working, then started throwing. Config files copied between microservices spread the issue.

Related errors


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