Skip to content

if.alwaysTrue when found in traits isn't reported correctly through context of a user class #14932

Closed
phpstan/phpstan-src
#6280

Description

@Numynum

Bug report

As seen in the example, deadCode.unreachable is found twice, in the context of the using classes.
But if.alwaysTrue is only found in the trait itself.
When generating a baseline, it seems to just "randomly" pick a class to write the ignored error for, leading to inconsistent results in our development vs CI pipeline.

Analysis result:

 3/3 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% < 1 sec

 ------ ------------------------------
  Line   FooTrait.php
 ------ ------------------------------
  15     If condition is always true.
         🪪  if.alwaysTrue
 ------ ------------------------------

 ------ -------------------------------------------------------
  Line   FooTrait.php (in context of class Bar)
 ------ -------------------------------------------------------
  19     Unreachable statement - code above always terminates.
         🪪  deadCode.unreachable
 ------ -------------------------------------------------------

 ------ -------------------------------------------------------
  Line   FooTrait.php (in context of class Foo)
 ------ -------------------------------------------------------
  19     Unreachable statement - code above always terminates.
         🪪  deadCode.unreachable
 ------ -------------------------------------------------------

Code snippet that reproduces the problem

https://phpstan.org/r/73efdd9c-0534-4553-9447-2f94e94bbefd

Expected output

if.alwaysTrue in a trait should always be reported in context of a class instead

Did PHPStan help you today? Did it make you happy in any way?

love the tool, it's helped find and prevent so many issues ❤️

Activity

  1. mabar commented on Jul 21, 2026

    @mabar
    Contributor

    This error occurs since phpstan/phpstan-src#5309 - reproducible from 2.2.0 up to latest 2.2.x-dev@ce1a7f1 (upcoming 2.2.6, post related fix phpstan/phpstan-src#6033)

    I was able to reliably replicate it by running phpstan on a single core, with a single class that uses a trait and reports an error. Error is baselined. Then there is a simple repeatable loop:

    • run phpstan -> no errors
    • add second class that uses the trait and run phpstan -> error
    • run phpstan 1 to N times (without any changes) -> no error
      Removing the second class and running the same loop replicates the behavior.

    phpstan-trait-flicker.zip
    This is a minimal reproduction. Just extract the archive, cd to directory and run ./replicate.sh

    Likely root cause - the winner therefore depends on the iteration order of collected data, and that order is unstable:

    • Cold run: ParallelAnalyser appends $collectedData[$file] in worker-message arrival order — a race between worker processes.
    • Warm run: collected data is replayed from the result cache, which ResultCacheManager::save() ksorts — deterministic alphabetical order, different from the arrival order of the run that populated it.
    • Partial run with a new file: mergeCollectedData() preserves key positions for existing files and appends new files at the end — so a newly added using class wins exactly once, until the next run replays the ksorted save.

    Because CollectedDataNode rules run in AnalyserResultFinalizer after the result cache is saved (AnalyseApplication), these errors are never stored in the cache's error map and are re-derived — with a potentially different winner — on every run.

  2. pelletiermaxime commented on Aug 12, 2026

    @pelletiermaxime

    I believe I ran into the same problem after updating from 2.2.1 to 2.2.8. I got errors that alwaysTrue or alwaysFalse was not matched, regenerated the baseline and then in my pipeline I got this:

     Line   app/Models/Db/Training.php                                            
     ------ ---------------------------------------------------------------------- 
             Ignored error pattern #^Negated boolean expression is always          
             false\.$# (booleanNot.alwaysFalse) in path                            
             /builds/hexfit/hexfit/api/backend-laravel/app/Models/Db/Training.php  
             was not matched in reported errors.                                   
             🪪  ignore.unmatched (non-ignorable)           
    ----- ----------------------------------------------------------------------- 
      Line   app/Models/Traits/SharingObject.php                                    
     ------ ----------------------------------------------------------------------- 
      37     Negated boolean expression is always false.                            
             🪪  booleanNot.alwaysFalse          
    
    

    The Training class uses the Trait HexfitModel. My AI just patched the baseline file to fix my pipeline:

    Image
  3. github-actions commented on Sep 27, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

  4. locked as resolved and limited conversation to collaborators on Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions