Skip to content

Conversation

@svenjeschmitt-ops
Copy link
Contributor

@svenjeschmitt-ops svenjeschmitt-ops commented Dec 18, 2025

Description

This pull request improves the debugging highlight functionality and fixes an issue where greyed-out highlights were not correctly reset between executions.

When an issue is detected in the code, the extension highlights the affected line in red and displays a descriptive error message explaining the nature of the problem. This visual feedback helps users quickly identify both the location of the error and its underlying cause.

A second part of the implementation concerns the assertion functionality. After an assertion failure, the extension greys out all lines of code that cannot have caused the error. While this behaviour is intended, the greyed-out highlights were previously not cleared when a new execution started. As a result, lines remained greyed out until the editor was closed and reopened.

Overall, the highlighting functionality provides an efficient way to show where an error occurred and what caused it. This change introduces an explicit reset of all greyed-out highlights on launch and restart events, ensuring that each execution starts with a clean editor state.

Fixes #(issue)

Checklist:

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

@coderabbitai
Copy link

coderabbitai bot commented Dec 18, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added error highlighting with precise location information for debugging assistance.
    • Enhanced parse error reporting to display exact line and column positions.
    • Introduced runtime state notifications during simulation execution.
  • Improvements

    • Strengthened state management during debug sessions to prevent premature exits.
    • Improved error handling during code loading and validation processes.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Queues parse errors and emits structured highlight events from the DAP server; adds HighlightError DAP message type and helpers; launch/restart now queue parse errors instead of failing inline; Python bindings capture loader stderr; C++ parser adds validation/position utilities and simulator resets; tests for coordinate/highlight behavior added.

Changes

Cohort / File(s) Summary
DAP server: state, highlight flow & messaging
python/mqt/debugger/dap/dap_server.py
Added pending_highlights and _prevent_exit; parse-error queuing and parse-location parsing (queue_parse_error, _parse_error_location, _build_parse_error_highlight, _flatten_message_parts); highlight builders (collect_highlight_entries, _build_highlight_entry, _format_highlight_reason); message helpers (send_message_simple, send_message_hierarchy now accepts category, send_state); post-command highlight emission and conditional suppression of ExitedDAPEvent.
DAP messages: new event & exports
python/mqt/debugger/dap/messages/highlight_error_dap_message.py, python/mqt/debugger/dap/messages/__init__.py
New HighlightError DAP event class with normalization, validation, and encode(); exported in module __all__.
Launch/Restart handling: parse-error flow
python/mqt/debugger/dap/messages/launch_dap_message.py, python/mqt/debugger/dap/messages/restart_dap_message.py
Set server.source_file earlier; capture RuntimeError from load_code, queue parse errors via server.queue_parse_error and track parsed_successfully; suppress reset exceptions on parse failure; avoid returning inline failure responses.
Bindings: capture Python-side load errors
bindings/InterfaceBindings.cpp
Redirect/capture Python stderr during loadCode (pybind11 iostream), throw runtime_error with captured message on failure; added IO includes.
C++ parser: target validation & error formatting
src/common/parsing/CodePreprocessing.cpp
Added utilities (isDigits, LineColumn, line/column helpers, formatParseError, validateTargets), safer numeric parsing for register declarations, improved start-position computation, centralized target validation, and richer parse error messages; replaced inline target checks with validateTargets.
Simulator backend: reset & readiness
src/backend/dd/DDSimDebug.cpp
Mark simulator not ready on load and clear per-instruction caches/maps; guard RunAll to return ERROR if simulator not ready; adjust breakpoint handling for positions before current instruction.
Tests
test/python/test_dap_server.py
Added tests for code_pos_to_coordinates and collect_highlight_entries verifying coordinate computation and highlight bounds.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DAPServer
  participant ParserLoader as Parser/Loader
  participant Simulation
  participant Editor

  Client->>DAPServer: launch/restart request
  DAPServer->>ParserLoader: load_code(program)
  alt parse success
    ParserLoader-->>DAPServer: OK
    DAPServer->>Simulation: run (unless stopOnEntry)
    Simulation-->>DAPServer: diagnostics / assertion events
    DAPServer->>DAPServer: collect_highlight_entries()
    Note right of DAPServer: pending_highlights populated
    DAPServer-->>Editor: HighlightError (highlights + source)
    DAPServer->>Client: send_state / messages
  else parse failure
    ParserLoader-->>DAPServer: raises RuntimeError (detail)
    DAPServer->>DAPServer: queue_parse_error(detail)
    DAPServer-->>Editor: HighlightError (from queued parse error)
    DAPServer->>Simulation: reset (exceptions suppressed)
    DAPServer->>Client: send_state (Finished/Assertion failed)
  end
  Note over Editor,DAPServer: _prevent_exit controls ExitedDAPEvent emission
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰
I dug a tunnel through the trace,
Found parse crumbs in every place.
Highlights glow where bugs reside,
Queued and shown — no need to hide.
Hop, report, and then we stride!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: clearing assertion greyed-out lines on restart and highlighting issue locations.
Description check ✅ Passed The description covers the main improvements and bug fix, explains the user-facing impact, and includes a checklist. However, the Fixes #(issue) placeholder was not completed with an actual issue number.
✨ 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

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

Copy link

@coderabbitai coderabbitai bot left a 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/mqt/debugger/dap/messages/restart_dap_message.py (1)

57-88: Consider extracting common parsing logic shared with LaunchDAPMessage.

The handle() method shares significant logic with LaunchDAPMessage.handle() (file reading, error handling, conditional simulation run). Consider extracting a shared helper to reduce duplication.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ef979f and 7b8fa09.

📒 Files selected for processing (8)
  • bindings/InterfaceBindings.cpp
  • python/mqt/debugger/dap/dap_server.py
  • python/mqt/debugger/dap/messages/__init__.py
  • python/mqt/debugger/dap/messages/highlight_error_dap_message.py
  • python/mqt/debugger/dap/messages/launch_dap_message.py
  • python/mqt/debugger/dap/messages/restart_dap_message.py
  • src/common/parsing/CodePreprocessing.cpp
  • test/python/test_dap_server.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-15T01:54:22.129Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/dd/register_matrix_dds.cpp:64-109
