Skip to content

#161: Fix class variable and tests#1229

Open
marinka-aa wants to merge 7 commits into
cqfn:masterfrom
marinka-aa:fix-class-variable-and-tests
Open

#161: Fix class variable and tests#1229
marinka-aa wants to merge 7 commits into
cqfn:masterfrom
marinka-aa:fix-class-variable-and-tests

Conversation

@marinka-aa

@marinka-aa marinka-aa commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added two new code-analysis checks for class inheritance and class-variable usage.
    • Expanded bidirectional index detection to identify more real-world update patterns.
  • Bug Fixes

    • NPath complexity analysis now works even when Maven isn’t installed, with a fallback calculation path.
    • Several previously skipped checks and tests are now active, improving validation coverage.
  • Documentation

    • Updated internal test expectations to match the latest output and analysis behavior.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5468eee1-af25-4cbf-976d-5455972bdebd

📥 Commits

Reviewing files that changed from the base of the PR and between a4abb7a and d445e99.

📒 Files selected for processing (2)
  • aibolit/__main__.py
  • test/recommend/test_recommend_pipeline.py
✅ Files skipped from review due to trivial changes (1)
  • aibolit/main.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/recommend/test_recommend_pipeline.py

📝 Walkthrough

Walkthrough

Adds two new AST-based pattern detectors (ClassVariable P35, BidirectIndex), registers them in config.py, adds a Maven-free fallback path to NPathMetric, and re-enables several previously skipped tests by removing @skip/@xfail decorators and updating expected values.

Changes

Pattern implementations, metric fallback, and test updates

Layer / File(s) Summary
ClassVariable detector and registration
aibolit/patterns/class_variable/class_variable.py, aibolit/config.py, test/patterns/class_variable/test_class_variable.py
ClassVariable.value() traverses LOCAL_VARIABLE_DECLARATION nodes, matching declarations whose reference type equals the CLASS_CREATOR type; registered as P35 in config; positive and negative tests added.
BidirectIndex AST implementation
aibolit/patterns/bidirect_index/bidirect_index.py, test/patterns/bidirect_index/test_bidirect_index.py
Stub replaced with full scope-aware AST traversal tracking per-variable increment/decrement state across blocks and for statements; test @skip removed.
NPath Maven-free fallback
aibolit/metrics/npath/main.py, test/metrics/npath/test_all_types.py
NPathMetric.value() detects missing mvn via shutil.which and calls _value_without_maven() using build_ast + MvnFreeNPathMetric; test conditionally expects 288 when Maven is absent.
Config test fix and pipeline re-enablement
test/config/test_config.py, test/recommend/test_recommend_pipeline.py, test/stats/test_stats.py, aibolit/__main__.py
test_each_metric_in_config_accepts_ast drops xfail and filters metrics_exclude; pipeline and stats tests have @skip removed with updated expected MD5; noqa comment narrowed to C901.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • cqfn/aibolit#832: Refactors existing patterns to enforce ast: AST signatures in config tests, directly related to the AST-interface changes in ClassVariable and config test adjustments here.
  • cqfn/aibolit#1227: Implements ClassInheritance (P34) and registers it in config.py at the same integration point as P35 added here.
  • cqfn/aibolit#1228: Also modifies aibolit/config.py to register ClassInheritance (P34), overlapping with the config registration changes in this PR.

Suggested reviewers

  • ivanovmg
  • yegor256

🐇 Two new patterns hop into the code,
P34 and P35 lighten the load.
BidirectIndex now tracks every ++,
NPath runs fine without Maven's fuss.
Skipped tests awaken, hashes refreshed—
the rabbit is pleased, the suite is blessed! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.95% 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 The title matches the main theme of adding the class variable pattern and updating related tests, though it omits other changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@marinka-aa marinka-aa changed the title Fix class variable and tests #161: Fix class variable and tests Jun 25, 2026

@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: 3

🧹 Nitpick comments (1)
aibolit/utils/encoding_detector.py (1)

24-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Final errors='replace' fallback is unreachable.

'latin-1' decoding never raises (it maps all 256 byte values), so once it is reached in the loop it always returns successfully. The trailing return data.decode('utf-8', errors='replace') at Line 31 is therefore dead code. Either drop 'latin-1' from the candidate list to let the replacement path handle undecodable bytes, or remove the unreachable final return.

