-
-
Notifications
You must be signed in to change notification settings - Fork 48
Fix: appliesTo() incorrectly excludes mutually exclusive class states #561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fain182
wants to merge
11
commits into
main
Choose a base branch
from
claude/fix-pr-560-rule-1ZmIe
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This test replicates the issue described in PR #560 where IsNotAbstract incorrectly filters out final classes when used in a that() clause. The test creates a scenario with: - Final classes with correct/incorrect naming - Abstract classes (should be filtered out) - Normal classes with incorrect naming Expected behavior: IsNotAbstract should include final classes in the filter since final classes are, by definition, non-abstract. Current behavior: Test fails with 1 violation instead of expected 2, demonstrating that final classes are incorrectly excluded.
This test verifies that when IsNotAbstract is used as a constraint in should() (not as a filter in that()), it correctly handles final classes. Expected behavior: Final classes should NOT generate violations because they are, by definition, non-abstract (isAbstract() returns false). Result: Test PASSES - demonstrates that evaluate() works correctly. The bug is only in appliesTo(), not in evaluate().
Include interface/trait/enum cases to explicitly document that: - Only 'abstract class' should generate violations - Final, normal classes, interfaces, traits, and enums should NOT generate violations (isAbstract() returns false for all of them) This makes the test more comprehensive and prevents future regressions.
This fixes the issue described in PR #560 where IsNotAbstract incorrectly excluded final classes when used in that() clauses. Root cause: appliesTo() was implementing two different semantics: 1. Type filtering: "Does this property apply to this type?" 2. Condition checking: "Does this class match the condition?" This caused final classes to be excluded from IsNotAbstract filters, even though final classes ARE non-abstract by definition. Changes: - IsAbstract/IsNotAbstract: Remove isFinal() check from appliesTo() Final classes can be checked for abstractness (they're always non-abstract) - IsFinal/IsNotFinal: Remove isAbstract() check from appliesTo() Abstract classes can be checked for finality (they're always non-final) New semantics for appliesTo(): - Returns true if the property can be semantically verified for this type - Only excludes interface/trait/enum where abstract/final concepts don't apply - Does NOT exclude mutually exclusive states (abstract/final) All 350 tests pass, including new integration tests that verify: - IsNotAbstract in that() now includes final classes - IsNotAbstract in should() correctly validates all class types
Enhance unit tests for IsAbstract/IsFinal to verify not just appliesTo() but also the actual evaluate() behavior with mutually exclusive states: - IsAbstract with final class: appliesTo=true, generates violation - IsNotAbstract with final class: appliesTo=true, no violation - IsFinal with abstract class: appliesTo=true, generates violation - IsNotFinal with abstract class: appliesTo=true, no violation This makes the tests more comprehensive and documents the expected behavior when these expressions are used in both that() and should() contexts.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #561 +/- ##
=========================================
Coverage 97.80% 97.80%
+ Complexity 632 628 -4
=========================================
Files 81 81
Lines 1822 1822
=========================================
Hits 1782 1782
Misses 40 40 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #560 -
IsNotAbstract(and other similar expressions) incorrectly excluded final classes when used inthat()clauses, preventing subsequent rules from being evaluated.Problem Description
The Bug
When using
IsNotAbstractas a filter inthat(), final classes were incorrectly excluded:Result: Final test classes with wrong names were NOT flagged as violations.
Root Cause
The
appliesTo()method was confusing two different concepts:Buggy implementation (from PR #478):
The logic error:
IsNotAbstractis semantically wrongIsFinal,IsNotFinal,IsAbstractHistorical Context
appliesTo()to avoid false positives on incompatible typesappliesTo()but incorrectly extended it to mutually exclusive statesSolution
Changed Semantics
New
appliesTo()semantics:Rules:
truefor regular classes, final classes, abstract classesfalseONLY for Interface/Trait/Enum (where abstract/final concepts don't apply)Code Changes
Same fix applied to:
IsAbstract(removed|| $theClass->isFinal())IsNotAbstract(removed|| $theClass->isFinal())IsFinal(removed|| $theClass->isAbstract())IsNotFinal(removed|| $theClass->isAbstract())Why This Is Correct
PHP Semantics
In PHP, abstract and final are mutually exclusive:
final class Foo {}→isAbstract()always returnsfalseabstract class Bar {}→isFinal()always returnsfalseRule Evaluation Flow
In
that()clauses (filtering):In
should()clauses (validation):Expected Behavior Table
IsAbstract::appliesTo()IsNotAbstract::appliesTo()truetruetrue✅true✅true✅true✅falsefalsefalsefalsefalsefalse