Skip to content

Extend MvnFreeNPathMetric to handle while loops#861

Merged
yegor256 merged 15 commits into
cqfn:masterfrom
ivanovmg:852
Jul 13, 2025
Merged

Extend MvnFreeNPathMetric to handle while loops#861
yegor256 merged 15 commits into
cqfn:masterfrom
ivanovmg:852

Conversation

@ivanovmg

@ivanovmg ivanovmg commented Jul 9, 2025

Copy link
Copy Markdown
Contributor

In this PR we extend MvnFreeNPathMetric to handle while loops.
All test cases are checked against checkstyle.

Closes #852

Summary by CodeRabbit

  • New Features

    • Improved support for analyzing while loops when calculating NPath complexity.
  • Tests

    • Added new tests to verify NPath complexity calculations for various while loop scenarios, including empty, nested, and conditional while loops.
    • Introduced tests highlighting known issues with moderate and high complexity cases involving conditional and switch statements.
    • Refactored test helpers for clearer and more reusable metric calculations.

@coderabbitai

coderabbitai Bot commented Jul 9, 2025

Copy link
Copy Markdown

"""

Walkthrough

The code introduces support for while loops in the MvnFreeNPathMetric class by adding a new handler and updating the dispatcher logic. It also adds comprehensive tests for various while loop scenarios to the test suite, ensuring correct NPath complexity calculations for these constructs.

Changes

File(s) Change Summary
aibolit/metrics/npath/main.py Refactored node dispatch logic, added _while_loop_npath and _condition_npath methods, updated handlers for while loops.
test/metrics/npath/test_all_types.py Added multiple tests for while loop handling, covering simple, compound, nested, and empty loop cases; introduced helper methods and xfail tests for known issues.

Sequence Diagram(s)

sequenceDiagram
    participant TestSuite
    participant MvnFreeNPathMetric
    participant ASTNode

    TestSuite->>MvnFreeNPathMetric: compute NPath (Java code with while loop)
    MvnFreeNPathMetric->>ASTNode: traverse AST nodes
    MvnFreeNPathMetric->>MvnFreeNPathMetric: _node_npath(node)
    alt node is WHILE_STATEMENT
        MvnFreeNPathMetric->>MvnFreeNPathMetric: _while_loop_npath(node)
        MvnFreeNPathMetric->>MvnFreeNPathMetric: _condition_npath(node)
    else other node types
        MvnFreeNPathMetric->>MvnFreeNPathMetric: appropriate handler
    end
    MvnFreeNPathMetric-->>TestSuite: return NPath value
Loading

Assessment against linked issues

Objective Addressed Explanation
Add support for while loops in MvnFreeNPathMetric (#852)
Extend test suite for while loop handling in NPath metric (#852)

Poem

A rabbit hopped through loops of "while",
Adding up paths with metric style.
Now tests abound for every case,
From empty loops to nested space.
With paws on code and nose for flow,
The NPath count is good to go!
🐇💻✨
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80dccc2 and 2f67c86.

📒 Files selected for processing (2)
  • aibolit/metrics/npath/main.py (2 hunks)
  • test/metrics/npath/test_all_types.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • aibolit/metrics/npath/main.py
⏰ 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
🔇 Additional comments (4)
test/metrics/npath/test_all_types.py (4)

4-5: LGTM: Import additions support the new functionality.

The new imports (os, pathlib, build_ast) are necessary for the file path handling and AST building functionality introduced in the helper methods.

Also applies to: 12-12


438-541: Comprehensive while loop test coverage with good complexity calculations.

The while loop test suite covers essential scenarios:

  • Basic while loops (complexity 2)
  • While loops with conditional statements (complexity 3)
  • While loops with compound conditions (OR: 6, AND: 3)
  • Control flow with break statements (complexity 3)
  • Edge cases like empty loops (complexity 2)
  • Nested while loops (complexity 3)

The complexity calculations appear correct based on NPath complexity rules where loops add +1 for the condition and compound conditions add additional complexity.


542-588: Well-documented xfail tests for known implementation issues.

The xfail tests appropriately document known problems in the MvnFreeNPathMetric implementation:

  • Includes detailed TODO comments with time estimates
  • Provides clear explanations of the expected behavior
  • Documents the verification process using external checkstyle tool
  • Uses strict=True to ensure tests fail for the right reasons

The complexity value of 288 for Foo.java is properly verified against the official checkstyle tool.


590-608: Excellent refactoring of helper methods.

The helper method refactoring improves code organization:

  • _filepath() provides a clean utility for test file resolution
  • _metric() centralizes the metric calculation logic
  • _value() is simplified to use the new _metric() method
  • _value_from_filepath() enables testing with external files

This refactoring eliminates code duplication and makes the test methods more maintainable.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

ASTNodeType.BINARY_OPERATION: self._binary_expression_npath,
}
handler = dispatcher.get(node.node_type, lambda n: self._sequence_npath(n.children))
return handler(node)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Here I had to perform refactoring towards this dictionary dispatch pattern to avoid too many returns.
In particular, the linter was complaining about 7 return statements with a max of 6.

aibolit/metrics/npath/main.py:102:4: R0911: Too many return statements (7/6) (too-many-return-statements)

We can consider reverting and silencing the warning if the original implementation is considered more readable.

Alternatively, we can simply write resulting value to a variable result and then only return result once.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ivanovmg IMO the dictionary dispatching is pretty clean and easy to read. Maybe we could consider add explicit if when default handle is needed but the lambda is also OK for me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@AntonProkopyev I extracted a separate function default_handler. I hope it helps convey the message better than an anonymous function. Does it look OK?

@ivanovmg

ivanovmg commented Jul 9, 2025

Copy link
Copy Markdown
Contributor Author

@AntonProkopyev could you review, please?

@ivanovmg

Copy link
Copy Markdown
Contributor Author

@AntonProkopyev could you review, please?

@yegor256 FYI

@AntonProkopyev

Copy link
Copy Markdown
Contributor

@AntonProkopyev could you review, please?

@ivanovmg Will look till end of day.

@AntonProkopyev AntonProkopyev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ivanovmg The PR is easy to read and understand. I have some minor considerations but the PR is well enough to get merged.

Comment thread aibolit/metrics/npath/main.py Outdated
Comment on lines +138 to +142
return body_npath + self._condition_npath(node.control)

def _while_loop_npath(self, node: ASTNode) -> int:
body_npath = self._node_npath(node.body)
return body_npath + self._condition_npath(node)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ivanovmg According to principle of least astonishment I suggest to calculate npath for condition before body part.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@AntonProkopyev I moved the statements around to first calculate condition_npath and only then body_npath. Is it what you meant?

''').strip()
assert self._value(content) == 5

def test_simple_while_loops(self) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ivanovmg The tests are clear and sufficient! I would also suggest making sure that the tests from the previous implementation work for the new implementation.

@ivanovmg ivanovmg Jul 11, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@AntonProkopyev the tests for the existing case files do not actually pass for MvnFreeNPathMetric as of now. I added the corresponding puzzles. Need some expertise in NPath complexity to fix the issues, I suppose. I tried to figure those out, but failed miserably.

ASTNodeType.BINARY_OPERATION: self._binary_expression_npath,
}
handler = dispatcher.get(node.node_type, lambda n: self._sequence_npath(n.children))
return handler(node)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ivanovmg IMO the dictionary dispatching is pretty clean and easy to read. Maybe we could consider add explicit if when default handle is needed but the lambda is also OK for me.

@ivanovmg

Copy link
Copy Markdown
Contributor Author

@ivanovmg The PR is easy to read and understand. I have some minor considerations but the PR is well enough to get merged.

@AntonProkopyev I have addressed the issues you raised. Please have a look when you can.

@AntonProkopyev

Copy link
Copy Markdown
Contributor

@ivanovmg @yegor256 LGTM

@yegor256 yegor256 merged commit b5d1b0a into cqfn:master Jul 13, 2025
19 checks passed
@yegor256

Copy link
Copy Markdown
Member

@ivanovmg thanks!

@0crat

0crat commented Jul 13, 2025

Copy link
Copy Markdown
Collaborator

@ivanovmg Hey there! 👋 Great job on your contribution! You've earned +4 points for this one. Here's the breakdown: +16 as a base, +10.55 for your 211 hits-of-code (though we had to deduct 8 for going over 200), and -16 for no code review. We bumped it up to 4 to keep you motivated. Remember, code reviews are crucial, and keeping contributions smaller can boost your points next time. Your balance is now +115. Keep the code coming!

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.

MvnFreeNPathMetric fails to handle while loops

4 participants