Reapply "[Dexter] Add rewriting for aggregate variables (#202800)" (#206535)

This reverts commit 5bf495456b9898c45c437b8dadae21417d04a9a5.

Reapply the original patch after the underlying issue was fixed in PR
#206732.
This commit is contained in:
Stephen Tozer 2026-06-30 16:55:06 +01:00 committed by GitHub
parent f49ca7997a
commit 19a0860da1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 253 additions and 3 deletions

View file

@ -510,6 +510,7 @@ class LLDBDAP(DAP):
"couldn't read from memory",
"Cannot access memory at address",
"invalid address (fault address:",
"error: parent is NULL",
]
)

View file

@ -25,7 +25,20 @@ class ExpectedValueRewriter:
def __init__(self, expect: Expect, value: ValueIR):
self.expect = expect
self.root_value = value
self.expected_value = expect.get_variable_result(value)
self.expected_value: Union[Dict, str, None] = None
if sub_values := self.root_value.sub_values:
self.expected_value = {
sub_value.expression: expected_value
for sub_value in sub_values
if (
expected_value := ExpectedValueRewriter(
expect, sub_value
).expected_value
)
is not None
}
if not self.expected_value:
self.expected_value = expect.get_variable_result(value)
def unique_expected_values(elements: List[ExpectedValueRewriter]):
@ -33,14 +46,21 @@ def unique_expected_values(elements: List[ExpectedValueRewriter]):
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."""
def freeze(input):
assert input is not None, "Unexpected 'None' in an expected_value"
if isinstance(input, dict):
return tuple(sorted((str(k), freeze(v)) for k, v in input.items()))
return input
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)
frozen_value = freeze(expected_value)
if frozen_value not in unique_set:
unique_set.add(frozen_value)
result.append(expected_value)
if not result:
return None

View file

@ -0,0 +1,70 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: %dexter_regression_test_cxx_build %s -o %t/test
// RUN: %dexter_regression_test_run --use-script --binary %t/test \
// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s
// RUN: diff %t/results/%{s:basename} %S/Inputs/rewrite_aggregates_expected.cpp
/// Test that Dexter can write disaggregated expected values for aggregates,
/// including falling back to the parent value if sub_values contain errors,
/// e.g. for pointers that are not dereferencable.
/// NB: The exact contents of this file are compared against the expect file in
/// the Inputs/ directory; any changes to this file, including comments,
/// will require updating the corresponding expected file.
// CHECK: Rewrote script to add 5 expected values.
// CHECK: total_watched_steps: 5
// CHECK: correct_steps: 5
// CHECK: incorrect_steps: 0
// CHECK: seen_values: 16
// CHECK: missing_values: 0
struct Point {
int X;
int Y;
int Z;
};
struct Rect {
Point TopLeft;
Point BottomRight;
};
int main() {
Point P{1, 2, 3};
int *I = &P.X;
Rect R{{1, 1, 1}, {2, 2, 2}};
int L[] = {0, 1, 2, 3, 4};
int *InvalidPtr = nullptr;
return 0; // !dex_label ret
}
/*
---
? !where {lines: !label 'ret'}
: !value 'P':
X: '1'
Y: '2'
Z: '3'
!value 'I':
'*I': '1'
!value 'R':
BottomRight:
X: '2'
Y: '2'
Z: '2'
TopLeft:
X: '1'
Y: '1'
Z: '1'
!value 'L':
'[0]': '0'
'[1]': '1'
'[2]': '2'
'[3]': '3'
'[4]': '4'
!value 'InvalidPtr': '0x0000000000000000'
...
*/

View file

@ -0,0 +1,58 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: %dexter_regression_test_cxx_build %s -o %t/test
// RUN: %dexter_regression_test_run --use-script --binary %t/test \
// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s
// RUN: diff %t/results/%{s:basename} \
// RUN: %S/Inputs/rewrite_list_aggregates_expected.cpp
/// Test that Dexter can write expects for variables that are aggregates and
/// have more than one value, without writing any duplicate expected values.
/// NB: The exact contents of this file are compared against the expect file in
/// the Inputs/ directory; any changes to this file, including comments,
/// will require updating the corresponding expected file.
// CHECK: Rewrote script to add 1 expected values.
struct Point {
int X;
int Y;
};
int main() {
Point P{1, 2};
P.X = 3; // !dex_label start
P.Y = 0;
P.X = 1;
P.Y = 2;
P = {0, 0};
return 0; // !dex_label end
}
// CHECK: total_watched_steps: 6
// CHECK: correct_steps: 6
// CHECK: incorrect_steps: 0
// CHECK: partial_step_correctness: 6.0
// CHECK: missing_var_steps: 0
// CHECK: unexpected_value_steps: 0
// CHECK: correct_step_coverage: 100.0% (6/6)
// CHECK: seen_values: 10
// CHECK: missing_values: 0
/*
---
? !where {lines: !range [!label 'start', !label 'end']}
: !value 'P':
- X: '1'
Y: '2'
- X: '3'
Y: '2'
- X: '3'
Y: '0'
- X: '1'
Y: '0'
- X: '0'
Y: '0'
...
*/

View file

@ -0,0 +1,53 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: %dexter_regression_test_cxx_build %s -o %t/test
// RUN: %dexter_regression_test_run --use-script --binary %t/test \
// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s
// RUN: diff %t/results/%{s:basename} %S/Inputs/rewrite_aggregates_expected.cpp
/// Test that Dexter can write disaggregated expected values for aggregates,
/// including falling back to the parent value if sub_values contain errors,
/// e.g. for pointers that are not dereferencable.
/// NB: The exact contents of this file are compared against the expect file in
/// the Inputs/ directory; any changes to this file, including comments,
/// will require updating the corresponding expected file.
// CHECK: Rewrote script to add 5 expected values.
// CHECK: total_watched_steps: 5
// CHECK: correct_steps: 5
// CHECK: incorrect_steps: 0
// CHECK: seen_values: 16
// CHECK: missing_values: 0
struct Point {
int X;
int Y;
int Z;
};
struct Rect {
Point TopLeft;
Point BottomRight;
};
int main() {
Point P{1, 2, 3};
int *I = &P.X;
Rect R{{1, 1, 1}, {2, 2, 2}};
int L[] = {0, 1, 2, 3, 4};
int *InvalidPtr = nullptr;
return 0; // !dex_label ret
}
/*
---
!where {lines: !label ret}:
? !value P
? !value I
? !value R
? !value L
? !value InvalidPtr
...
*/

View file

@ -0,0 +1,48 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: %dexter_regression_test_cxx_build %s -o %t/test
// RUN: %dexter_regression_test_run --use-script --binary %t/test \
// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s
// RUN: diff %t/results/%{s:basename} \
// RUN: %S/Inputs/rewrite_list_aggregates_expected.cpp
/// Test that Dexter can write expects for variables that are aggregates and
/// have more than one value, without writing any duplicate expected values.
/// NB: The exact contents of this file are compared against the expect file in
/// the Inputs/ directory; any changes to this file, including comments,
/// will require updating the corresponding expected file.
// CHECK: Rewrote script to add 1 expected values.
struct Point {
int X;
int Y;
};
int main() {
Point P{1, 2};
P.X = 3; // !dex_label start
P.Y = 0;
P.X = 1;
P.Y = 2;
P = {0, 0};
return 0; // !dex_label end
}
// CHECK: total_watched_steps: 6
// CHECK: correct_steps: 6
// CHECK: incorrect_steps: 0
// CHECK: partial_step_correctness: 6.0
// CHECK: missing_var_steps: 0
// CHECK: unexpected_value_steps: 0
// CHECK: correct_step_coverage: 100.0% (6/6)
// CHECK: seen_values: 10
// CHECK: missing_values: 0
/*
---
!where {lines: !range [!label start, !label end]}:
? !value P
...
*/