Timestamp: 2025-12-15T01:54:22.129Z
Learning: In the munich-quantum-toolkit/core repository, after migrating to nanobind, docstrings for Python bindings are now added directly in the C++ binding code (using R"pb(...)pb" syntax) and stub files (.pyi) are auto-generated using the `bindings/generate-stubs.sh` script. This replaces the previous pybind11 approach where docstrings were manually maintained in stub files.

Applied to files:

  • bindings/InterfaceBindings.cpp
🧬 Code graph analysis (6)
python/mqt/debugger/dap/messages/launch_dap_message.py (1)
python/mqt/debugger/pydebugger.pyi (3)
  • load_code (130-135)
  • run_simulation (183-188)
  • reset_simulation (193-198)
src/common/parsing/CodePreprocessing.cpp (3)
include/common/parsing/ParsingError.hpp (1)
  • ParsingError (32-32)
src/common/parsing/ParsingError.cpp (1)
  • ParsingError (23-23)
include/common/parsing/CodePreprocessing.hpp (1)
  • isVariableDeclaration (296-296)
test/python/test_dap_server.py (2)
python/mqt/debugger/dap/dap_server.py (2)
  • code_pos_to_coordinates (376-402)
  • collect_highlight_entries (444-470)
python/mqt/debugger/pydebugger.pyi (3)
  • potential_error_causes (527-535)
  • get_instruction_position (274-284)
  • get_diagnostics (419-424)
python/mqt/debugger/dap/messages/highlight_error_dap_message.py (4)
python/mqt/debugger/dap/messages/dap_event.py (1)
  • DAPEvent (17-41)
python/mqt/debugger/dap/messages/launch_dap_message.py (1)
  • validate (44-55)
python/mqt/debugger/dap/messages/restart_dap_message.py (1)
  • validate (44-55)
python/mqt/debugger/dap/dap_server.py (1)
  • start (122-142)
python/mqt/debugger/dap/messages/__init__.py (1)
python/mqt/debugger/dap/messages/highlight_error_dap_message.py (1)
  • HighlightError (22-140)
python/mqt/debugger/dap/messages/restart_dap_message.py (1)
python/mqt/debugger/pydebugger.pyi (3)
  • load_code (130-135)
  • run_simulation (183-188)
  • reset_simulation (193-198)
🪛 Cppcheck (2.19.0)
src/common/parsing/CodePreprocessing.cpp

[information] 23-23: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)

bindings/InterfaceBindings.cpp

[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)

🪛 Ruff (0.14.10)
python/mqt/debugger/dap/messages/launch_dap_message.py

69-69: Boolean positional value in function call

(FBT003)

python/mqt/debugger/dap/messages/restart_dap_message.py

70-70: Boolean positional value in function call

(FBT003)

⏰ 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). (11)
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🇨‌ Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
🔇 Additional comments (20)
bindings/InterfaceBindings.cpp (1)

179-192: LGTM! The error capture pattern is well-implemented.

The use of py::scoped_ostream_redirect for RAII-style stderr capture and the fallback to a generic error message when the captured output is empty is a clean approach for surfacing C++ parsing errors to Python callers.

src/common/parsing/CodePreprocessing.cpp (5)

100-145: Well-structured validation with clear error messages.

The centralized validateTargets function properly validates qubit target syntax and bounds, with informative error messages that include the problematic target and context. Good use of early returns for efficiency.


476-490: LGTM! Robust register size parsing.

The combination of isDigits validation and try/catch around std::stoul provides defense-in-depth against malformed input and overflow conditions.


446-450: Improved position accuracy for diagnostics.

The updated trueStart calculation correctly identifies the first non-whitespace character for more precise error reporting.


554-563: LGTM! Proper integration of target validation.

The validation is correctly applied to assertions and regular instructions while appropriately skipping variable declarations. The context strings provide meaningful differentiation in error messages.


50-64: No action needed—lineColumnForOffset correctly handles offset values at and near code boundaries.

The function uses std::string::rfind('\n', offset) which has well-defined behavior even when offset == code.size(). The C++ standard guarantees this is safe, and the subsequent column calculation works correctly for valid positions. No bounds checking or code changes are required.

Likely an incorrect or invalid review comment.

python/mqt/debugger/dap/messages/__init__.py (1)

24-24: LGTM! Clean module export.

The new HighlightError class is properly imported and added to __all__ following the existing alphabetical ordering convention.

Also applies to: 61-61

python/mqt/debugger/dap/messages/restart_dap_message.py (1)

73-82: Verify: Response returns success=True even when parsing fails.

The handler queues the parse error and returns "success": True even when load_code raises a RuntimeError. This defers error reporting to the highlight event mechanism. Confirm this aligns with the DAP protocol expectations for the restart command.

python/mqt/debugger/dap/messages/launch_dap_message.py (1)

67-81: LGTM! Error handling aligns with restart handler pattern.

The parse error handling with queue_parse_error and the use of contextlib.suppress for the reset fallback path is consistent and well-implemented.

test/python/test_dap_server.py (2)

18-25: LGTM! Good test for coordinate boundary handling.

This test correctly validates that the column calculation for a newline position returns the correct 1-based column value for the end of the line.


28-44: Good regression test for the highlight boundary fix.

This test validates the core fix by ensuring highlight entries don't incorrectly span to the next instruction. The use of SimpleNamespace for lightweight mocking is appropriate for unit tests.

python/mqt/debugger/dap/messages/highlight_error_dap_message.py (2)

102-140: LGTM! Position handling is correct.

The _normalize_position method properly validates and extracts line/column values, and _later_than correctly implements lexicographic position comparison.


22-39: LGTM! Clean implementation of the custom DAP event.

The HighlightError class follows the established DAPEvent pattern, properly normalizing inputs in the constructor before calling super().__init__() which triggers validation.

