Skip to content

Fix phpstan/phpstan#14250: trait.duplicateMethod is not reported#5181

Merged
ondrejmirtes merged 8 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-y7tlrzi
Mar 20, 2026
Merged

Fix phpstan/phpstan#14250: trait.duplicateMethod is not reported#5181
ondrejmirtes merged 8 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-y7tlrzi

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

Duplicate method declarations within traits were not being reported with the trait.duplicateMethod identifier. The existing DuplicateDeclarationRule listens for InClassNode, but InClassNode is never emitted for trait declarations — NodeScopeResolver skips trait nodes entirely at declaration time (they are only processed when used by a class).

Changes

  • Created src/Rules/Classes/DuplicateTraitDeclarationRule.php — a new rule that implements Rule<InTraitNode> and checks for duplicate methods, properties, and constants within trait declarations
  • The rule mirrors the logic of DuplicateDeclarationRule but operates on InTraitNode (emitted when a trait is used by a class)
  • Registered at level 0 via #[RegisteredRule(level: 0)], same as the original rule
  • Added test class tests/PHPStan/Rules/Classes/DuplicateTraitDeclarationRuleTest.php
  • Added test data tests/PHPStan/Rules/Classes/data/bug-14250.php

Root cause

NodeScopeResolver::processStmtNodes() short-circuits trait declarations (line 967-968), returning immediately without emitting InClassNode. This is by design — traits are processed in the context of their using class via processTraitUse(). However, InTraitNode IS emitted during trait use processing, so a rule listening for InTraitNode can detect duplicate declarations within traits. No such rule existed before this fix.

Test

The regression test in bug-14250.php defines a trait with two methods named doSomething() and a class that uses the trait. The test expects one error: "Cannot redeclare method Bug14250\MyTrait::doSomething()." at line 11.

Fixes phpstan/phpstan#14250

Copy link
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

This could be refactor with DuplicateDeclarationRule by creating some DuplicateDeclarationHelper

Also, this need more tests, for each possible trait duplicate content

@phpstan-bot
Copy link
Collaborator Author

Done. Here's what I did to address the review:

1. Extracted DuplicateDeclarationHelper — A new helper class with a static checkClassLike() method containing all the shared duplicate-checking logic (constants, enum cases, properties, promoted properties, methods).

2. Refactored both rules to use the helper:

  • DuplicateDeclarationRule (for InClassNode) now delegates to DuplicateDeclarationHelper::checkClassLike()
  • DuplicateTraitDeclarationRule (for InTraitNode) does the same

3. Added comprehensive tests for each type of duplicate content in traits:

  • Duplicate constants (separate lines and comma-separated)
  • Duplicate properties (separate lines and comma-separated)
  • Duplicate methods (including case-insensitive duplicates)
  • Duplicate promoted constructor properties (traditional + promoted collision, and two promoted duplicates)

@VincentLanglet VincentLanglet requested a review from staabm March 19, 2026 08:19
@phpstan-bot
Copy link
Collaborator Author

Static analysis passes. The one test failure (RawErrorFormatterTest) is a pre-existing issue unrelated to this PR — it's about the raw error formatter now including identifiers in output, which is not related to the duplicate declaration changes.

The review from VincentLanglet is an approval with no additional changes requested. The previous feedback (extract helper, add more tests) was already addressed in the latest commits. Everything looks good — no further action needed.

Copy link
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

Fix the lint build

--------------------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 2 LINES
--------------------------------------------------------------------------------
  24 | ERROR | [x] Class \PHPStan\Rules\IdentifierRuleError should not be
     |       |     referenced via a fully qualified name, but via a use
     |       |     statement.
     |       |     (SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName)
 127 | ERROR | [x] Class \PHPStan\Rules\IdentifierRuleError should not be
     |       |     referenced via a fully qualified name, but via a use
     |       |     statement.
     |       |     (SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName)
--------------------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
--------------------------------------------------------------------------------

@phpstan-bot
Copy link
Collaborator Author

Fixed the lint errors by adding use PHPStan\Rules\IdentifierRuleError; and replacing the two fully qualified \PHPStan\Rules\IdentifierRuleError references with the short IdentifierRuleError form. All checks pass (lint, CS, tests, phpstan).

VincentLanglet
VincentLanglet previously approved these changes Mar 19, 2026
@phpstan-bot
Copy link
Collaborator Author

The review from VincentLanglet is an approval with no additional changes requested. All previous feedback (extract helper, add more tests, fix lint, add insteadof/alias tests) has already been addressed in the existing commits. No further action is needed.

@VincentLanglet VincentLanglet dismissed their stale review March 19, 2026 12:36

failing checks

Copy link
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

Add test for

trait Foo {
	public function doFoo(): void {}
}

trait Foo2 {
	use Foo;
}

class Bar
{
  use Foo;
  use Foo;
}

class Baz
{
  use Foo;
  use Foo2;
}

and fix the rules if needed.

No error should be reported.

