Skip to content

Deduplicate class-level patterns after decomposition#1232

Open
somepatt wants to merge 2 commits into
cqfn:masterfrom
somepatt:fix-1217-dedup-class-patterns
Open

Deduplicate class-level patterns after decomposition#1232
somepatt wants to merge 2 commits into
cqfn:masterfrom
somepatt:fix-1217-dedup-class-patterns

Conversation

@somepatt

@somepatt somepatt commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • count class-level patterns only once per class during decomposition
  • keep component-specific patterns and metrics unchanged
  • add a regression test for duplicated P4/P24 findings

Problem

calculate_patterns_and_metrics_with_decomposition() runs every pattern on every decomposed component. Patterns that inspect CLASS_DECLARATION therefore report the same class once per component, which duplicates findings and distorts ranking.

Fix

Precompute class-level pattern results on the whole class AST and attach them only to the first decomposed component. Other components keep an empty result for those patterns.

Verification

  • python3 -B -m pytest test/recommend/test_recommend_pipeline.py -q
  • python3 -B -m flake8 --max-line-length=120 aibolit/__main__.py test/recommend/test_recommend_pipeline.py

Closes #1217

Summary by CodeRabbit

  • Bug Fixes
    • Improved pattern counting for decomposed analysis so certain class-level patterns are only counted once across related components.
    • Prevented duplicate matches from appearing in later components, making recommendation results more accurate and consistent.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The decomposition pipeline now treats selected pattern codes as class-level during per-component analysis, reuses a cached class-level result for them, and filters excluded patterns and metrics before iterating components. A new regression test checks single-count reporting for P4 and P24.

Changes

Class-level pattern handling

Layer / File(s) Summary
Class-level pattern handling
aibolit/__main__.py
CLASS_LEVEL_PATTERN_CODES is added and calculate_patterns_and_metrics_with_decomposition filters excluded patterns and metrics before component iteration, caches class-level pattern results, and reuses them for later decomposed components.

Regression coverage

Layer / File(s) Summary
Regression test
test/recommend/test_recommend_pipeline.py
A temporary Java class test calls the decomposition pipeline and asserts that P4 and P24 are counted once, with line data kept only on the first decomposed component.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

(\/)
(•
•) I hop through classes, neat and bright,
One class-level hop, not twelve in sight.
P4 and P24 now sing one tune,
With cached-deer? no — cached code under the moon.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly states the main change: deduplicating class-level patterns after decomposition.
Linked Issues check ✅ Passed The pipeline now caches class-level pattern results and reports them only once per class, matching #1217's duplicate-finding fix.
Out of Scope Changes check ✅ Passed The changes stay focused on decomposition deduplication and the regression test, with no obvious unrelated scope creep.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/recommend/test_recommend_pipeline.py (1)

172-180: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover all cached class-level pattern codes in the regression.

CLASS_LEVEL_PATTERN_CODES also includes P7 and P9, but this test only guards P4/P24. Please add fixture coverage/assertions for the remaining class-level codes, and mirror the first-component line assertion for P24.

Small assertion to add for the existing fixture
         self.assertEqual(sum(component['input_params']['P24'] for component in components), 1)
         self.assertEqual(len(components[0]['code_lines_dict']['lines_P4']), 1)
+        self.assertEqual(len(components[0]['code_lines_dict']['lines_P24']), 1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/recommend/test_recommend_pipeline.py` around lines 172 - 180, The
regression test in test_recommend_pipeline should cover all cached class-level
pattern codes, not just P4 and P24. Update the existing assertions around
components to also include P7 and P9 from CLASS_LEVEL_PATTERN_CODES, and add
fixture coverage so the test verifies their input_params and code_lines_dict
behavior across components. Also mirror the first-component line assertion for
P24 so the test checks its lines list in the same way as P4.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@aibolit/__main__.py`:
- Around line 293-297: Class-level patterns are being computed in __main__.py
before suppression is applied, so suppressed codes can still trigger unnecessary
work or affect decomposition. Update the class-level pattern evaluation in the
comprehension that builds class_level_pattern_results to skip any pattern
already suppressed, using the same suppression state checked later in the
decomposition flow, and keep the logic aligned with the pattern code symbols in
patterns_info and CLASS_LEVEL_PATTERN_CODES.

---

Nitpick comments:
In `@test/recommend/test_recommend_pipeline.py`:
- Around line 172-180: The regression test in test_recommend_pipeline should
cover all cached class-level pattern codes, not just P4 and P24. Update the
existing assertions around components to also include P7 and P9 from
CLASS_LEVEL_PATTERN_CODES, and add fixture coverage so the test verifies their
input_params and code_lines_dict behavior across components. Also mirror the
first-component line assertion for P24 so the test checks its lines list in the
same way as P4.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 077ca4c0-f1f8-406a-99c0-e07ad4d64213

📥 Commits

Reviewing files that changed from the base of the PR and between 6d5c676 and fed1797.

📒 Files selected for processing (2)
  • aibolit/__main__.py
  • test/recommend/test_recommend_pipeline.py

Comment thread aibolit/__main__.py
Comment on lines +293 to +297
class_level_pattern_results = {
pattern_info['code']: pattern_info['make']().value(class_ast)
for pattern_info in patterns_info
if pattern_info['code'] in CLASS_LEVEL_PATTERN_CODES
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Respect suppression before precomputing class-level patterns.

Suppressed class-level patterns are still evaluated here before Line 304 can zero them out, so a suppressed P4/P7/P9/P24 can still fail the whole decomposition or waste work.

Proposed fix
             class_level_pattern_results = {
                 pattern_info['code']: pattern_info['make']().value(class_ast)
                 for pattern_info in patterns_info
-                if pattern_info['code'] in CLASS_LEVEL_PATTERN_CODES
+                if (
+                    pattern_info['code'] in CLASS_LEVEL_PATTERN_CODES
+                    and pattern_info['code'] not in patterns_to_suppress
+                )
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class_level_pattern_results = {
pattern_info['code']: pattern_info['make']().value(class_ast)
for pattern_info in patterns_info
if pattern_info['code'] in CLASS_LEVEL_PATTERN_CODES
}
class_level_pattern_results = {
pattern_info['code']: pattern_info['make']().value(class_ast)
for pattern_info in patterns_info
if (
pattern_info['code'] in CLASS_LEVEL_PATTERN_CODES
and pattern_info['code'] not in patterns_to_suppress
)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@aibolit/__main__.py` around lines 293 - 297, Class-level patterns are being
computed in __main__.py before suppression is applied, so suppressed codes can
still trigger unnecessary work or affect decomposition. Update the class-level
pattern evaluation in the comprehension that builds class_level_pattern_results
to skip any pattern already suppressed, using the same suppression state checked
later in the decomposition flow, and keep the logic aligned with the pattern
code symbols in patterns_info and CLASS_LEVEL_PATTERN_CODES.

@somepatt

Copy link
Copy Markdown
Contributor Author

@yegor256 please review this PR.

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.

Duplicated violation reports for class-level patterns due to class decomposition in Aibolit

1 participant