python/mqt/debugger/dap/dap_server.py (7)

317-323: LGTM! reset_gray_out correctly clears highlights on new execution.

This directly addresses the PR objective by sending an empty gray-out event on launch/restart, ensuring a clean editor state.


385-402: LGTM! Improved boundary handling in coordinate calculation.

The pos <= len(line_code) condition and the else clause for handling positions beyond the text correctly address edge cases for newline positions and end-of-file scenarios.


535-544: Verify: Backward search terminates correctly.

The while loop searching for a previous non-empty line correctly decrements prev_index and has a proper termination condition (prev_index >= 0). The logic is sound.


444-470: LGTM! Robust highlight collection with appropriate fallback.

The method correctly handles the case where no specific error causes are found by falling back to a generic assertion failure highlight. The RuntimeError catch ensures resilience against diagnostics failures.


262-280: LGTM! Proper coordination of highlight emission and exit events.

The _prevent_exit flag correctly prevents premature ExitedDAPEvent emission when highlights are pending, and is reset on subsequent stepping commands to restore normal behavior.


559-582: LGTM! Clean recursive flattening of message structures.

The helper correctly handles various nested structures while filtering empty strings and appropriately converting other types to strings.


653-668: send_state method is unused in the codebase.

This method is well-implemented but has no internal calls. Verify whether it's intended as part of the public DAP API, required for external clients, or should be removed.

Comment on lines 41 to 66
def validate(self) -> None:
"""Validate the 'highlightError' DAP event message after creation."""
if not self.highlights:
msg = "At least one highlight entry is required to show the issue location."
raise ValueError(msg)

for highlight in self.highlights:
if "message" not in highlight or not str(highlight["message"]).strip():
msg = "Each highlight entry must contain a descriptive 'message'."
raise ValueError(msg)
highlight_range = highlight.get("range")
if not isinstance(highlight_range, dict):
msg = "Each highlight entry must provide a 'range' dictionary."
raise TypeError(msg)
start = highlight_range.get("start")
end = highlight_range.get("end")
if not isinstance(start, dict) or not isinstance(end, dict):
msg = "Highlight ranges must define 'start' and 'end' coordinates."
raise TypeError(msg)
if self._later_than(start, end):
msg = "Highlight range 'end' must not precede 'start'."
raise ValueError(msg)

if "name" not in self.source or "path" not in self.source:
msg = "Source information must at least expose 'name' and 'path'."
raise ValueError(msg)
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Redundant validation between normalize and validate methods.

The _normalize_highlight method already validates ranges and throws on invalid input (lines 77-89), but validate() re-checks similar conditions (lines 51-62). Consider consolidating to avoid duplicate validation logic.

🤖 Prompt for AI Agents
In python/mqt/debugger/dap/messages/highlight_error_dap_message.py around lines
41-66, validate() duplicates range and message checks already performed in
_normalize_highlight (lines ~77-89); remove the redundant per-highlight
range/message checks from validate() and either call/ensure _normalize_highlight
has been run before validate() (or invoke it at the start of validate()),
keeping only high-level checks in validate() (e.g., non-empty highlights and
required source fields). Ensure error types/messages remain consistent by
centralizing the detailed validation in _normalize_highlight and leaving
validate() to assert overall structure.

renovate bot and others added 4 commits December 24, 2025 11:31
…-quantum-toolkit#234)

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This reverts commit 83e928a, reversing
changes made to 68a8ffe.
@svenjeschmitt-ops svenjeschmitt-ops changed the title 🐛 assertion issue solved 🚀 highlight issue and assertion issue solved Dec 30, 2025
Copy link

@coderabbitai coderabbitai bot left a 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/mqt/debugger/dap/dap_server.py (1)

373-389: Handle empty source code edge case in code_pos_to_coordinates.

When source_code is empty, scopes_dap_message calls this method with end_pos = -1, causing it to return line = -1 when lines_start_at_one is False. This produces invalid line numbers in DAP responses. Add an early check to handle empty source code explicitly or validate that pos >= 0.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b8fa09 and 81801fa.

📒 Files selected for processing (1)
  • python/mqt/debugger/dap/dap_server.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.
⏰ 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). (15)
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Release) / 🍎 macos-14 clang Release
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Debug) / 🍎 macos-14 clang Debug
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🇨‌ Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
🔇 Additional comments (13)
python/mqt/debugger/dap/dap_server.py (13)

118-119: LGTM! Clean state management additions.

The new fields pending_highlights and _prevent_exit are properly initialized and serve the PR objective of managing highlight lifecycle and preventing premature exit when errors need to be displayed.


172-186: LGTM! Proper reset of exit prevention flag.

Resetting _prevent_exit to False when execution commands are issued ensures that the debugger can exit normally after the user takes action, aligning with the PR objective of preventing exit only while highlights are being displayed.


257-261: LGTM! Proper highlight emission and exit prevention.

The logic correctly sends queued highlights, clears the pending list, and sets the exit prevention flag to ensure users can review the highlighted errors before the debugger exits.


271-275: LGTM! Exit gated by highlight state.

The modified condition correctly prevents the debugger from exiting when _prevent_exit is True, ensuring highlights remain visible until the user continues or steps.


355-361: LGTM! Proper error categorization and highlight emission.

Using "stderr" for assertion failure messages is appropriate, and the highlight collection logic ensures error causes are properly displayed to the user.


431-457: LGTM! Robust error cause collection with fallback.

The method properly handles missing diagnostics and ensures at least the failing instruction is highlighted when no specific error causes are identified.


459-478: LGTM! Well-structured highlight entry construction.

The method correctly handles position-to-coordinate conversion with proper clamping at Line 466 to prevent out-of-bounds coordinates. The snippet extraction at Line 468 relies on Python's graceful slice handling, which is acceptable since the snippet is for display purposes only.


480-487: LGTM! Clean error type mapping.

Simple, focused method with proper default handling for unknown error types.


489-494: LGTM! Parse error queuing for deferred display.