🤖 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/utils/encoding_detector.py` around lines 24 - 31, The fallback in
encoding_detector.decode is unreachable because the encoding loop always
succeeds once 'latin-1' is tried. Update the candidate list or the fallback
logic in the decode path so that the final data.decode('utf-8',
errors='replace') branch is either actually reachable or removed, keeping the
behavior in sync with detect_encoding_of_data and the utf-8/latin-1 fallback
order.
🤖 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/metrics/npath/main.py`:
- Around line 61-73: The fallback implementation in _value_without_maven()
rejects directory inputs even though value() already supports them, causing
inconsistent behavior when mvn is unavailable. Update _value_without_maven() to
mirror the directory-handling logic used in value() by detecting directories and
resolving the Java sources/AST the same way before calling
AST.build_from_javalang and MvnFreeNPathMetric.value(). Keep the existing
file-path handling and error behavior for missing inputs, but ensure directory
analysis works in both the Maven and non-Maven paths.

In `@PATTERNS.md`:
- Around line 629-643: Add a missing P35 entry in PATTERNS.md to match the
registry in aibolit/config.py. Update the pattern catalog by adding the Class
variable section with the same title, code, and description used by the
registered P35 symbol so the public documentation stays consistent and complete.
Use the existing P34 section as the local reference point for where the new
pattern entry should be inserted.

In `@test/patterns/class_variable/test_class_variable.py`:
- Around line 26-62: The multiline source fixtures in test_class_variable.py use
triple double-quoted strings, which triggers Ruff Q001 under the repo’s quoting
rule. Update the three string literals passed into _find_lines in
test_ignore_interface_variable, test_ignore_primitive_variable, and the
preceding fixture block to use triple single quotes instead, keeping the content
unchanged and consistent with the existing test helper calls.

---

Nitpick comments:
In `@aibolit/utils/encoding_detector.py`:
- Around line 24-31: The fallback in encoding_detector.decode is unreachable
because the encoding loop always succeeds once 'latin-1' is tried. Update the
candidate list or the fallback logic in the decode path so that the final
data.decode('utf-8', errors='replace') branch is either actually reachable or
removed, keeping the behavior in sync with detect_encoding_of_data and the
utf-8/latin-1 fallback order.
🪄 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: cb2c4979-f051-41c8-92b6-6555cc9af9ff

📥 Commits

Reviewing files that changed from the base of the PR and between a3ace02 and 9c081dc.

