Skip to content

Do not report a private method overriding a private trait method as unused - #6191

Open
Jean-Beru wants to merge 1 commit into
phpstan:2.2.xfrom
Jean-Beru:fix-12201-private-method-overriding-trait
Open

Do not report a private method overriding a private trait method as unused#6191
Jean-Beru wants to merge 1 commit into
phpstan:2.2.xfrom
Jean-Beru:fix-12201-private-method-overriding-trait

Conversation

@Jean-Beru

Copy link
Copy Markdown

Fixes phpstan/phpstan#12201

Why

A class member takes precedence over the member of the same name coming from a used trait. So a private method redeclared in the class is the one the trait's own methods call.

UnusedPrivateMethodRule sees those call sites only when the trait is part of the analysed files. In a project whose paths exclude its dependencies, ClassMethodsNode::getMethodCalls() collects no call at all. The class method is then reported as unused.

The canonical case is a Symfony application. The recipe generates a Kernel that redeclares KernelTrait::getAllowedEnvs(), and the trait lives in vendor/.

// vendor/symfony/dependency-injection/Kernel/KernelTrait.php, not analysed
trait KernelTrait
{
	private function getAllowedEnvs(): array
	{
		return [];
	}

	protected function getKernelParameters(): array
	{
		// ...
		if (!$knownEnvs = array_flip($this->getAllowedEnvs())) {
		// ...
	}
}
// src/Kernel.php, analysed
class Kernel extends BaseKernel
{
	use MicroKernelTrait; // uses KernelTrait

	/**
	 * @return list<string>
	 */
	private function getAllowedEnvs(): array // reported as unused
	{
		return ['prod', 'dev', 'test'];
	}
}

Every new Symfony 8.1 project hits this from level 4 up. ignoreErrors is the only way out.

What

A private method is skipped when a used trait declares a private method of the same name.

getTraits() is enough without recursion. PHP flattens trait composition. getAllowedEnvs() comes from the nested KernelTrait, and the reflection of MicroKernelTrait already reports it.

@Jean-Beru Jean-Beru changed the title Do not report a private method overriding a private trait method as u… Do not report a private method overriding a private trait method as unused Aug 6, 2026
continue;
}

if ($this->isOverridingPrivateTraitMethod($classReflection, $methodName)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why does $method->isDeclaredInTrait() in line 63 not work?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see now, that we have 2 declared methods, and the one in the loop is not declared on the trait but overrides one from the trait

…nused

A class member takes precedence over the member of the same name coming
from a used trait, so a private method redeclared in the class is what the
trait's own methods call. The rule sees those call sites only when the
trait is part of the analysed files: analysing a project whose paths do not
include its dependencies leaves ClassMethodsNode::getMethodCalls() with no
call at all, and the class method is reported as unused.

The canonical case is a Symfony application, where the framework recipe
generates a Kernel redeclaring KernelTrait::getAllowedEnvs() while the
trait lives in vendor/, outside of the analysed paths.

Skip those methods. Nothing is left to distinguish an override that the
trait calls from one it does not, so a redeclared private method the trait
never calls is no longer reported either.

Fixes phpstan/phpstan#12201

Assisted-by: Claude Code:claude-opus-5
@staabm
staabm force-pushed the fix-12201-private-method-overriding-trait branch from 16238a7 to c718210 Compare August 13, 2026 08:46
@staabm

staabm commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

do we have a similar problem for private constants (UnusedPrivateConstantRule) or properties (UnusedPrivatePropertyRule)?

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.

"method unused" when overriding private methode of a trait

2 participants