The method correctly parses and queues parse errors. Note that Line 494 replaces any existing pending highlights rather than appending; this is likely intentional for parse errors which typically occur individually during launch.


496-508: LGTM! Robust parse error location extraction.

The regex pattern correctly matches typical compiler error format <input>:line:column: detail with appropriate fallback values when the pattern doesn't match.


546-569: LGTM! Comprehensive message flattening logic.

The recursive flattening correctly handles nested message structures (strings, dicts, lists). For typical DAP message hierarchies, the recursion depth should be shallow and safe. If deeply nested messages become a concern, consider adding a depth limit parameter.


571-638: LGTM! Clean message sending refactor.

The introduction of send_message_simple and the addition of the category parameter to send_message_hierarchy improves code organization and allows proper categorization of messages (console/stdout/stderr).


640-656: LGTM! Clear execution state reporting.

The method correctly reports execution state with appropriate priority (assertion failure > breakpoint > finished > running) and uses the new simple message pathway for clean output.

Copy link

@coderabbitai coderabbitai bot left a 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/backend/dd/DDSimDebug.cpp (1)

591-627: Consolidate redundant state clearing into ddsimLoadCode.

Lines 597–608 clear the same state containers that preprocessAssertionCode clears again at lines 1654–1665. Since preprocessAssertionCode is only called from this function (line 611) after the initial clearing, the duplicate clearing in preprocessAssertionCode is redundant and obscures where state initialization actually occurs.

Consider removing the clearing loops from preprocessAssertionCode (lines 1654–1665) and documenting that callers must ensure a clean state beforehand, or make preprocessAssertionCode more self-contained if it may be called independently in the future.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81801fa and e151e5d.

📒 Files selected for processing (1)
  • src/backend/dd/DDSimDebug.cpp
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.
🧬 Code graph analysis (1)
src/backend/dd/DDSimDebug.cpp (2)
test/utils/common_fixtures.hpp (6)
  • code (211-215)
  • code (211-213)
  • code (236-244)
  • code (236-237)
  • code (254-256)
  • code (254-254)
include/frontend/cli/CliFrontEnd.hpp (2)
  • code (46-46)
  • code (68-68)
⏰ 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). (15)
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Release) / 🍎 macos-14 clang Release
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Debug) / 🍎 macos-14 clang Debug
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
🔇 Additional comments (1)
src/backend/dd/DDSimDebug.cpp (1)

1134-1151: LGTM: Readiness guard prevents stale state execution.

The check on line 1136–1138 properly gates execution to ensure code is loaded and state is initialized. Combined with the ready flag management (set false when loading code, true after successful processing), this ensures each execution starts with a clean slate and prevents undefined behavior from uninitialized state.

Copy link

@coderabbitai coderabbitai bot left a 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

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e151e5d and d5cd2e7.

📒 Files selected for processing (1)
  • src/backend/dd/DDSimDebug.cpp
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.
⏰ 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). (7)
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
🔇 Additional comments (1)
src/backend/dd/DDSimDebug.cpp (1)

593-608: LGTM: Proper state reset on code load.

The explicit clearing of all instruction-related metadata and setting ready = false ensures each execution starts with a clean state. This aligns with the PR's objective to reset greyed-out highlights between executions.

Copy link

@coderabbitai coderabbitai bot left a 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

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5cd2e7 and ff0e4b3.

📒 Files selected for processing (2)
  • bindings/InterfaceBindings.cpp
  • src/common/parsing/CodePreprocessing.cpp
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-15T01:54:22.129Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/dd/register_matrix_dds.cpp:64-109
Timestamp: 2025-12-15T01:54:22.129Z
Learning: In the munich-quantum-toolkit/core repository, after migrating to nanobind, docstrings for Python bindings are now added directly in the C++ binding code (using R"pb(...)pb" syntax) and stub files (.pyi) are auto-generated using the `bindings/generate-stubs.sh` script. This replaces the previous pybind11 approach where docstrings were manually maintained in stub files.

Applied to files:

  • bindings/InterfaceBindings.cpp
🧬 Code graph analysis (1)
bindings/InterfaceBindings.cpp (1)
test/utils/common_fixtures.hpp (6)
  • code (211-215)
  • code (211-213)
  • code (236-244)
  • code (236-237)
  • code (254-256)
  • code (254-254)
🪛 Cppcheck (2.19.0)
src/common/parsing/CodePreprocessing.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)

bindings/InterfaceBindings.cpp

[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)

⏰ 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). (14)
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Debug) / 🍎 macos-14 clang Debug
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Release) / 🍎 macos-14 clang Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
🔇 Additional comments (14)
bindings/InterfaceBindings.cpp (2)

26-26: LGTM! Includes are necessary for error capture.

The <iostream> header provides std::cerr and <pybind11/iostream.h> provides py::scoped_ostream_redirect, both of which are used in the updated load_code binding.

Also applies to: 29-29


177-193: Excellent improvement to error reporting!

The new implementation captures stderr output during loadCode execution via Python's StringIO, providing users with actual parse error messages instead of generic error text. The use of py::scoped_ostream_redirect ensures proper cleanup through RAII, and the fallback message prevents empty errors.

src/common/parsing/CodePreprocessing.cpp (12)

23-23: LGTM! Necessary includes for new functionality.

The includes support std::isdigit (line 43) and std::exception (lines 146, 501).

Also applies to: 25-25


38-44: LGTM! Robust digit validation.

The empty-string check and unsigned char cast properly handle edge cases for std::isdigit.


46-49: LGTM! Clear structure for line/column positions.

The 1-based initialization aligns with standard editor conventions.


67-91: LGTM! Well-designed target position fallback.

The function gracefully falls back to the first non-space character when the target is not found, providing helpful error positioning.


93-99: LGTM! Standard error message formatting.

The <input>:line:column: format follows common compiler conventions for error reporting.


101-108: LGTM! Clear error message construction.


110-115: LGTM! Clear error message construction.


117-162: LGTM! Comprehensive target validation with robust error handling.