⛔ Files ignored due to path filters (1)
  • test/stats/results_test.csv is excluded by !**/*.csv
📒 Files selected for processing (21)
  • PATTERNS.md
  • aibolit/config.py
  • aibolit/metrics/npath/main.py
  • aibolit/patterns/bidirect_index/bidirect_index.py
  • aibolit/patterns/class_inheritance/__init__.py
  • aibolit/patterns/class_inheritance/class_inheritance.py
  • aibolit/patterns/class_variable/class_variable.py
  • aibolit/utils/encoding_detector.py
  • test/config/test_config.py
  • test/metrics/maxDiameter/test_max_diameter.py
  • test/metrics/npath/test_all_types.py
  • test/patterns/bidirect_index/test_bidirect_index.py
  • test/patterns/class_inheritance/NestedInheritance.java
  • test/patterns/class_inheritance/OnlyImplements.java
  • test/patterns/class_inheritance/SimpleInheritance.java
  • test/patterns/class_inheritance/TypeParameterExtends.java
  • test/patterns/class_inheritance/test_class_inheritance.py
  • test/patterns/class_variable/test_class_variable.py
  • test/recommend/test_recommend_pipeline.py
  • test/stats/test_stats.py
  • test/utils/test_encoding_detector.py

Comment on lines +61 to +73
def _value_without_maven(self):
if not os.path.isfile(self.input):
raise Exception(' '.join(['File', self.input, 'does not exist']))
try:
ast = AST.build_from_javalang(build_ast(self.input))
except Exception as exception:
raise Exception(f'PMDException: {exception}') from exception
complexity = MvnFreeNPathMetric(ast).value()
return {
'data': [{'file': self.input, 'complexity': complexity}],
'errors': [],
}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP --type=py -C3 'NPathMetric\s*\('

Repository: cqfn/aibolit

Length of output: 150


Fallback rejects directory inputs that the Maven path accepts.

The value() method handles directory inputs (Lines 37–40), but _value_without_maven() raises a File ... does not exist error for them (Line 62). This creates an environment-dependent regression: directory analysis fails only when mvn is unavailable.

Add directory handling to the fallback path to match the Maven behavior.

aibolit/metrics/npath/main.py Lines: 61-73

Original code
    def _value_without_maven(self):
        if not os.path.isfile(self.input):
            raise Exception(' '.join(['File', self.input, 'does not exist']))
        try:
            ast = AST.build_from_javalang(build_ast(self.input))
        except Exception as exception:
            raise Exception(f'PMDException: {exception}') from exception
        complexity = MvnFreeNPathMetric(ast).value()
        return {
            'data': [{'file': self.input, 'complexity': complexity}],
            'errors': [],
        }
🤖 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/metrics/npath/main.py` around lines 61 - 73, The fallback
implementation in _value_without_maven() rejects directory inputs even though
value() already supports them, causing inconsistent behavior when mvn is
unavailable. Update _value_without_maven() to mirror the directory-handling
logic used in value() by detecting directories and resolving the Java
sources/AST the same way before calling AST.build_from_javalang and
MvnFreeNPathMetric.value(). Keep the existing file-path handling and error
behavior for missing inputs, but ensure directory analysis works in both the
Maven and non-Maven paths.

Comment thread PATTERNS.md
Comment on lines +629 to +643
***

*Title*: Class inheritance

*Code*: **P34**

*Description*: Once a class extends another class via `extends`, it's a pattern.

*Example*:

```java
class MyList extends AbstractList { // here
}
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a P35 section to keep the pattern catalog consistent with registry.

aibolit/config.py registers P35 (Class variable) at Line 204, but PATTERNS.md currently has no P35 entry. This leaves the public pattern dictionary incomplete.

🤖 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 `@PATTERNS.md` around lines 629 - 643, Add a missing P35 entry in PATTERNS.md
to match the registry in aibolit/config.py. Update the pattern catalog by adding
the Class variable section with the same title, code, and description used by
the registered P35 symbol so the public documentation stays consistent and
complete. Use the existing P34 section as the local reference point for where
the new pattern entry should be inserted.

Comment thread test/patterns/class_variable/test_class_variable.py Outdated

@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.

🧹 Nitpick comments (1)
.github/workflows/ruff.yml (1)

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

Pin Ruff to the version declared in pyproject.toml.

.github/workflows/ruff.yml installs ruff without a version, while the repo pins ruff==0.15.19. That lets CI drift as Ruff releases change lint behavior.

🤖 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 @.github/workflows/ruff.yml at line 27, The Ruff install step in the workflow
is not using the repo-pinned version, which can make CI behavior drift. Update
the install command in the ruff workflow to install the same Ruff version
declared in pyproject.toml, and keep the workflow aligned with that pinned
version so the Ruff check runs consistently.
🤖 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.

Nitpick comments:
In @.github/workflows/ruff.yml:
- Line 27: The Ruff install step in the workflow is not using the repo-pinned
version, which can make CI behavior drift. Update the install command in the
ruff workflow to install the same Ruff version declared in pyproject.toml, and
keep the workflow aligned with that pinned version so the Ruff check runs
consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b36b727-3f97-45a5-97eb-f792b9a45f27

📥 Commits

Reviewing files that changed from the base of the PR and between 9c081dc and a4abb7a.

📒 Files selected for processing (5)
  • .github/workflows/flake8.yml
  • .github/workflows/ruff.yml
  • aibolit/__main__.py
  • aibolit/patterns/bidirect_index/bidirect_index.py
  • test/patterns/class_variable/test_class_variable.py
✅ Files skipped from review due to trivial changes (2)
  • .github/workflows/flake8.yml
  • aibolit/main.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/patterns/class_variable/test_class_variable.py
  • aibolit/patterns/bidirect_index/bidirect_index.py

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