@phpstan-bot
Copy link
Collaborator Author

Already reviewed — tests passed (the single failure is the pre-existing RawErrorFormatterTest issue). All changes are pushed.

@VincentLanglet
Copy link
Contributor

@staabm Seems like what you asked with #5181 (comment) is a little bit more complex. There is lot of false positive atm.

I wonder if we shouldn't split it into a separate PR (or if you want to try to finish the PR)

@staabm
Copy link
Contributor

staabm commented Mar 19, 2026

I wonder if we shouldn't split it into a separate PR (or if you want to try to finish the PR)

I am fine with opening a new issue for a followup and merging a simpler first step here

@VincentLanglet VincentLanglet force-pushed the create-pull-request/patch-y7tlrzi branch from 4fe4ec4 to d0974e2 Compare March 19, 2026 15:08
Copy link
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

Fix the lint build

FILE: src/Rules/Classes/DuplicateDeclarationHelper.php
--------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
--------------------------------------------------------------------------------
 20 | ERROR | [x] Class \PHPStan\Rules\IdentifierRuleError should not be
    |       |     referenced via a fully qualified name, but via a use
    |       |     statement.
    |       |     (SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName)
--------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
--------------------------------------------------------------------------------

@phpstan-bot
Copy link
Collaborator Author

Fixed the lint error by adding use PHPStan\Rules\IdentifierRuleError; and replacing the fully qualified \PHPStan\Rules\IdentifierRuleError reference with the short IdentifierRuleError form. All checks pass (lint, tests, phpstan).

phpstan-bot and others added 6 commits March 20, 2026 08:27
… methods in traits

- Created DuplicateTraitDeclarationRule that listens for InTraitNode to detect duplicate methods, properties, and constants within trait declarations
- The existing DuplicateDeclarationRule only handles InClassNode, which is never emitted for traits (NodeScopeResolver skips trait declarations at line 967-968)
- InTraitNode is emitted when a trait is used by a class, allowing the new rule to check trait bodies for duplicates
- Added regression test in tests/PHPStan/Rules/Classes/data/bug-14250.php
…clarationRule and DuplicateTraitDeclarationRule

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ts, properties, promoted properties, methods)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@staabm staabm force-pushed the create-pull-request/patch-y7tlrzi branch from 77ef26e to f59c8fe Compare March 20, 2026 07:27
@phpstan-bot
Copy link
Collaborator Author

The branch is clean and up to date. The review from staabm is an approval — no further changes are needed. The PR is ready to be merged.

/**
* @return list<IdentifierRuleError>
*/
public static function checkClassLike(ClassLike $classLike, string $displayName, string $identifierType): array
Copy link
Member

Choose a reason for hiding this comment

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

Rule helpers are supposed to be instances with AutowiredService. So that later we can inject things in them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All checks pass on the committed code — PHPStan, lint, and tests. The review feedback is fully addressed and pushed.

Rule helpers should be instances with AutowiredService so that
dependencies can be injected into them later.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
{

/**
* @return list<IdentifierRuleError>
Copy link
Member

Choose a reason for hiding this comment

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

This method needs @param with string literals as possible identifier parts so that later we're able to generate the identifiers documentation.

Something like this: @param 'class'|'trait' $identifier

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done. Here's what I changed:

  1. src/Rules/Classes/DuplicateDeclarationHelper.php: Added @param 'class'|'interface'|'trait'|'enum' $identifierType to the checkClassLike() method so identifier documentation can be generated from the string literals.

  2. src/PhpDoc/StubValidator.php: Fixed the DuplicateDeclarationRule instantiation to pass the DuplicateDeclarationHelper dependency (was missing after the refactor to use constructor injection).

Both PHPStan and tests pass.

…ationHelper

Adds @param 'class'|'interface'|'trait'|'enum' $identifierType so that
identifier documentation can be generated from the string literals.
Also fixes StubValidator to pass DuplicateDeclarationHelper to DuplicateDeclarationRule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ondrejmirtes ondrejmirtes enabled auto-merge (squash) March 20, 2026 08:01
@ondrejmirtes
Copy link
Member

@VincentLanglet I think you also need to approve.

@phpstan-bot
Copy link
Collaborator Author

All 11,621 tests pass and PHPStan reports no errors. The branch is clean and up to date with the remote. The PR has been approved by both staabm and ondrejmirtes — no further changes are needed. The PR is ready to merge.

@ondrejmirtes ondrejmirtes merged commit 594b7bd into phpstan:2.1.x Mar 20, 2026
653 checks passed
@ondrejmirtes ondrejmirtes deleted the create-pull-request/patch-y7tlrzi branch March 20, 2026 08:28
@phpstan-bot
Copy link
Collaborator Author

I processed this review but have nothing to report.

@VincentLanglet
Copy link
Contributor

You might want to reopen a new issue now @staabm

@staabm
Copy link
Contributor

staabm commented Mar 20, 2026

here we go: phpstan/phpstan#14332

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.

4 participants