The validation properly handles:

  • Format checking (register name must be non-empty, proper bracket nesting)
  • Numeric index validation with overflow protection
  • Shadowed register handling
  • Bounds checking

The try-catch around std::stoul (lines 144-150) correctly guards against numeric overflow even after isDigits validation.


463-467: LGTM! Improved error positioning accuracy.

Skipping leading whitespace ensures error highlights point to the actual instruction start rather than preceding spaces, improving the user experience.


493-504: LGTM! Robust register validation with detailed error reporting.

The strengthened validation correctly handles:

  • Missing sizes
  • Non-numeric sizes
  • Negative sizes (via isDigits rejecting '-')
  • Overflow (via exception handling)

The formatted error messages with line/column info significantly improve the debugging experience.


569-570: LGTM! Consolidated validation for assertion targets.

Delegating to validateTargets eliminates code duplication and provides consistent error formatting across all validation paths.


575-578: LGTM! Consolidated validation for regular instruction targets.

The conditional check properly excludes variable declarations from target validation, as they have distinct syntax rules validated separately (lines 493-504).

Copy link

@coderabbitai coderabbitai bot left a 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 (2)
python/mqt/debugger/dap/messages/highlight_error_dap_message.py (1)

58-84: Inconsistent message validation between normalization and validation.

At line 79, _normalize_highlight sets message to "" as a default for missing or whitespace-only messages. However, validate() (lines 48-50) later requires each message to be non-empty. This defers the error from normalization to validation.

Consider failing immediately during normalization if a non-empty message is required, so callers receive earlier feedback.

🔎 Proposed fix

Validate the message during normalization:

     normalized["instruction"] = int(normalized.get("instruction", -1))
     normalized["reason"] = str(normalized.get("reason", "unknown"))
     normalized["code"] = str(normalized.get("code", ""))
     normalized["message"] = str(normalized.get("message", "")).strip()
+    if not normalized["message"]:
+        msg = "Each highlight entry must contain a descriptive 'message'."
+        raise ValueError(msg)
     normalized["range"] = {
         "start": start,
         "end": end,
     }

Then simplify validate() to only check that highlights is non-empty, since per-highlight validation is already done:

 def validate(self) -> None:
     """Validate the 'highlightError' DAP event message after creation."""
     if not self.highlights:
         msg = "At least one highlight entry is required to show the issue location."
         raise ValueError(msg)
-
-    for highlight in self.highlights:
-        if "message" not in highlight or not str(highlight["message"]).strip():
-            msg = "Each highlight entry must contain a descriptive 'message'."
-            raise ValueError(msg)
src/common/parsing/CodePreprocessing.cpp (1)

51-65: Bounds validation still missing.

The previous review comment regarding the lack of bounds validation for offset remains applicable. If offset exceeds code.size(), the column calculation on line 63 will produce incorrect results.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff0e4b3 and 8d6d7f0.

📒 Files selected for processing (2)
  • python/mqt/debugger/dap/messages/highlight_error_dap_message.py
  • src/common/parsing/CodePreprocessing.cpp
🧰 Additional context used
🧬 Code graph analysis (1)
src/common/parsing/CodePreprocessing.cpp (3)
include/common/parsing/ParsingError.hpp (1)
  • ParsingError (32-32)
src/common/parsing/ParsingError.cpp (1)
  • ParsingError (23-23)
include/common/parsing/CodePreprocessing.hpp (1)
  • isVariableDeclaration (296-296)
🪛 Cppcheck (2.19.0)
src/common/parsing/CodePreprocessing.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)

⏰ 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). (15)
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Debug) / 🍎 macos-14 clang Debug
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Release) / 🍎 macos-14 clang Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🇨‌ Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
🔇 Additional comments (7)
src/common/parsing/CodePreprocessing.cpp (7)

38-44: LGTM!

The isDigits implementation correctly handles the empty string case and properly casts to unsigned char before calling std::isdigit to avoid undefined behavior with negative char values.


67-91: LGTM!

The function correctly calculates line/column positions for error reporting, with appropriate fallback to the first non-whitespace character when the target is not found.


93-115: LGTM!

Clean utility functions for consistent error message formatting.


117-162: LGTM!

The validateTargets function is well-implemented with:

  • Proper bracket syntax validation
  • Guarded numeric parsing with isDigits check followed by try/catch for overflow protection
  • Correct handling of shadowed registers (function parameters)
  • Proper bounds checking against defined register sizes

463-467: LGTM!

The improved trueStart calculation correctly skips leading whitespace for more accurate error position reporting while properly maintaining bounds within the current instruction.


493-504: LGTM!

The strengthened register declaration validation provides:

  • Proper rejection of declarations missing size specifications
  • Guarded numeric parsing with both isDigits and try/catch for overflow edge cases
  • Descriptive error messages with accurate position information

569-578: LGTM!

Proper integration of validateTargets for both assertions and regular instructions, with the correct guard to skip variable declarations which don't have qubit targets to validate.

Comment on lines +30 to +39
def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any] | None) -> None:
"""Create a new 'highlightError' DAP event message.
Args:
highlights (Sequence[Mapping[str, Any]]): Highlight entries describing the problematic ranges.
source (Mapping[str, Any] | None): Information about the current source file.
"""
self.highlights = [self._normalize_highlight(entry) for entry in highlights]
self.source = self._normalize_source(source)
super().__init__()
Copy link

@coderabbitai coderabbitai bot Dec 30, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Type signature allows None but implementation rejects it.

The constructor signature permits source: Mapping[str, Any] | None (line 30), but _normalize_source (line 106) raises TypeError if source is not a Mapping. Passing None will cause a runtime exception.

🔎 Proposed fix

Either handle None in _normalize_source:

 @staticmethod
 def _normalize_source(source: Mapping[str, Any] | None) -> dict[str, Any]:
     """Create a defensive copy of the provided DAP Source information."""
+    if source is None:
+        msg = "Source information is required."
+        raise ValueError(msg)
     if not isinstance(source, Mapping):
         msg = "Source information must be provided as a mapping."
         raise TypeError(msg)

