Skip to content

Improve unused code detection by adding class / method references from Symfony compnents#382

Open
tunterreitmeier wants to merge 1 commit intopsalm:5.xfrom
tunterreitmeier:fix/mark-symfony-usages
Open

Improve unused code detection by adding class / method references from Symfony compnents#382
tunterreitmeier wants to merge 1 commit intopsalm:5.xfrom
tunterreitmeier:fix/mark-symfony-usages

Conversation

@tunterreitmeier
Copy link

Currently, UnusedClass is suppressed for all services in the container. This is a symptom that Psalm does not statically see any usage where Symfony dynamically calls application code. So by adding call references, we can significantly improve dead code detection.

As the dumped debug container contains also unused services, they are effectively no longer detected.
Another issue is that when Psalm has no references to a class, member references are not even checked. So a class that is not statically called by application code might be completely unused or have unused methods / properties.
This is true for example for:

  • EventListeners / Subscribers
  • Messenger Message Handlers
  • Controllers
  • Service Aliases (Implementation is "unused" when Interface is the dependency)

I created a dummy Symfony project with examples of dead code that is currently not detected.
The repository has a GitHub Action that runs Psalm with this branch to showcase the difference.
I also ran this on larger code base where I was able to find quite a bit of genuinely unused code, remove suppression statements for false positives, without raising any new false positives.

Changes

All services, constructors, factory methods and setter injections (e.g. #[Required) are now marked as used.

Objects (and their nested sub-objects) created by Symfony Serializer via Denormalizer::denormalize, Serializer::deserialize, #[MapRequestPayload] or #[MapQueryString] are now marked as used.

Event Listener (also Doctrine ORM) and Subscriber methods are now marked as used.

Controller methods used as Routes are now marked as used.

foreach (self::INVOKABLE_CLASS_ATTRIBUTES as $attributeClass => $callerIdentifier) {
if ($storage->hasAttributeIncludingParents($attributeClass, $codebase)) {
$identifier = new MethodIdentifier($storage->name, '__invoke');
$codebase->methodExists($identifier, null, $callerIdentifier);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative to CodeBase::methodExists would be to add the reference manually. methodExists is more convenient as you do not have to bother with code location and the case of classes / methods.

@tunterreitmeier tunterreitmeier force-pushed the fix/mark-symfony-usages branch from 51273c3 to d568689 Compare March 1, 2026 10:56
@tunterreitmeier tunterreitmeier force-pushed the fix/mark-symfony-usages branch from d568689 to 92b2bde Compare March 1, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant