-
Notifications
You must be signed in to change notification settings - Fork 40
bidirect_index.py:18-25: Implement bidirect index pattern #842
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
base: master
Are you sure you want to change the base?
Conversation
tests is working add docs for code
too-many-statements issue
WalkthroughThe Changes
Poem
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
✨ Finishing Touches
🧪 Generate 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
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 (4)
aibolit/patterns/bidirect_index/bidirect_index.py (4)
42-42: Fix quote style for consistency.The codebase prefers single quotes based on the pipeline failures.
- with open(filename, encoding="utf-8") as f: + with open(filename, encoding='utf-8') as f:
59-62: Simplify nested if statements.Combine the conditions to improve readability.
- if re.search(r"\bvoid\b\s+\w+\s*\(.*\)\s*{", line): - if mstart is None: - mstart = idx - brace = 0 + if re.search(r'\bvoid\b\s+\w+\s*\(.*\)\s*{', line) and mstart is None: + mstart = idx + brace = 0
63-64: Fix quote style for consistency.- brace += line.count("{") - brace -= line.count("}") + brace += line.count('{') + brace -= line.count('}')
99-100: Fix quote style for consistency.- brace += lines[i].count("{") - brace -= lines[i].count("}") + brace += lines[i].count('{') + brace -= lines[i].count('}')
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)test/patterns/bidirect_index/test_bidirect_index.py(0 hunks)
💤 Files with no reviewable changes (1)
- test/patterns/bidirect_index/test_bidirect_index.py
🧰 Additional context used
🪛 Ruff (0.11.9)
aibolit/patterns/bidirect_index/bidirect_index.py
59-60: Use a single if statement instead of nested if statements
(SIM102)
86-86: Ambiguous variable name: l
(E741)
116-116: Ambiguous variable name: l
(E741)
141-141: Ambiguous variable name: l
(E741)
🪛 Flake8 (7.2.0)
aibolit/patterns/bidirect_index/bidirect_index.py
[error] 86-86: ambiguous variable name 'l'
(E741)
[error] 116-116: ambiguous variable name 'l'
(E741)
[error] 141-141: ambiguous variable name 'l'
(E741)
🪛 GitHub Actions: flake8
aibolit/patterns/bidirect_index/bidirect_index.py
[error] 86-86: flake8: ambiguous variable name 'l' (E741)
[error] 116-116: flake8: ambiguous variable name 'l' (E741)
[error] 141-141: flake8: ambiguous variable name 'l' (E741)
🪛 GitHub Actions: ruff
aibolit/patterns/bidirect_index/bidirect_index.py
[error] 42-42: Ruff Q000: Double quotes found but single quotes preferred.
[error] 59-59: Ruff Q000: Double quotes found but single quotes preferred.
[error] 63-63: Ruff Q000: Double quotes found but single quotes preferred.
[error] 64-64: Ruff Q000: Double quotes found but single quotes preferred.
[error] 86-86: Ruff E741: Ambiguous variable name: l.
[error] 99-99: Ruff Q000: Double quotes found but single quotes preferred.
[error] 100-100: Ruff Q000: Double quotes found but single quotes preferred.
[error] 116-116: Ruff E741: Ambiguous variable name: l.
[error] 141-141: Ruff E741: Ambiguous variable name: l.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: latexmk
- GitHub Check: make
- GitHub Check: e2e-test
🔇 Additional comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (2)
8-25: Excellent documentation!The docstring provides a clear and comprehensive explanation of the bidirectional index pattern, including the definition, use cases, and usage example.
1-154: Well-implemented bidirectional index pattern detection!The implementation successfully achieves the PR objectives with comprehensive pattern matching and proper scope handling. The logic correctly identifies bidirectional indices while excluding increments/decrements within for-loops that declare local variables.
The only issues are style-related (variable naming and quote consistency) which should be addressed to pass the CI checks.
flake8 ruff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (2)
116-119: Replace variable name 'line_' with a more descriptive name.For consistency and clarity, use a more descriptive variable name.
Apply this diff:
k = start while k < end: - line_ = lines[k] - for_decl = re.match(r'\s*for\s*\(\s*int\s+' + re.escape(var) + r'\s*=', line_) + current_line = lines[k] + for_decl = re.match(r'\s*for\s*\(\s*int\s+' + re.escape(var) + r'\s*=', current_line) if for_decl: - brace = line_.count('{') - line_.count('}') + brace = current_line.count('{') - current_line.count('}')
141-151: Replace variable name 'line_' with a more descriptive name.For consistency with the rest of the codebase, use a more descriptive variable name.
Apply this diff:
if not in_for: - line_ = lines[k] + current_line = lines[k] if re.search( r'(\+\+' + re.escape(var) + r'|' + re.escape(var) + r'\+\+|' + re.escape(var) + r'\s*\+=\s*1\b|' + - re.escape(var) + r'\s*=\s*' + re.escape(var) + r'\s*\+\s*1\b)', line_): + re.escape(var) + r'\s*=\s*' + re.escape(var) + r'\s*\+\s*1\b)', current_line): inc_outside += 1 if re.search( r'(--' + re.escape(var) + r'|' + re.escape(var) + r'--|' + re.escape(var) + r'\s*-=\s*1\b|' + - re.escape(var) + r'\s*=\s*' + re.escape(var) + r'\s*-\s*1\b)', line_): + re.escape(var) + r'\s*=\s*' + re.escape(var) + r'\s*-\s*1\b)', current_line): dec_outside += 1
🧹 Nitpick comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (2)
59-62: Simplify nested if statements.The nested if statements can be combined into a single condition for better readability.
Apply this diff to simplify:
- if re.search(r'\bvoid\b\s+\w+\s*\(.*\)\s*{', line): - if mstart is None: - mstart = idx - brace = 0 + if re.search(r'\bvoid\b\s+\w+\s*\(.*\)\s*{', line) and mstart is None: + mstart = idx + brace = 0
86-90: Use consistent variable naming.The variable name
line_is used here whilelineis used elsewhere. Consider using a more descriptive name likenext_lineorcurrent_linefor clarity and consistency.Apply this diff:
j = i + 1 while j <= end: - line_ = lines[j] - if re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_): + next_line = lines[j] + if re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', next_line): break j += 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
Learnt from: SinbadTheSailor2005
PR: cqfn/aibolit#816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called `expressionl` (with 'l' suffix), not `expression`. This is confirmed in `aibolit/ast_framework/_auxiliary_data.py` and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
🪛 Ruff (0.11.9)
aibolit/patterns/bidirect_index/bidirect_index.py
59-60: Use a single if statement instead of nested if statements
(SIM102)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: latexmk
- GitHub Check: make
- GitHub Check: e2e-test
update doc; update pattern for searching;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (2)
42-46: Improve exception chaining for better error context.The error handling is good, but consider using exception chaining to preserve the original exception context.
- except (FileNotFoundError, IOError) as e: - raise ValueError(f'Failed to read file {filename}: {e}') + except (FileNotFoundError, IOError) as e: + raise ValueError(f'Failed to read file {filename}: {e}') from e
68-73: Simplify nested if statements for better readability.The regex pattern effectively captures Java method signatures with various modifiers and return types. However, the nested if statement can be simplified.
- if (re.search( - r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', - line)): - if mstart is None: - mstart = idx - brace = 0 + if (re.search( + r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', + line) and mstart is None): + mstart = idx + brace = 0
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
Learnt from: SinbadTheSailor2005
PR: cqfn/aibolit#816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called `expressionl` (with 'l' suffix), not `expression`. This is confirmed in `aibolit/ast_framework/_auxiliary_data.py` and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
🧬 Code Graph Analysis (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
aibolit/ast_framework/ast_node.py (1)
line(41-69)
🪛 Ruff (0.11.9)
aibolit/patterns/bidirect_index/bidirect_index.py
46-46: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
68-71: Use a single if statement instead of nested if statements
(SIM102)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: make
- GitHub Check: e2e-test
- GitHub Check: latexmk
🔇 Additional comments (4)
aibolit/patterns/bidirect_index/bidirect_index.py (4)
7-25: Excellent documentation and clear pattern definition.The class docstring provides a comprehensive explanation of the bidirectional index pattern with clear usage examples. The distinction about excluding for-loop local variables is particularly important for avoiding false positives.
81-117: Well-implemented block analysis with proper variable scope handling.The method correctly handles both typed and untyped variable declarations, properly determines variable scope, and recursively analyzes nested blocks. The logic for identifying bidirectional indices by checking for both increments and decrements outside for-loop blocks is sound.
119-139: Correct implementation for detecting for-loop local variables.The method properly identifies for-loop blocks that declare local variables with the target variable name. This is essential for excluding increments/decrements that occur within for-loops, preventing false positives in the bidirectional index detection.
141-164: Comprehensive increment/decrement pattern matching.The method correctly identifies various increment and decrement patterns including pre/post operators, compound assignments, and explicit assignments. The exclusion of operations within for-loop blocks is properly implemented to avoid false positives.
ivanovmg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KachanovYev thank you for the PR! It looks like you spent quite a lot of time figuring out this pattern while brushing up your regex knowledge!
Please have a look at my comments. Bottom line is:
- Prefer instance methods or external functions (if more reasonable) over static methods
- Extract helper methods describing what regular expressions mean
- Use verbose mode for regex, with comments what each section of the pattern does
- Add a puzzle to refactor this solution in future towards using
ast_framework.ASToverfilenameas an input parameter, related to #813
| - increments/decrements inside a for-loop with a local variable of the same name | ||
| are ignored (to avoid "fake" cases). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe indent it to match the indentation of the corresponding item
| - increments/decrements inside a for-loop with a local variable of the same name | |
| are ignored (to avoid "fake" cases). | |
| - increments/decrements inside a for-loop with a local variable of the same name | |
| are ignored (to avoid "fake" cases). |
| @staticmethod | ||
| def value(filename: str | os.PathLike): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see, in this implementation you totally relied on the literate source code of the file, rather than the abstract syntax tree.
Although, your solution covers all test cases, it goes against the direction on the unification of the Pattern interface.
I suppose that if this is a working solution, then having resolved the comments, it may be merged with a puzzle to refactor it into using an ast_framework.AST rather than a filename as an input parameter.
@AntonProkopyev can you suggest the way forward here?
| for m_start, m_end in BidirectIndex.find_methods(lines): | ||
| BidirectIndex.analyze_block(lines, m_start + 1, m_end, result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we maybe use instance methods, rather than static methods?
| for m_start, m_end in BidirectIndex.find_methods(lines): | |
| BidirectIndex.analyze_block(lines, m_start + 1, m_end, result) | |
| for m_start, m_end in self.find_methods(lines): | |
| self.analyze_block(lines, m_start + 1, m_end, result) |
Or, if reasonable, we can extract functions, that detect the pattern, but make BidirectIndex.value simply call this function.
| @staticmethod | ||
| def value(filename: str | os.PathLike): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest against use of a static method, at least in this particular case, where it is a public method.
| @staticmethod | |
| def value(filename: str | os.PathLike): | |
| def value(self, filename: str | os.PathLike): |
| if (re.search( | ||
| r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', | ||
| line)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose this deserves a separate function/method.
Maybe extract a function:
# or whatever the name should be
# I can't tell from the regex what it actually does :)
def _is_index_definition(line: str) -> bool:
...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These long regexes are really hard to read. But the good news is that they can be made very readable using re.VERBOSE flag.
Please have a look at the examples here: https://pycon2017.regex.training/re-module#verbose-flag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KachanovYev can you please follow up on this? These regexes are still too difficult to digest unless properly commented like described here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KachanovYev I strongly encourage you to address this issue. Reading complex regexes in the future is difficult and reduces the level of maintainability.
See #842 (comment)
| return for_blocks | ||
|
|
||
| @staticmethod | ||
| def count_inc_dec(lines, var, for_blocks, start, end): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest that all methods apart from value are made internal by adding a leading underscore. Or, if you gravitate towards static methods, then better extract functions, outside the class.
tests and pattern also add new rows the enum class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
13-14: Fix indentation to match the parent item.The indentation is inconsistent with the parent bullet point.
- - increments/decrements inside a for-loop with a local variable of the same name - are ignored (to avoid "fake" cases). + - increments/decrements inside a for-loop with a local variable of the same name + are ignored (to avoid "fake" cases).
🧹 Nitpick comments (5)
aibolit/patterns/bidirect_index/bidirect_index.py (4)
61-66: Simplify nested if statements for better readability.The nested if statement can be combined into a single condition as suggested by the static analysis tool.
- if (re.search( - r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', - line)): - if mstart is None: - mstart = idx - brace = 0 + if (re.search( + r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', + line) and mstart is None): + mstart = idx + brace = 0
82-86: Consider extracting variable declaration detection logic.The variable declaration detection logic is complex and could benefit from extraction into a separate method for better readability and testability.
+ @staticmethod + def _extract_declared_variable(line): + """Extract variable name from declaration line if present.""" + typed_decl = re.match(r'\s*(int|long|byte|short)\s+(\w+)\s*=', line) + if typed_decl: + return typed_decl.group(2) + + untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^;]+;', line) + if untyped_decl: + return untyped_decl.group(1) + + return NoneThen use it in the main method:
- typed_decl = re.match(r'\s*(int|long|byte|short)\s+(\w+)\s*=', line) - untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^;]+;', line) \ - if not typed_decl else None - var = typed_decl.group(2) if typed_decl else (untyped_decl.group(1) - if untyped_decl else None) + var = BidirectIndex._extract_declared_variable(line)
146-155: Consider using verbose regex for better readability.The complex regex patterns for increment/decrement detection are hard to read and maintain. Consider using the
re.VERBOSEflag as suggested in past comments.+ @staticmethod + def _create_increment_pattern(var): + """Create verbose regex pattern for increment detection.""" + return re.compile(rf""" + (?: + \+\+{re.escape(var)} | # Pre-increment: ++var + {re.escape(var)}\+\+ | # Post-increment: var++ + {re.escape(var)}\s*\+=\s*1\b | # Addition assignment: var += 1 + {re.escape(var)}\s*=\s*{re.escape(var)}\s*\+\s*1\b # Assignment: var = var + 1 + ) + """, re.VERBOSE) + + @staticmethod + def _create_decrement_pattern(var): + """Create verbose regex pattern for decrement detection.""" + return re.compile(rf""" + (?: + --{re.escape(var)} | # Pre-decrement: --var + {re.escape(var)}-- | # Post-decrement: var-- + {re.escape(var)}\s*-=\s*1\b | # Subtraction assignment: var -= 1 + {re.escape(var)}\s*=\s*{re.escape(var)}\s*-\s*1\b # Assignment: var = var - 1 + ) + """, re.VERBOSE)
143-156: Extract range checking logic for better readability.The range checking logic for determining if a line is within a for-block could be extracted for clarity.
+ @staticmethod + def _is_in_for_block(line_idx, for_blocks): + """Check if line index is within any for-block range.""" + return any(bstart <= line_idx <= bend for bstart, bend in for_blocks)Then use it in the main method:
- in_for = any(bstart <= k <= bend for (bstart, bend) in for_blocks) + in_for = BidirectIndex._is_in_for_block(k, for_blocks)test/patterns/bidirect_index/test_bidirect_index.py (1)
11-13: Consider adding error handling to the helper method.The
_lines_from_filemethod should handle potential file reading errors gracefully to provide better test failure messages.def _lines_from_file(self, filename): - with open(filename, encoding='utf-8') as f: - return f.readlines() + try: + with open(filename, encoding='utf-8') as f: + return f.readlines() + except FileNotFoundError: + self.fail(f"Test file not found: {filename}") + except IOError as e: + self.fail(f"Error reading test file {filename}: {e}")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
aibolit/ast_framework/ast_node_type.py(3 hunks)aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)test/patterns/bidirect_index/test_bidirect_index.py(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- aibolit/ast_framework/ast_node_type.py
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
Learnt from: SinbadTheSailor2005
PR: cqfn/aibolit#816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called `expressionl` (with 'l' suffix), not `expression`. This is confirmed in `aibolit/ast_framework/_auxiliary_data.py` and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
🪛 Ruff (0.11.9)
aibolit/patterns/bidirect_index/bidirect_index.py
61-64: Use a single if statement instead of nested if statements
(SIM102)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: make
- GitHub Check: e2e-test
- GitHub Check: latexmk
🔇 Additional comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
88-93: Potential infinite loop if end condition is not met.The while loop searches for the next declaration of the same variable but doesn't handle the case where
jexceedsend. This could cause incorrect behavior.- j = i + 1 - while j <= end: - line_ = lines[j] - if re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_): - break - j += 1 + j = i + 1 + while j <= end: + line_ = lines[j] + if re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_): + break + j += 1 + # If we didn't find another declaration, analyze until end + if j > end: + j = end + 1Likely an incorrect or invalid review comment.
test/patterns/bidirect_index/test_bidirect_index.py (1)
16-21: LGTM! Test method correctly adapted to new interface.The test method has been properly updated to use the new
linesparameter instead of filename, and the expected behavior is preserved.
ivanovmg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KachanovYev please look through my comments. I suppose you haven't addressed them yet. Let me know if you have any questions.
| @staticmethod | ||
| def find_methods(lines): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider removing staticmethod everywhere, or extracting a related functionality into separate functions (https://github.com/cqfn/aibolit/pull/842/files#r2176314046).
| @staticmethod | |
| def find_methods(lines): | |
| def _find_methods(self, lines): |
| @staticmethod | ||
| def count_inc_dec(lines, var, for_blocks, start, end): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/cqfn/aibolit/pull/842/files#r2176330476
| @staticmethod | |
| def count_inc_dec(lines, var, for_blocks, start, end): | |
| def _count_inc_dec(self, lines, var, for_blocks, start, end): |
| def value(self, filename: str | os.PathLike): | ||
| def value(self, lines): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that adding a third kind of interface for value is a good idea.
This is what we already have:
def value(self, ast: AST) -> list[LineNumber]: ... # preferable
def value(self, filename: str | os.PathLike) -> list[LineNumber]: ... # soon to be deprecated in favor of the preferable option abovechange doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
aibolit/patterns/bidirect_index/bidirect_index.py (4)
41-43: Use re.VERBOSE flag for complex regex patterns to improve readability.The regex patterns are difficult to understand. Consider using the re.VERBOSE flag with comments:
typed_decl = re.match( r''' \s* # Optional whitespace (int|long|byte|short) # Java primitive types \s+ # Required whitespace (\w+) # Variable name (captured) \s* # Optional whitespace = # Assignment operator ''', line, re.VERBOSE )
127-127: Fix indentation to match the list item structure.- - increments/decrements inside a for-loop with a local variable of the same name - are ignored (to avoid "fake" cases). + - increments/decrements inside a for-loop with a local variable of the same name + are ignored (to avoid "fake" cases).
143-157: Consider following the unified Pattern interface using AST.Based on the PR objectives and past comments, the Pattern interface should accept an AST object rather than source lines. The current implementation using source lines goes against the unified interface direction.
Consider refactoring to:
def value(self, ast: AST) -> list[LineNumber]: """Analyze the AST and return line numbers with bidirectional indices.""" # Implementation using AST traversalThis would align with the codebase's direction towards AST-based analysis rather than text-based parsing.
105-114: Refactor complex regex patterns using re.VERBOSE for better readability.These regex patterns are extremely difficult to parse. Use re.VERBOSE with comments:
inc_pattern = re.compile( rf''' ( \+\+{re.escape(var)} # Pre-increment: ++var |{re.escape(var)}\+\+ # Post-increment: var++ |{re.escape(var)}\s*\+=\s*1\b # Compound assignment: var += 1 |{re.escape(var)}\s*=\s* # Assignment: var = var + 1 {re.escape(var)}\s*\+\s*1\b ) ''', re.VERBOSE ) if inc_pattern.search(line_): inc_outside += 1
🧹 Nitpick comments (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
6-31: Consider making this a private class method or keeping functions consistently outside the class.The function is defined outside the class but used inside. For consistency, either make all helper functions class methods (prefixed with
_for internal use) or keep them all as standalone functions.-def find_methods(lines): +class BidirectIndex: + # ... existing code ... + + def _find_methods(self, lines):Also update the call in line 155:
- for m_start, m_end in find_methods(lines): + for m_start, m_end in self._find_methods(lines):
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
Learnt from: SinbadTheSailor2005
PR: cqfn/aibolit#816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called `expressionl` (with 'l' suffix), not `expression`. This is confirmed in `aibolit/ast_framework/_auxiliary_data.py` and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: pylint
- GitHub Check: pdd
- GitHub Check: make
- GitHub Check: e2e-test
- GitHub Check: latexmk
…thLike) -> list[LineNumber]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (6)
aibolit/patterns/bidirect_index/bidirect_index.py (6)
9-34: Convert standalone function to instance method.Based on past review feedback, standalone functions should be converted to instance methods of the
BidirectIndexclass for better encapsulation and consistency with the codebase design patterns.-def find_methods(lines): +class BidirectIndex: + def _find_methods(self, lines):
23-25: Improve regex readability using re.VERBOSE flag.The complex regex pattern is difficult to read and maintain. Based on past review feedback, use the
re.VERBOSEflag to make it more readable with comments and proper formatting.- if (re.search( - r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', - line)): + method_pattern = re.compile(r''' + (public|private|protected|static|\s)* # Access modifiers + ([\w<>\[\]]+) # Return type + \s+\w+ # Method name + \s*\([^)]*\) # Parameters + \s*{ # Opening brace + ''', re.VERBOSE) + if method_pattern.search(line):
37-73: Convert to instance method and improve structure.This function should be an instance method for consistency with the class design pattern. Also consider breaking down the complex logic into smaller, more focused methods.
-def analyze_block(lines, start, end, result): + def _analyze_block(self, lines, start, end, result):
75-95: Convert to instance method.This function should be an instance method for consistency with the class design pattern.
-def find_for_blocks(lines, var, start, end): + def _find_for_blocks(self, lines, var, start, end):
97-120: Convert to instance method and improve regex readability.This function should be an instance method, and the complex regex patterns should use the
re.VERBOSEflag for better readability.-def count_inc_dec(lines, var, for_blocks, start, end): + def _count_inc_dec(self, lines, var, for_blocks, start, end):For the regex patterns, consider using
re.VERBOSE:- if re.search( - r'(\+\+' + re.escape(var) + r'|' + re.escape(var) + r'\+\+|' + - re.escape(var) + r'\s*\+=\s*1\b|' + - re.escape(var) + r'\s*=\s*' + re.escape(var) + r'\s*\+\s*1\b)', line_): + increment_pattern = re.compile(rf''' + (\+\+{re.escape(var)} | # Pre-increment + {re.escape(var)}\+\+ | # Post-increment + {re.escape(var)}\s*\+=\s*1\b | # += 1 + {re.escape(var)}\s*=\s*{re.escape(var)}\s*\+\s*1\b) # = var + 1 + ''', re.VERBOSE) + if increment_pattern.search(line_):
139-157: Address interface consistency and add error handling.Based on past review feedback, the interface should be updated to match the established pattern in the codebase. Also, file operations need proper error handling.
- def value(self, filename: str | os.PathLike) -> list[LineNumber]: + def value(self, lines: list[str]) -> list[LineNumber]: """ - Analyze the given Java file and return a sorted list of line numbers where a variable + Analyze the given Java source lines and return a sorted list of line numbers where a variable is used as a bidirectional index. Args: - filename (str | os.PathLike): Path to the Java source file. + lines (list[str]): Lines of Java source code to analyze. Returns: list[LineNumber]: Sorted list of line numbers where bidirectional indices are found. """ - - with open(filename, encoding='utf-8') as f: - lines = f.readlines() result: list[int] = [] - for m_start, m_end in find_methods(lines): - analyze_block(lines, m_start + 1, m_end, result) + for m_start, m_end in self._find_methods(lines): + self._analyze_block(lines, m_start + 1, m_end, result) return [LineNumber(n) for n in sorted(result)]
🧹 Nitpick comments (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
44-48: Simplify variable declaration detection logic.The current logic for detecting typed and untyped declarations is complex and could be simplified for better readability.
- typed_decl = re.match(r'\s*(int|long|byte|short)\s+(\w+)\s*=', line) - untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^;]+;', line) \ - if not typed_decl else None - var = typed_decl.group(2) if typed_decl else (untyped_decl.group(1) - if untyped_decl else None) + var = None + typed_decl = re.match(r'\s*(int|long|byte|short)\s+(\w+)\s*=', line) + if typed_decl: + var = typed_decl.group(2) + else: + untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^;]+;', line) + if untyped_decl: + var = untyped_decl.group(1)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)test/patterns/bidirect_index/test_bidirect_index.py(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- test/patterns/bidirect_index/test_bidirect_index.py
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
Learnt from: SinbadTheSailor2005
PR: cqfn/aibolit#816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called `expressionl` (with 'l' suffix), not `expression`. This is confirmed in `aibolit/ast_framework/_auxiliary_data.py` and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: latexmk
- GitHub Check: make
Update method calls to use instance methods. The calls to standalone functions should be updated to use instance methods after converting them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (4)
aibolit/patterns/bidirect_index/bidirect_index.py (4)
16-16: Fix indentation to match the list format.The continuation line should be properly indented to align with the list items above.
- - increments/decrements inside a for-loop with a local variable of the same name are ignored. + - increments/decrements inside a for-loop with a local variable of the same name + are ignored (to avoid "fake" cases).
135-151: Consider refactoring to use AST interface for consistency.As mentioned in previous reviews, this implementation uses filename as input while the unified Pattern interface expects AST. This goes against the project's direction for interface unification.
Consider creating a puzzle to track the refactoring of this implementation to use
ast_framework.ASTrather thanfilenameas suggested in the past review comments.
122-131: Use re.VERBOSE flag to improve regex readability.These complex regex patterns are difficult to read and maintain. Consider using the re.VERBOSE flag with comments as previously suggested.
# Define patterns with re.VERBOSE for better readability INCREMENT_PATTERN = re.compile(rf''' \+\+{re.escape(var)} | # prefix increment {re.escape(var)}\+\+ | # postfix increment {re.escape(var)}\s*\+=\s*1\b | # compound assignment {re.escape(var)}\s*=\s*{re.escape(var)}\s*\+\s*1\b # assignment ''', re.VERBOSE) DECREMENT_PATTERN = re.compile(rf''' --{re.escape(var)} | # prefix decrement {re.escape(var)}-- | # postfix decrement {re.escape(var)}\s*-=\s*1\b | # compound assignment {re.escape(var)}\s*=\s*{re.escape(var)}\s*-\s*1\b # assignment ''', re.VERBOSE) # Then use: if INCREMENT_PATTERN.search(line_): inc_outside += 1 if DECREMENT_PATTERN.search(line_): dec_outside += 1
146-147: Add error handling for file operations.The file operations should handle potential errors gracefully as previously suggested.
- with open(filename, encoding='utf-8') as f: - lines = f.readlines() + try: + with open(filename, encoding='utf-8') as f: + lines = f.readlines() + except (FileNotFoundError, IOError) as e: + raise ValueError(f"Failed to read file {filename}: {e}")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
Learnt from: SinbadTheSailor2005
PR: cqfn/aibolit#816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called `expressionl` (with 'l' suffix), not `expression`. This is confirmed in `aibolit/ast_framework/_auxiliary_data.py` and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
🧬 Code Graph Analysis (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
aibolit/ast_framework/ast_node.py (1)
line(41-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: latexmk
- GitHub Check: make
| untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^;]+;', line) \ | ||
| if not typed_decl else None | ||
| var = typed_decl.group(2) if typed_decl else (untyped_decl.group(1) | ||
| if untyped_decl else None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve untyped variable detection to avoid false positives.
The current regex for untyped declarations could match invalid patterns. In Java, variable reassignments must be preceded by a declaration. Consider checking if the variable was previously declared.
- untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^;]+;', line) \
- if not typed_decl else None
+ # Only match simple reassignments, not method calls or field access
+ untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^=;]+;', line) \
+ if not typed_decl else None📝 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.
| untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^;]+;', line) \ | |
| if not typed_decl else None | |
| var = typed_decl.group(2) if typed_decl else (untyped_decl.group(1) | |
| if untyped_decl else None) | |
| # Only match simple reassignments, not method calls or field access | |
| untyped_decl = re.match(r'^\s*(\w+)\s*=\s*[^=;]+;', line) \ | |
| if not typed_decl else None | |
| var = typed_decl.group(2) if typed_decl else (untyped_decl.group(1) | |
| if untyped_decl else None) |
🤖 Prompt for AI Agents
In aibolit/patterns/bidirect_index/bidirect_index.py around lines 61 to 64, the
regex used to detect untyped variable declarations may produce false positives
by matching invalid patterns. To fix this, enhance the logic to verify that the
variable identified by the regex was previously declared before treating it as a
valid untyped declaration. This can be done by maintaining a record of declared
variables and checking against it before assigning the variable from the
untyped_decl match.
| if re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_): | ||
| break | ||
| j += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for both typed and untyped redeclarations when finding variable scope.
The current implementation only checks for typed redeclarations, missing cases where the variable is reassigned without a type.
- if re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_):
+ # Check for both typed declarations and simple reassignments
+ if (re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_) or
+ re.match(r'^\s*' + re.escape(var) + r'\s*=\s*[^=;]+;', line_)):📝 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.
| if re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_): | |
| break | |
| j += 1 | |
| # Check for both typed declarations and simple reassignments | |
| if (re.match(r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_) or | |
| re.match(r'^\s*' + re.escape(var) + r'\s*=\s*[^=;]+;', line_)): | |
| break | |
| j += 1 |
🤖 Prompt for AI Agents
In aibolit/patterns/bidirect_index/bidirect_index.py around lines 69 to 71, the
code only checks for typed redeclarations of variables but misses untyped
reassignments. Update the condition to also detect lines where the variable is
reassigned without a type, such as simple assignments, by adding a check for
untyped redeclarations alongside the existing typed check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
149-150: Add error handling for file operations.The method should handle potential file reading errors gracefully.
- with open(filename, encoding='utf-8') as f: - lines = f.readlines() + try: + with open(filename, encoding='utf-8') as f: + lines = f.readlines() + except (FileNotFoundError, IOError) as e: + raise ValueError(f"Failed to read file {filename}: {e}")
🧹 Nitpick comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (2)
40-43: Consider handling methods with opening braces on separate lines.The current regex assumes the opening brace
{is on the same line as the method signature. In Java, it's valid to have:public void myMethod() { // code }Consider updating the logic to handle this case:
- if (re.search( - r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', - line)): + # First check for method signature + method_sig = re.search( + r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*$', + line) + # Then check if it has brace on same line or next line + if method_sig or (re.search( + r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', + line)):
125-134: Consider improving regex readability.The increment/decrement detection regexes are comprehensive but difficult to read.
Consider using the
re.VERBOSEflag for better readability:inc_pattern = re.compile(rf''' \+\+{re.escape(var)} | # pre-increment {re.escape(var)}\+\+ | # post-increment {re.escape(var)}\s*\+=\s*1\b | # compound assignment {re.escape(var)}\s*=\s*{re.escape(var)}\s*\+\s*1\b # reassignment ''', re.VERBOSE)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
Learnt from: SinbadTheSailor2005
PR: cqfn/aibolit#816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called `expressionl` (with 'l' suffix), not `expression`. This is confirmed in `aibolit/ast_framework/_auxiliary_data.py` and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
🔇 Additional comments (1)
aibolit/patterns/bidirect_index/bidirect_index.py (1)
91-112: LGTM!The method correctly identifies for-loop blocks with proper handling of all numeric types and brace counting.
|
@ivanovmg |
ivanovmg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KachanovYev please have a look at my review.
- Could you move
valueto the top of the class, so that it is the public method, that is visible first to the reader? - Keep only essential changes in the test files, while moving those desirable into another PR.
- Comment regexes, because it is extremely difficult to understand them. I suggest using
re.VERBOSEflag
While issues 1 and 2 are not that critical, issue 3 is pretty concerning. Could you address it?
| BidirectIndex().value(Path(self.dir_path, 'BidirectIndexIncreaseDecrease.java')), | ||
| BidirectIndex().value(file_path), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest that we leave the changes in the test files outside of the present PR (apparently, except making the test non-skipped). It is OK to do it in another one, dedicated to the refactoring if necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
| k += 1 | ||
| return inc_outside, dec_outside | ||
|
|
||
| def value(self, filename: str | os.PathLike) -> list[LineNumber]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to keep the public method on top, while having the remaining helper methods defined below. This way we will read the code like the newspaper - header first, then details, then more details, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
| if (re.search( | ||
| r'(public|private|protected|static|\s)*([\w<>\[\]]+)\s+\w+\s*\([^)]*\)\s*{', | ||
| line)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KachanovYev I strongly encourage you to address this issue. Reading complex regexes in the future is difficult and reduces the level of maintainability.
See #842 (comment)
added re.VERBOSE for regex update doc string
resolved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
aibolit/patterns/bidirect_index/bidirect_index.py (4)
16-16: Fix docstring indentation for consistency.The list item indentation doesn't match the other items.
- - increments/decrements inside a for-loop with a local variable of the same name are ignored. + - increments/decrements inside a for-loop with a local variable of the same name + are ignored (to avoid "fake" cases).
23-39: Consider migrating to AST-based interface.Per previous discussions, the preferred interface should use
ast_framework.ASTrather than filename as input parameter to align with the unified Pattern interface direction.- def value(self, filename: str | os.PathLike) -> list[LineNumber]: + def value(self, ast: AST) -> list[LineNumber]:Additionally, add error handling for file operations:
- with open(filename, encoding='utf-8') as f: - lines = f.readlines() + try: + with open(filename, encoding='utf-8') as f: + lines = f.readlines() + except (FileNotFoundError, IOError) as e: + raise ValueError(f"Failed to read file {filename}: {e}")
87-90: Improve untyped variable detection to avoid false positives.The current regex for untyped declarations could match invalid patterns like method calls. Consider a more restrictive pattern:
- untyped_decl = re.match( - r'^\s*(\w+)\s*=\s*[^=;]+;', line) if not typed_decl else None + # Only match simple reassignments, not method calls or field access + untyped_decl = re.match( + r'^\s*(\w+)\s*=\s*[^=;]+;', line) if not typed_decl else None
95-99: Include untyped reassignments when finding variable scope.The current implementation only checks for typed redeclarations, missing cases where variables are reassigned without type:
- # Check for re-declaration of variable with type - if re.match( - r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_): + # Check for both typed declarations and simple reassignments + if (re.match( + r'\s*(int|long|byte|short)\s+' + re.escape(var) + r'\s*=', line_) or + re.match(r'^\s*' + re.escape(var) + r'\s*=\s*[^=;]+;', line_)):
🧹 Nitpick comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (2)
41-74: Well-structured method detection with readable regex.Good use of
re.VERBOSEfor regex readability. Minor typo in comment:- ([\w<>\[\]]+) # # return type + ([\w<>\[\]]+) # return type
1-175: Consider future refactoring to AST-based implementation.While this regex-based implementation is functional and covers all test cases, it would benefit from being refactored to use the
ast_framework.ASTfor better maintainability and consistency with other patterns in the codebase. This could be tracked as a technical debt item.Would you like me to create an issue to track the refactoring from regex-based to AST-based implementation?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
aibolit/patterns/bidirect_index/bidirect_index.py(1 hunks)test/patterns/bidirect_index/test_bidirect_index.py(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- test/patterns/bidirect_index/test_bidirect_index.py
🧰 Additional context used
🧠 Learnings (1)
aibolit/patterns/bidirect_index/bidirect_index.py (3)
Learnt from: SinbadTheSailor2005
PR: #816
File: aibolit/patterns/loop_outsider/loop_outsider.py:0-0
Timestamp: 2025-06-08T11:50:22.673Z
Learning: In the aibolit codebase, ASTNodeType.ASSIGNMENT nodes have an attribute called expressionl (with 'l' suffix), not expression. This is confirmed in aibolit/ast_framework/_auxiliary_data.py and used consistently throughout existing patterns like mutable_index and many_primary_ctors.
Learnt from: Error10556
PR: #866
File: test/metrics/npath/javacode/IfWithIfElseInsideOuterElse.java:6-8
Timestamp: 2025-07-15T19:00:29.065Z
Learning: In the aibolit project, syntax correctness (such as using boolean instead of bool) is not a concern for test files under test/metrics/npath/javacode/ directory since they are code snippets for NPath complexity testing, not standalone compilable Java classes.
Learnt from: Error10556
PR: #866
File: test/metrics/npath/javacode/OneIfStatement.java:7-7
Timestamp: 2025-07-15T19:00:20.609Z
Learning: In the aibolit project, syntax errors in test files under test/metrics/npath/javacode/ directory (such as using bool instead of boolean) are not relevant concerns since these are code snippets used for NPath complexity testing, not standalone compilable Java classes.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: make
- GitHub Check: latexmk
🔇 Additional comments (2)
aibolit/patterns/bidirect_index/bidirect_index.py (2)
117-142: Well-implemented for-loop detection.Good use of
re.VERBOSEfor regex readability and proper handling of all numeric types.
144-174: Comprehensive increment/decrement detection.Excellent use of
re.VERBOSEwith clear pattern documentation. The logic correctly excludes counts within for-loops that declare local variables.
|
@yegor256 |

#742
Implement bidirect index pattern
tests is working
add docs for code
Summary by CodeRabbit
New Features
Documentation
Tests