Or remove | None from the constructor signature if source is always required:

-def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any] | None) -> None:
+def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any]) -> 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.

Suggested change
def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any] | None) -> None:
"""Create a new 'highlightError' DAP event message.
Args:
highlights (Sequence[Mapping[str, Any]]): Highlight entries describing the problematic ranges.
source (Mapping[str, Any] | None): Information about the current source file.
"""
self.highlights = [self._normalize_highlight(entry) for entry in highlights]
self.source = self._normalize_source(source)
super().__init__()
def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any]) -> None:
"""Create a new 'highlightError' DAP event message.
Args:
highlights (Sequence[Mapping[str, Any]]): Highlight entries describing the problematic ranges.
source (Mapping[str, Any] | None): Information about the current source file.
"""
self.highlights = [self._normalize_highlight(entry) for entry in highlights]
self.source = self._normalize_source(source)
super().__init__()
🤖 Prompt for AI Agents
python/mqt/debugger/dap/messages/highlight_error_dap_message.py lines 30-39: the
constructor type allows source: Mapping[str, Any] | None but calling
self._normalize_source(source) will raise when source is None; update the code
so None is handled consistently—either remove the optional from the constructor
signature if source must always be provided, or (preferred) keep the optional
and modify _normalize_source to accept None (returning None or a normalized
empty/default mapping) and adjust the constructor's annotated type to
typing.Optional[Mapping[str, Any]] so the runtime and type annotations match.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree with @coderabbitai here, if the source field is allowed to be None, then the _normalize_source method and its signature should be updated.

If it really should not be None, then the __init__ signature should be updated.

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for HighlightError instantiation to see how source parameter is used
rg -n "HighlightError\(" --type=py -A 3 -B 1

Length of output: 1443


🏁 Script executed:

#!/bin/bash
# Search for source_file in DAPServer to see its initialization and usage
rg -n "source_file" --type=py python/mqt/debugger/dap/dap_server.py -C 2

Length of output: 1276


🏁 Script executed:

#!/bin/bash
# Search for source_file assignments to see if it's always initialized
rg -n "self\.source_file\s*=" --type=py python/mqt/debugger/dap/ -C 2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for source_file assignments with broader pattern
rg -n "source_file =" --type=py python/mqt/debugger/dap/ -C 2

Length of output: 1136


@DRovara, thank you for the confirmation! Based on my analysis of the codebase, I recommend removing | None from the constructor signature.

Here's why:

  1. Actual usage never passes None: Both instantiations of HighlightError in dap_server.py (lines 258 and 359) pass self.source_file, which is always initialized as a dict with "name" and "path" keys before these calls.

  2. Field declaration expects a dict: In DAPServer, source_file is declared as source_file: dict[str, Any] (not optional), and it's set in both launch_dap_message.py and restart_dap_message.py before any HighlightError instances are created.

  3. _normalize_source validates required fields: The method checks for "name" and "path" keys, which indicates source information is essential for the event.

Recommended fix:

def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any]) -> None:
    """Create a new 'highlightError' DAP event message.

    Args:
        highlights (Sequence[Mapping[str, Any]]): Highlight entries describing the problematic ranges.
        source (Mapping[str, Any]): Information about the current source file.
    """
    self.highlights = [self._normalize_highlight(entry) for entry in highlights]
    self.source = self._normalize_source(source)
    super().__init__()

This makes the signature consistent with the implementation and actual usage patterns.

@svenjeschmitt-ops svenjeschmitt-ops changed the title 🚀 highlight issue and assertion issue solved Clear assertion greyed-out lines on restart and highlight issue locations Dec 31, 2025
@svenjeschmitt-ops svenjeschmitt-ops changed the title Clear assertion greyed-out lines on restart and highlight issue locations 🚀 Clear assertion greyed-out lines on restart and highlight issue locations Dec 31, 2025
Copy link

@coderabbitai coderabbitai bot left a 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 (3)
python/mqt/debugger/dap/messages/highlight_error_dap_message.py (2)

36-45: Critical: Type signature mismatch - None not handled.

The constructor allows source: Mapping[str, Any] | None, but _normalize_source (line 153) raises TypeError when source is not a Mapping. Passing None causes a runtime exception.

🔎 Recommended fix

Either handle None explicitly in _normalize_source:

 @staticmethod
 def _normalize_source(source: Mapping[str, Any] | None) -> dict[str, Any]:
     """Create a defensive copy of the provided DAP Source information."""
+    if source is None:
+        msg = "Source information is required."
+        raise ValueError(msg)
     if not isinstance(source, Mapping):
         msg = "Source information must be provided as a mapping."
         raise TypeError(msg)

Or remove | None from the constructor if source is always required:

-def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any] | None) -> None:
+def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any]) -> None:

47-61: Validation partially duplicates normalization checks.

validate() checks for non-empty highlights and messages, while _normalize_highlight (lines 86-98) validates range structure. There's overlap in that both methods touch the highlights list and message field, though validate() enforces non-empty messages post-normalization while _normalize_highlight only normalizes. Consider consolidating to reduce duplication.

python/mqt/debugger/dap/dap_server.py (1)

513-547: Minor: Redundant condition on Line 542.

Line 542's ternary end_column if end_column > 0 else column is redundant. Since column >= 1 (line 521) and end_column = max(column, len(line_text) + 1) (line 536), end_column is always positive.

Otherwise, the empty-line heuristic (lines 525-534) is a thoughtful UX improvement for locating errors on blank lines.

🔎 Optional simplification
         return {
             "instruction": -1,
             "range": {
                 "start": {"line": line, "column": column},
-                "end": {"line": line, "column": end_column if end_column > 0 else column},
+                "end": {"line": line, "column": end_column},
             },
             "reason": "parseError",
             "code": snippet,
             "message": detail,
         }
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d6d7f0 and 3653c60.

