[llvm] [Dexter] Add ability to rewrite scripts to fill-in unknown values (PR #202799)

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 22 05:59:39 PDT 2026


================
@@ -0,0 +1,201 @@
+# DExTer : Debugging Experience Tester
+# ~~~~~~   ~         ~~         ~   ~~
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+"""Utilities for using debugger output to generate expected values that match that output."""
+
+from collections import Counter, OrderedDict, defaultdict
+from copy import deepcopy
+from enum import Enum, IntEnum
+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.test_script.Nodes import Expect, Then, Value, Where
+from dex.test_script.Script import DexterScript, Scope
+from dex.tools.Main import Context
+
+
+class ExpectedValueWriter:
+    """Given a ValueIR for an Expect, generates a complete expected value that matches that value if one can be
+    provided."""
+
+    def __init__(self, expect: Expect, value: ValueIR):
+        self.expect = expect
+        self.root_value = value
+        self.expected_value = expect.get_variable_result(value)
+
+
+def unique_expected_values(elements: List[ExpectedValueWriter]):
+    """Given a list of ExpectedValueWriters, and returns either a list containing the unique set of non-None expected
+    values, or a single item if there is only one non-duplicated expected value in the list, or None if there are no
+    valid expected values."""
+
+    unique_set = set()
+    result = []
+    for element in elements:
+        expected_value = element.expected_value
+        if expected_value is None:
+            continue
+        if expected_value not in unique_set:
+            unique_set.add(expected_value)
+            result.append(expected_value)
+    if not result:
+        return None
+    if len(result) == 1:
+        return result[0]
+    return result
+
+
+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):
+        self.step = step
+        self.script = script
+        self.state_match = get_active_where_matches(script, step)
+        active_expects = {
+            expect
+            for where_match in self.state_match.values()
+            for expect in where_match.active_expects
+        }
+        self.expect_matches: Dict[Expect, ExpectedValueWriter] = {}
+
+        def add_expected_values(expect: Expect, expected_value: Any, scope: Scope):
+            assert isinstance(expect, Value), "Non-Value expects currently unsupported"
+            if expect in active_expects and expected_value is None:
+                self.expect_matches[expect] = ExpectedValueWriter(
+                    expect, step.watches[expect.get_watched_expr()]
+                )
+
+        script.visit_script(visit_expect=add_expected_values)
+
+
+class ScriptExpectWriter:
+    """Given the full output from a debugger run and a script with missing expected values, returns a script with
+    filled-in expected values that match the debugger output."""
+
+    def __init__(self, context: Context, dext_ir: DextIR):
+        self.context = context
+        self.dext_ir = dext_ir
+        self.unknown_expect_rewrites: Dict[
+            Expect, List[Tuple[int, ExpectedValueWriter]]
+        ] = {}
+        self.new_script: Optional[DexterScript] = None
+        self.new_expected_values: Dict[Expect, Any] = {}
+        self.missing_expect_rewrites: List[Expect] = []
+
+        def collect_unknown_expects(expect: Expect, expected_value: Any, scope: Scope):
+            assert isinstance(expect, Value), "Non-Value expects currently unsupported"
+            if expected_value is None:
+                self.unknown_expect_rewrites[expect] = []
+
+        script = dext_ir.script
+        assert (
+            script is not None
+        ), "Cannot use ScriptExpectWriter on a non-script Dexter test."
+        script.visit_script(visit_expect=collect_unknown_expects)
+
+        # If there are no expects to update, then there is no rewriting to be done - exit early.
+        if not self.unknown_expect_rewrites:
+            return
+
+        self.step_writers = [StepExpectWriter(step, script) for step in dext_ir.steps]
----------------
jmorse wrote:

A comment along the lines of "groups x by somethingorother" for each of these collections would be useful.

https://github.com/llvm/llvm-project/pull/202799


More information about the llvm-commits mailing list