[llvm-branch-commits] [llvm] [Dexter] Add for_hit_count for state nodes (PR #203359)

Stephen Tozer via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Jun 15 04:39:29 PDT 2026


https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/203359

>From 6755dd4ec887cb4b0f5f9528ea3fc2a95d58c206 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Thu, 11 Jun 2026 18:44:38 +0100
Subject: [PATCH 1/2] [Dexter] Add for_hit_count for state nodes

This patch adds the ability for state nodes to use a `for_hit_count: <int>`
field to limit the number of times that a given state node will be active.
---
 .../ScriptDebuggerController.py               | 46 +++++++---
 .../dexter/dex/dextIR/StepIR.py               |  3 +-
 .../dexter/dex/evaluation/ExpectWriter.py     | 14 ++-
 .../dexter/dex/evaluation/RunMatch.py         | 13 ++-
 .../dexter/dex/evaluation/StateMatch.py       | 86 ++++++++++++++++---
 .../dexter/dex/test_script/Nodes.py           |  2 +-
 .../scripts/debugging/where_fn_hit_count.cpp  | 26 ++++++
 .../scripts/debugging/where_for_hit_count.cpp | 78 +++++++++++++++++
 .../debugging/where_hit_count_early_exit.cpp  | 33 +++++++
 9 files changed, 269 insertions(+), 32 deletions(-)
 create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_fn_hit_count.cpp
 create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_for_hit_count.cpp
 create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_hit_count_early_exit.cpp

diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ScriptDebuggerController.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ScriptDebuggerController.py
index f1404c37116fa..fc052b5274089 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ScriptDebuggerController.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ScriptDebuggerController.py
@@ -19,7 +19,7 @@
 )
 from dex.debugger.DebuggerBase import DebuggerBase
 from dex.debugger.DAP import DAP
-from dex.evaluation.StateMatch import get_active_where_matches
+from dex.evaluation.StateMatch import StateMatchContext, get_active_where_matches
 from dex.test_script.Nodes import Where
 from dex.test_script.Script import DexterScript, Scope
 from dex.tools import Context
@@ -87,6 +87,7 @@ def _run_debugger_custom(self, cmdline):
         self.step_collection.clear_steps()
 
         script: DexterScript = self.script
+        state_match_context = StateMatchContext()
         self._init_bps()
 
         self.debugger.launch(cmdline)
@@ -119,8 +120,22 @@ def _run_debugger_custom(self, cmdline):
 
             ## Fetch frame information and breakpoint information from the debugger.
             step_info: StepIR = self.debugger.get_stack_frames(self._step_index)
+            # For some niche cases around function breakpoints, we may fail to
+            # correctly notice a !where being hit; therefore we explicitly track
+            # !where breakpoints that get hit in the StepIR.
+            hit_bps = self.debugger.get_triggered_breakpoint_ids()
+            step_info.hit_where_bps = sorted(
+                (
+                    where
+                    for where, bp_ids in self._where_bps.items()
+                    if any(bp_id in hit_bps for bp_id in bp_ids)
+                ),
+                key=lambda where: str(where),
+            )
 
-            active_where_matches = get_active_where_matches(script, step_info)
+            active_where_matches = get_active_where_matches(
+                script, step_info, state_match_context
+            )
 
             watches = [
                 watch
@@ -160,6 +175,11 @@ def _run_debugger_custom(self, cmdline):
                 next_action = DebuggerAction.STEP_OVER
             elif active_where_matches:
                 next_action = DebuggerAction.STEP_OUT
+            elif all(
+                where in state_match_context.expired_wheres
+                for where in script.root_wheres
+            ):
+                next_action = DebuggerAction.EXIT
             else:
                 next_action = DebuggerAction.CONTINUE
 
@@ -170,19 +190,23 @@ def _run_debugger_custom(self, cmdline):
                 for where_match in active_where_matches.values()
                 for where in where_match.pending_wheres
             )
+
+            def where_should_have_breakpoint(where: Where):
+                if where in state_match_context.expired_wheres:
+                    return False
+                if where in script.root_wheres:
+                    return True
+                if should_step_out:
+                    return False
+                return where in pending_wheres
             for where, bp_ids in self._where_bps.items():
