4

I'm trying to use the psalm static analysis tool for PHP. It's my understanding that this tool can tell me about unused methods in my codebase. However, if I create a simple test file

#File: src/test.php
<?php
class A {
    private function foo() : void {}
}

new A();

and then run psalm

$ ./vendor/bin/psalm --find-dead-code src/test.php 
Scanning files...
Analyzing files...

------------------------------
No errors found!
------------------------------

Checks took 0.16 seconds and used 32.694MB of memory
Psalm was able to infer types for 100% of the codebase

or psalter,

$ ./vendor/bin/psalter --find-unused-code --dry-run --issues=UnusedMethod src/test.php 
Scanning files...
Analyzing files...

------------------------------
No errors found!
------------------------------

Checks took 0.05 seconds and used 29.214MB of memory
Psalm was able to infer types for 100% of the codebase

no errors are found.

Why isn't psalm finding the unused method foo? Is there extra configuration that's needed? Or do I misunderstand what this tool does? My psalm.xml file is below.

<?xml version="1.0"?>
<psalm
    totallyTyped="false"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="https://getpsalm.org/schema/config"
    xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
    <projectFiles>
        <directory name="src" />
        <ignoreFiles>
            <directory name="vendor" />
        </ignoreFiles>
    </projectFiles>

    <issueHandlers>
        <LessSpecificReturnType errorLevel="info" />

        <!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->

        <DeprecatedMethod errorLevel="info" />
        <DeprecatedProperty errorLevel="info" />
        <DeprecatedClass errorLevel="info" />
        <DeprecatedConstant errorLevel="info" />
        <DeprecatedInterface errorLevel="info" />
        <DeprecatedTrait errorLevel="info" />

        <InternalMethod errorLevel="info" />
        <InternalProperty errorLevel="info" />
        <InternalClass errorLevel="info" />

        <MissingClosureReturnType errorLevel="info" />
        <MissingReturnType errorLevel="info" />
        <MissingPropertyType errorLevel="info" />
        <InvalidDocblock errorLevel="info" />
        <MisplacedRequiredParam errorLevel="info" />

        <PropertyNotSetInConstructor errorLevel="info" />
        <MissingConstructor errorLevel="info" />
        <MissingClosureParamType errorLevel="info" />
        <MissingParamType errorLevel="info" />

        <RedundantCondition errorLevel="info" />

        <DocblockTypeContradiction errorLevel="info" />
        <RedundantConditionGivenDocblockType errorLevel="info" />

        <UnresolvableInclude errorLevel="info" />

        <RawObjectIteration errorLevel="info" />

        <InvalidStringClass errorLevel="info" />

        <UnusedMethod errorLevel="info" />
    </issueHandlers>
</psalm>
Alana Storm
  • 164,128
  • 91
  • 395
  • 599

2 Answers2

7

Psalm creator here - dead code detection only detects unused classes and methods when the entire project is analysed - e.g. ./vendor/bin/psalm --find-dead-code, omitting src/test.php.

While private methods and properties are a special case (their non-use can be inferred without checking the entire project), for public/protected methods and properties everything must be consumed.

  • `--find-dead-code=always` will skip that heuristic and enable full dead code analysis even when a file name is passed on the command line. You may want to add this to you answer. – weirdan Apr 30 '19 at 21:40
2

According to the documentation, you'll want to use the --find-dead-code argument to psalm:

./vendor/bin/psalm --find-dead-code foo.php

Output:

Scanning files...
Analyzing files...

ERROR: UnusedVariable - foo.php:6:1 - Variable $a is never referenced
$a = new A();


------------------------------
1 errors found
------------------------------

Checks took 0.27 seconds and used 67.096MB of memory
Psalm was able to infer types for 100% of the codebase
miken32
  • 42,008
  • 16
  • 111
  • 154
  • Thank you, +1 for useful information! I'd assumed psalm was just a list of rule that were applied, I didn't realize CLI flags might control what rules were applied. That said, even with this option (and the `psalter` command's similar `--find-unused-code`), the system still failed to detected the used method. :( – Alana Storm Apr 30 '19 at 16:08
  • Well I just installed it, initialized the config at level 3, and ran against your test code, worked ok for me! – miken32 Apr 30 '19 at 16:25
  • Looks like it found the unused variable, but didn't find the unused method. I'll update my example code to make this problem clearer. – Alana Storm Apr 30 '19 at 17:01