scheb / tombstone

Dead code detection with tombstones for PHP 🪦🧟
MIT License
260 stars 17 forks source link

Automation to Place Tombstones and Clean-Up Dead Code #11

Open scheb opened 4 years ago

scheb commented 4 years ago

Idea

I'd like to provide some automation to

1) add tombstones to a codebase 2) remove dead code from the codebase based on the results from the tombstone library

Since Rector is the standard library for PHP code migrations now, I thought it would be a good thing to use the Rector platform, rather than building my own little code migrations tool.

In detail, it would need to provide 2 rectors:

1. Setting Tombstones

The first rector would add tombstone() function calls to each public method. There could be potentially additional filtering options, which public method to target.

public function foo() {
    // ... Some code here
}

Result after rector was applied:

public function foo() {
    tombstone();
    // ... Some code here
}

2. Deleting Dead Code

The second rector would take a result from the tombstone library (transfer format TBD), providing the information which methods are detected as "dead code". The rector would remove these dead methods from the code.


If this idea would be useful to you, give it a +1

jawira commented 2 years ago

Maybe you can also add a Rector rule to remove tombstones, for example after deleting dead code we want to remove all tombstones (for performance reasons maybe) and uninstall scheb/tombstone.

scheb commented 2 years ago

@jawira You could easily do that with a regex ;)

michondr commented 1 month ago

hi! currently implementing this library.

for the first type of rector rule this is what I [and chatgpt] came up with:

<?php

declare(strict_types=1);

namespace Utils\Rector\Rector;

use DateTimeImmutable;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

use function array_unshift;

final class AddTombstones extends AbstractRector
{
    private string $now;

    public function __construct()
    {
        $this->now = (new DateTimeImmutable())->format('Y-m-d');
    }

    public function getRuleDefinition(): RuleDefinition
    {
        return new RuleDefinition(
            'add tombstone method call in every class method',
            [
                new CodeSample(
                    <<<'CODE_SAMPLE'
function someFunction()
{
    // Some code
}
CODE_SAMPLE
                    ,
                    <<<'CODE_SAMPLE'
function someFunction()
{
    tombstone('2024-01-01');
    // Some code
}
CODE_SAMPLE
                ),
            ]
        );
    }

    public function getNodeTypes(): array
    {
        return [Function_::class, ClassMethod::class];
    }

    public function refactor(Node $node)
    {
        if ($this->getName($node) === '__construct') {
            return null;
        }
        if ($node->stmts === null) { //interface
            return null;
        }

        $tombstoneCall = new FuncCall(
            new Name('tombstone'),
            [new Arg(new String_($this->now))]
        );

        array_unshift($node->stmts, new Node\Stmt\Expression($tombstoneCall));

        return $node;
    }
}

I'll do some testing and find out a way to fetch log results for the other rules:

I suppose private functions can be removed with some static-analysis tool as they cannot be invoked without being referenced, but reflection.... so that's why I'm adding tombstones even to them