-                if (
-                    bp_ids
-                    and where not in script.root_wheres
-                    and (where not in pending_wheres or should_step_out)
-                ):
+                if bp_ids and not where_should_have_breakpoint(where):
                     bp_to_delete.extend(bp_ids)
                     bp_ids.clear()
             self.debugger.delete_breakpoints(bp_to_delete)
-            if not should_step_out:
-                for where in pending_wheres:
-                    if not self._where_bps[where]:
-                        self.add_where_entry_bp(where)
+            for where in pending_wheres:
+                if not self._where_bps[where] and where_should_have_breakpoint(where):
+                    self.add_where_entry_bp(where)
 
             if step_info.current_frame:
                 self._step_index += 1
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py
index 1b23015d02bcb..d2860a0ce8b95 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py
@@ -14,6 +14,7 @@
 from dex.dextIR.FrameIR import FrameIR
 from dex.dextIR.LocIR import LocIR
 from dex.dextIR.ProgramState import ProgramState
+from dex.test_script.Nodes import Where
 
 
 class StopReason(Enum):
@@ -66,7 +67,7 @@ def __init__(
             watches = {}
         self.watches = watches
         self.scope_watches = scope_watches or OrderedDict()
-        self.hit_fn_bps: List[str] = []
+        self.hit_where_bps: List[Where] = []
 
     def __str__(self):
         try:
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py
index d288f4caed70f..a39549d271209 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py
@@ -10,7 +10,7 @@
 from typing import Any, Dict, List, Optional, Set, Tuple, Union
 
 from dex.dextIR import DextIR, StepIR, ValueIR
-from dex.evaluation.StateMatch import get_active_where_matches
+from dex.evaluation.StateMatch import StateMatchContext, get_active_where_matches
 from dex.test_script.Nodes import DexRange, Expect, Line, Then, Value, ValueAll, Where
 from dex.test_script.Script import DexterScript, Scope
 from dex.tools.Main import Context
@@ -158,10 +158,12 @@ class StepExpectWriter:
     """Processes all active, unknown expects at a given debugger step and produces ExpectedValueWriter results for
     each."""
 
-    def __init__(self, step: StepIR, script: DexterScript):
+    def __init__(
+        self, step: StepIR, script: DexterScript, state_match_context: StateMatchContext
+    ):
         self.step = step
         self.script = script
-        self.state_match = get_active_where_matches(script, step)
+        self.state_match = get_active_where_matches(script, step, state_match_context)
         active_expects = {
             expect
             for where_match in self.state_match.values()
@@ -227,7 +229,11 @@ def collect_expects_to_write(expect: Expect, expected_value: Any, scope: Scope):
         if not self.unknown_expect_rewrites and not self.scope_expect_rewrites:
             return
 
-        self.step_writers = [StepExpectWriter(step, script) for step in dext_ir.steps]
+        state_match_context = StateMatchContext()
+        self.step_writers = [
+            StepExpectWriter(step, script, state_match_context)
+            for step in dext_ir.steps
+        ]
         for step_writer in self.step_writers:
             step_idx = step_writer.step.step_index
             for (
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/RunMatch.py b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/RunMatch.py
index 73b12a918bd5f..e09cce69e6c28 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/RunMatch.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/RunMatch.py
@@ -25,7 +25,7 @@
     get_variable_metrics,
     serialize_metric_to_json,
 )
-from dex.evaluation.StateMatch import get_active_where_matches
+from dex.evaluation.StateMatch import StateMatchContext, get_active_where_matches
 from dex.test_script import DexterScript, Scope
 from dex.test_script.Nodes import Expect, Value
 
@@ -35,12 +35,16 @@ class DebuggerStepMatch:
     expected output."""
 
     def __init__(
-        self, step: StepIR, script: DexterScript, match_context: ExpectMatchContext
+        self,
+        step: StepIR,
+        script: DexterScript,
+        match_context: ExpectMatchContext,
+        state_match_context: StateMatchContext,
     ):
         self.step = step
         self.script = script
         self.match_context = match_context
-        self.state_match = get_active_where_matches(script, step)
+        self.state_match = get_active_where_matches(script, step, state_match_context)
         expects_to_match = {
             expect
             for where_match in self.state_match.values()
@@ -92,9 +96,10 @@ def add_expected_values(expect: Expect, expected_value: Any, scope: Scope):
         script.visit_script(visit_expect=add_expected_values)
 
         # Then produce all of our step matches.
+        state_match_context = StateMatchContext()
         for step in self.dext_ir.steps:
             self.step_matches.append(
-                DebuggerStepMatch(step, script, self.match_context)
+                DebuggerStepMatch(step, script, self.match_context, state_match_context)
             )
 
         # Then, for each expect, produce the list of results for just that variable.
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
index 03a9d4c0d0b89..2161511584ca5 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
@@ -7,15 +7,55 @@
 """Utilities for matching debugger state, such as the call stack, conditions, or historical state (e.g. breakpoint
 hitcounts) to descriptions of expected state in a DexterScript."""
 
+from collections import Counter
 from dataclasses import dataclass, field
 import os
-from typing import Dict, List, Tuple
+from typing import Dict, List, Optional, Set, Tuple
 
 from dex.dextIR import FrameIR, StepIR
 from dex.test_script import DexterScript, Scope
 from dex.test_script.Nodes import Expect, FileLabels, Where, Then
 
 
+class StateMatchContext:
+    """Class that holds any state needed for matching state nodes to debugger state across a run."""
+
+    def __init__(self):
+        self.where_hit_counts: Counter[Where] = Counter()
+        self.expired_wheres: Set[Where] = set()
+        self._last_match_result: Optional[StateMatchResult] = None
+
+    def where_hit_is_new(self, where: Where, step: StepIR) -> bool:
+        """Returns True if the current step can be counted as a new "hit" for `where`, assuming that `where` was hit in
+        this step (but does not check if `where` has actually been hit in the current step).
+        """
+        # If this is the first step, all hits are new.
+        if self._last_match_result is None:
+            return True
+        # If this !where did not appear in any frame in the previous step, this is a fresh hit.
+        if where not in self._last_match_result:
+            return True
+        # If !where uses a function breakpoint and that breakpoint was hit this step, this is a fresh hit.
+        if where.function and not where.lines and where in step.hit_where_bps:
+            return True
+        return False
+
+    def add_hit_if_where_hit_is_new(self, where: Where, step: StepIR) -> bool:
+        """Checks whether the current step can be counted as a new "hit" for `where`. Increments `where`'s hit count if
+        it has a new hit, and returns True iff so."""
+        assert (
+            where.for_hit_count is not None
+        ), "Tried to add hit count for !where without for_hit_count?"
+        if self.where_hit_is_new(where, step):
+            self.where_hit_counts[where] += 1
+            if self.where_hit_counts[where] >= where.for_hit_count:
+                self.expired_wheres.add(where)
+            return True
+        return False
+
+    def update(self, new_match_result: "StateMatchResult"):
+        self._last_match_result = new_match_result
+
 def is_subpath(subpath: str, superpath: str) -> bool:
     """Returns True if subpath is a trailing subpath of superpath, i.e. if `superpath` ends with `subpath` after
     normalizing both paths."""
@@ -24,11 +64,11 @@ def is_subpath(subpath: str, superpath: str) -> bool:
     return normalized_superpath.endswith(normalized_subpath)
 
 
-# A very simple matcher, returns True iff `where` matches `frame`.
-def match_where_to_frame(
+def _match_where_to_frame(
     where: Where,
     frame: FrameIR,
     labels: FileLabels,
+    context: StateMatchContext,
 ) -> bool:
     """A very simple matcher, returns True iff `where` matches `frame`."""
     if where.file is not None and not is_subpath(where.file, frame.loc.path):
@@ -42,16 +82,30 @@ def match_where_to_frame(
     if where.lines is not None:
         if frame.loc.lineno not in where.get_lines(labels):
             return False
-    if (
-        where.for_hit_count is not None
-        or where.after_hit_count is not None
-        or where.conditions is not None
-    ):
+    if where.for_hit_count is not None:
+        where_hit_count = context.where_hit_counts[where]
+        if where_hit_count > where.for_hit_count:
+            return False
+    if where.after_hit_count is not None or where.conditions is not None:
         raise NotImplementedError(
             "!where hit counts and conditions currently unsupported."
         )
     return True
 
+def match_where_to_frame(
+    where: Where,
+    frame: FrameIR,
+    step: StepIR,
+    labels: Dict[str, int],
+    context: StateMatchContext,
+) -> bool:
+    """Returns True if `where` matches `frame`. As part of this check, we perform the check once, and if necessary we
+    may increment `where`'s hit count and check again."""
+    result = _match_where_to_frame(where, frame, labels, context)
+    if result == True and where.for_hit_count is not None:
+        if context.add_hit_if_where_hit_is_new(where, step):
+            result = _match_where_to_frame(where, frame, labels, context)
+    return result
 
 @dataclass
 class WhereMatchResult:
@@ -63,10 +117,13 @@ class WhereMatchResult:
     active_expects: List[Expect] = field(default_factory=list)
     active_thens: List[Then] = field(default_factory=list)
     pending_wheres: List[Where] = field(default_factory=list)
+    expired_wheres: List[Where] = field(default_factory=list)
+
 
+StateMatchResult = Dict[Where, WhereMatchResult]
 
 def get_active_where_matches(
-    script: DexterScript, step_info: StepIR
+    script: DexterScript, step_info: StepIR, match_context: StateMatchContext
 ) -> Dict[Where, WhereMatchResult]:
     """Match the script against the step_info, producing a dict that maps each !where that matches a stack frame to the
     index of the (rootmost) stack frame that it matches, and if the frame that it matches is the current stack frame
@@ -93,14 +150,20 @@ def get_active_wheres(where: Where, scope: Scope):
             labels = script.get_labels(
                 expected_file or step_info.frames[target_frame_idx].loc.path
             )
-            if match_where_to_frame(where, step_info.frames[target_frame_idx], labels):
+            if match_where_to_frame(
+                where,
+                step_info.frames[target_frame_idx],
+                step_info,
+                labels,
+                match_context,
+            ):
                 active_where_expects[where] = WhereMatchResult(target_frame_idx)
             return
         # For this !where, search for the rootmost stack frame that matches it.
         matching_frame_idx = None
         for frame_idx, frame in reversed(list(enumerate(step_info.frames))):
             labels = script.get_labels(expected_file or frame.loc.path)
-            if match_where_to_frame(where, frame, labels):
+            if match_where_to_frame(where, frame, step_info, labels, match_context):
                 matching_frame_idx = frame_idx
                 break
 
@@ -129,4 +192,5 @@ def get_active_thens(then: Then, scope: Scope):
         visit_then=get_active_thens,
     )
 
+    match_context.update(active_where_expects)
     return active_where_expects
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Nodes.py b/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Nodes.py
index d441657f5761d..893d7f3c1ba31 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Nodes.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Nodes.py
@@ -63,7 +63,7 @@ def __init__(self, attributes: dict, is_and: bool):
         self.lines: Union[Line, DexRange, None] = lines
         self.after_hit_count: Optional[int] = attributes.pop("after_hit_count", None)
         self.for_hit_count: Optional[int] = attributes.pop("for_hit_count", None)
-        self.conditions: dict = attributes.pop("conditions", None)
+        self.conditions: Optional[dict] = attributes.pop("conditions", None)
         self.is_and = is_and
         if attributes:
             raise DexterNodeError(
diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_fn_hit_count.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_fn_hit_count.cpp
new file mode 100644
index 0000000000000..0796968b47943
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_fn_hit_count.cpp
@@ -0,0 +1,26 @@
+// RUN: %dexter_regression_test_cxx_build %s -o %t
+// RUN: %dexter_regression_test_run --use-script --skip-evaluate --binary %t \
+// RUN:   -- %s | FileCheck %s
+
+/// Test that we record hit counts for !where{function} nodes, even when there
+/// are no other steps between each breakpoint hit.
+
+/// We should only hit countdown 3 times, even though there is no gap between
+/// the tail calls.
+// CHECK-LABEL: Step 0
+// CHECK-COUNT-3: countdown
+// CHECK-NOT: countdown
+
+/// All on one line for simplicity so that we only get one step per call.
+// clang-format off
+int countdown(int Num) { if (!Num) return 0; __attribute__((musttail)) return countdown(Num - 1); }
+// clang-format on
+
+int main() { return countdown(2) + countdown(2); }
+
+/*
+---
+!where {function: countdown, for_hit_count: 3}:
+  !value Num: [2, 1, 0]
+...
+*/
diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_for_hit_count.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_for_hit_count.cpp
new file mode 100644
index 0000000000000..8a540b356b952
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_for_hit_count.cpp
@@ -0,0 +1,78 @@
+// RUN: %dexter_regression_test_cxx_build %s -o %t
+// RUN: %dexter_regression_test_run --use-script --skip-evaluate --binary %t \
+// RUN:   -- %s | FileCheck %s
+
+/// Test !where nodes work with for_hit_count.
+
+/// All on one line for simplicity so that we only get one step per call.
+int collatz(int N) { return (N % 2) ? N * 3 + 1 : N / 2; }
+
+int main() {
+  int MaxAttempts = 50;
+  int Value = 472959593;
+  for (int I = 0; I < MaxAttempts; ++I) {
+    Value = collatz(Value); // !dex_label loop_start
+    if (Value == 1)
+      break; // !dex_label loop_end
+  }
+  return Value == 1 ? 0 : 1;
+}
+
+// CHECK:      Step 0
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(14:21)
+// CHECK:      Step 1
+// CHECK:        Frame 0:
+// CHECK-NEXT:     collatz(int)
+// CHECK-NEXT:     where_for_hit_count.cpp(8:30)
+// CHECK:      Step 2
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(14:11)
+// CHECK:      Step 3
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(15:15)
+// CHECK:      Step 4
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(17:3)
+// CHECK:      Step 5
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(14:21)
+// CHECK:      Step 6
+// CHECK:        Frame 0:
+// CHECK-NEXT:     collatz(int)
+// CHECK-NEXT:     where_for_hit_count.cpp(8:30)
+// CHECK:      Step 7
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(14:11)
+// CHECK:      Step 8
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(15:15)
+// CHECK:      Step 9
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(17:3)
+// CHECK:      Step 10
+// CHECK:        Frame 0:
+// CHECK-NEXT:     collatz(int)
+// CHECK-NEXT:     where_for_hit_count.cpp(8:30)
+// CHECK:      Step 11
+// CHECK:        Frame 0:
+// CHECK-NEXT:     main
+// CHECK-NEXT:     where_for_hit_count.cpp(14:11)
+
+/*
+---
+!where {function: collatz, for_hit_count: 3}:
+    !value N: 0
+!where {lines: !range [!label loop_start, !label loop_end], for_hit_count: 2}:
+    !value Value: 0
+    !value I: 0
+...
+*/
diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_hit_count_early_exit.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_hit_count_early_exit.cpp
new file mode 100644
index 0000000000000..5d799f4119c47
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/debugging/where_hit_count_early_exit.cpp
@@ -0,0 +1,33 @@
+// RUN: %dexter_regression_test_cxx_build %s -o %t
+// RUN: %dexter_regression_test_run --use-script --skip-evaluate --binary %t \
+// RUN:   --timeout-total 10 -- %s 2>&1 | FileCheck %s
+
+/// Test that when all root !where nodes have expired, we exit without waiting
+/// for the debuggee to finish.
+
+// CHECK-NOT: timeout reached
+
+// CHECK-LABEL: Step 0
+// CHECK-COUNT-3: getRandomNumber
+// CHECK-NOT: getRandomNumber
+
+/// All on one line for simplicity so that we only get one step per call.
+int getRandomNumber(int Max) { return 4 % Max; }
+
+int main() {
+  // Bogo search
+  int List[] = {0, 0, 0, 0, 5, 0, 0, 0, 0, 0};
+  int SearchTarget = 0;
+  while (true) {
+    int NextSearch = getRandomNumber(10);
+    if (List[NextSearch] == SearchTarget)
+      return NextSearch;
+  }
+}
+
+/*
+---
+!where {function: getRandomNumber, for_hit_count: 3}:
+    !value Max: 10
+...
+*/

>From 1e2dcb17e1c7667864a96bebac5c66c840fdf453 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Mon, 15 Jun 2026 09:50:12 +0100
Subject: [PATCH 2/2] darker

---
 .../debuginfo-tests/dexter/dex/evaluation/StateMatch.py         | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
index 2161511584ca5..563ec798e6c83 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
@@ -92,6 +92,7 @@ def _match_where_to_frame(
         )
     return True
 
+
 def match_where_to_frame(
     where: Where,
     frame: FrameIR,
@@ -122,6 +123,7 @@ class WhereMatchResult:
 
 StateMatchResult = Dict[Where, WhereMatchResult]
 
+
 def get_active_where_matches(
     script: DexterScript, step_info: StepIR, match_context: StateMatchContext
 ) -> Dict[Where, WhereMatchResult]:



More information about the llvm-branch-commits mailing list