📒 Files selected for processing (2)
  • python/mqt/debugger/dap/dap_server.py
  • python/mqt/debugger/dap/messages/highlight_error_dap_message.py
🧰 Additional context used
🧬 Code graph analysis (2)
python/mqt/debugger/dap/messages/highlight_error_dap_message.py (1)
python/mqt/debugger/dap/messages/dap_event.py (1)
  • DAPEvent (17-41)
python/mqt/debugger/dap/dap_server.py (2)
python/mqt/debugger/dap/messages/highlight_error_dap_message.py (2)
  • HighlightError (22-179)
  • encode (62-70)
python/mqt/debugger/dap/messages/output_dap_event.py (2)
  • encode (54-69)
  • OutputDAPEvent (18-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). (15)
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🇨‌ Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Debug) / 🍎 macos-14 clang Debug
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Release) / 🍎 macos-14 clang Release
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
🔇 Additional comments (18)
python/mqt/debugger/dap/messages/highlight_error_dap_message.py (4)

62-70: LGTM: Encoding follows DAP event pattern.

The encode() method correctly extends the base event structure and wraps the normalized data under body, consistent with other DAP event implementations.


72-109: LGTM: Robust normalization with validation.

_normalize_highlight provides thorough validation of range structure and ordering, with sensible defaults for optional fields. The defensive checks and error messages are clear.


111-137: LGTM: Position normalization is defensive.

The type check and KeyError handling ensure required fields are present and convertible to integers, with clear error messages for debugging.


139-162: Source normalization is correct but see constructor issue.

The validation and normalization logic is sound for non-None sources. However, this method's rejection of None creates the type mismatch issue flagged in the constructor review (lines 36-45).

python/mqt/debugger/dap/dap_server.py (14)

14-14: LGTM: Import supports parse error handling.

The re module import is used for parsing error message locations in _parse_error_location (line 502).


118-119: LGTM: State variables properly initialized.

pending_highlights and _prevent_exit are correctly typed and initialized to support the highlight queuing and exit prevention logic.


172-186: LGTM: Exit prevention reset on execution commands.

Resetting _prevent_exit for execution-resuming commands (Next, Step*, Continue, Restart, Launch) correctly allows exit events after clean execution while preventing them after parse/assertion errors.


257-261: LGTM: Highlight emission and exit prevention.

The logic correctly emits queued highlights, clears the queue, and sets _prevent_exit to prevent premature exit events after parse errors.


271-275: LGTM: Exit condition respects prevention flag.

The condition correctly prevents ExitedDAPEvent when _prevent_exit is True, allowing the session to remain active after parse or assertion errors for user review.


357-361: LGTM: Assertion failure highlights emitted.

Collecting and emitting highlight entries for assertion failures with _prevent_exit correctly prevents premature session termination while displaying error context.


363-389: LGTM: Position calculation handles edge cases.

The logic correctly handles empty source, positions exceeding the source length, and normal cases. The for-else clause appropriately handles overflow by using the last line's bounds.


431-457: LGTM: Highlight collection with defensive fallback.

The method defensively handles diagnostic errors and provides a sensible fallback to highlight the failing instruction when no specific error causes are identified.


459-481: LGTM: Highlight entry construction is defensive.

The method safely handles runtime errors from position lookups and appropriately calculates end positions to exclude trailing newlines for cleaner highlighting.


483-490: LGTM: Error cause mapping is clear.

The mapping from ErrorCauseType to string identifiers is straightforward, with a sensible default for unknown types.


492-497: LGTM: Parse error queuing is appropriate.

Extracting location from the error message and replacing pending_highlights is correct for parse errors, which occur once per launch/restart.


499-511: LGTM: Error location parsing with fallback.

The regex pattern correctly parses compiler error format, with a sensible fallback to line 1, column 1 when the format doesn't match.


549-641: LGTM: Message sending refactored for modularity.

The refactoring into _flatten_message_parts and send_message_simple improves code organization. The recursive flattening correctly handles nested message structures (strings, dicts, lists), and the delegation from send_message_hierarchy to send_message_simple reduces duplication.


643-659: LGTM: State reporting is comprehensive.

The method checks all relevant execution states (assertion failed, breakpoint, finished, running) and sends clear status messages to the client using the appropriate messaging infrastructure.

Comment on lines +164 to +179
@staticmethod
def _later_than(start: Mapping[str, Any], end: Mapping[str, Any]) -> bool:
"""Return True if 'end' describes a position before 'start'.
Args:
start (Mapping[str, Any]): The start position mapping.
end (Mapping[str, Any]): The end position mapping.
Returns:
bool: True when the end position is before the start position.
"""
start_line = int(start.get("line", 0))
start_column = int(start.get("column", 0))
end_line = int(end.get("line", 0))
end_column = int(end.get("column", 0))
return (end_line, end_column) < (start_line, start_column)
Copy link

@coderabbitai coderabbitai bot Dec 31, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider renaming for clarity.

The method returns True when end is before start, but the name _later_than might suggest the opposite. Consider renaming to _is_before or _end_before_start for clarity.

🤖 Prompt for AI Agents
In python/mqt/debugger/dap/messages/highlight_error_dap_message.py around lines
164 to 179, the private method name _later_than is misleading because it returns
True when the end position is before the start; rename the method to a clearer
name such as _end_before_start (or _is_before) and update its docstring to match
the new name/semantics, then refactor all internal references/usages in this
module (and any tests) to call the new name so behavior stays the same.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Agree with @coderabbitai, maybe something like _start_comes_after_end or so?

Copy link

Choose a reason for hiding this comment

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

@DRovara, excellent suggestion! _start_comes_after_end reads very clearly. Here's the refactoring with your suggested name:

Updated method definition:

@staticmethod
def _start_comes_after_end(start: Mapping[str, Any], end: Mapping[str, Any]) -> bool:
    """Return True if 'start' describes a position after 'end'.

    Args:
        start (Mapping[str, Any]): The start position mapping.
        end (Mapping[str, Any]): The end position mapping.

    Returns:
        bool: True when the start position is after the end position.
    """
    start_line = int(start.get("line", 0))
    start_column = int(start.get("column", 0))
    end_line = int(end.get("line", 0))
    end_column = int(end.get("column", 0))
    return (end_line, end_column) < (start_line, start_column)

Updated usage in _normalize_highlight:

start = HighlightError._normalize_position(highlight_range.get("start"))
end = HighlightError._normalize_position(highlight_range.get("end"))
if HighlightError._start_comes_after_end(start, end):
    msg = "Highlight range 'end' must be after 'start'."
    raise ValueError(msg)

All three suggestions (_end_before_start, _is_before, _start_comes_after_end) are significant improvements over _later_than. Your choice is nicely descriptive!

Copy link
Collaborator

@DRovara DRovara left a comment

Choose a reason for hiding this comment

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

Thanks a lot for the work and sorry for the late review. This ended up becoming quite a big PR, so I hope I have covered everything. Either way, at some point I would probably also like to manually try everything out myself, but I guess that can wait until after we merge this.

I have a couple of requested changes. Some of them should be quite simple, but there are also some bigger ones that might need larger restructuring. I hope that works out fine for you.

Comment on lines -177 to +192
checkOrThrow(self->loadCode(self, code));
const py::module io = py::module::import("io");
const py::object stringIo = io.attr("StringIO")();
Result result = OK;
{
const py::scoped_ostream_redirect redirect(std::cerr, stringIo);
result = self->loadCode(self, code);
}
if (result != OK) {
auto message = stringIo.attr("getvalue")().cast<std::string>();
if (message.empty()) {
message = "An error occurred while executing the operation";
}
throw std::runtime_error(message);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems like a really hacky solution.

I'd rather prefer if there was some other way of getting the error message that does not involve redirecting STDERR. Here are some potential solutions I can imagine:

  • rather than returning a Result, loadCode could be updated to return some other enum value that has a value for each type of error that could come up when parsing and pre-defined strings are defined for each error type.
  • loadCode receives an additional char* errorMessage parameter that expects a fixed-size (e.g. 255) char buffer to which the error message can be written.
  • We define a new struct called LoadResult that contains all important information that could come up with a parsing error (uint errorCode, char errorMessage[255], uint line...) and use that as a return type for loadCode

I personally feel like the third suggestion might be the best one.

Comment on lines +30 to +39
def __init__(self, highlights: Sequence[Mapping[str, Any]], source: Mapping[str, Any] | None) -> None:
"""Create a new 'highlightError' DAP event message.
Args:
highlights (Sequence[Mapping[str, Any]]): Highlight entries describing the problematic ranges.
source (Mapping[str, Any] | None): Information about the current source file.
"""
self.highlights = [self._normalize_highlight(entry) for entry in highlights]
self.source = self._normalize_source(source)
super().__init__()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree with @coderabbitai here, if the source field is allowed to be None, then the _normalize_source method and its signature should be updated.

If it really should not be None, then the __init__ signature should be updated.

def collect_highlight_entries(self, failing_instruction: int) -> list[dict[str, Any]]:
"""Collect highlight entries for the current assertion failure."""
highlights: list[dict[str, Any]] = []
if getattr(self, "source_code", ""):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need this check? Is there no cleaner way to do it?

Comment on lines +436 to +437
diagnostics = self.simulation_state.get_diagnostics()
error_causes = diagnostics.potential_error_causes()
Copy link
Collaborator

Choose a reason for hiding this comment

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

From my understanding, this method is always called when handle_assertion_fail happens. However, in that method, there is already a call to potential_error_causes(). Since that call could, in some cases, be rather small, I'm not sure if we really want to do it twice.

Is there any way to instead pass the error causes on as parameters to this method?

def _format_highlight_reason(cause_type: mqt.debugger.ErrorCauseType | None) -> str:
"""Return a short identifier for the highlight reason."""
if cause_type == mqt.debugger.ErrorCauseType.MissingInteraction:
return "missingInteraction"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we maybe use a "cleaner" datatype - i.e., an enum - for these indicators?

Comment on lines +1135 to +1138
auto* ddsim = toDDSimulationState(self);
if (!ddsim->ready) {
return ERROR;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just out of interest - is there any particular reason this check is needed here? According to coderabbit, we don't have similar checks in other run methods.

Comment on lines +123 to +125
if (target.empty()) {
continue;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we allow a target to be an empty string here?

Comment on lines +643 to +659
def send_state(self, connection: socket.socket) -> None:
"""Send the state of the current execution to the client.
Args:
connection (socket.socket): The client socket.
"""
output_lines = []
if self.simulation_state.did_assertion_fail():
output_lines.append("Assertion failed")
if self.simulation_state.was_breakpoint_hit():
output_lines.append("Breakpoint hit")
if self.simulation_state.is_finished():
output_lines.append("Finished")
if not output_lines:
output_lines.append("Running")
for line_text in output_lines:
self.send_message_simple(line_text, None, None, 0, 0, connection)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the idea of this method? Where is it used (I can't seem to find any uses of it in the code)

Comment on lines +164 to +179
@staticmethod
def _later_than(start: Mapping[str, Any], end: Mapping[str, Any]) -> bool:
"""Return True if 'end' describes a position before 'start'.
Args:
start (Mapping[str, Any]): The start position mapping.
end (Mapping[str, Any]): The end position mapping.
Returns:
bool: True when the end position is before the start position.
"""
start_line = int(start.get("line", 0))
start_column = int(start.get("column", 0))
end_line = int(end.get("line", 0))
end_column = int(end.get("column", 0))
return (end_line, end_column) < (start_line, start_column)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Agree with @coderabbitai, maybe something like _start_comes_after_end or so?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The "best practice" we have built so far in this project is for every C++ function to have a docstring either with the header declaration in the hpp file, or, if no such header declaration exists, when the actual function is defined in the cpp file, even for private namespace functions.

Therefore, it would be nice if you could also add docstrings to the functions you defined here in the same way.

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.

2 participants