Skip to content

Conversation

@dmpots
Copy link
Contributor

@dmpots dmpots commented Nov 11, 2025

This commit adds a new ValueMatcher class that can be used in gtest matching contexts to match against lldb_private::Value objects. We always match against the values value_type and context_type. For HostAddress values we will also match against the expected host buffer contents. For Scalar, FileAddress, and LoadAddress values we match against an expected Scalar value.

The matcher is used to improve the quality of the tests in the DwarfExpressionTest.cpp file. Previously, the local Evaluate function would return an Expected<Scalar> value which makes it hard to verify that we actually get a Value of the expected type without adding custom evaluation code. Now we return an Expected<Value> so that we can match against the full value contents.

The resulting change improves the quality of the existing checks and in some cases eliminates the need for special code to explicitly check value types.

This commit adds a new `ValueMatcher` class that can be used in gtest
matching contexts to match against `lldb_private::Value` objects. We
always match against the values `value_type` and `context_type`. For
HostAddress values we will also match against the expected host buffer
contents. For Scalar, FileAddress, and LoadAddress values we match
against an expected Scalar value.

The matcher is used to improve the quality of the tests in the
`DwarfExpressionTest.cpp` file. Previously, the local `Evaluate`
function would return an `Expected<Scalar>` value which makes it hard to
verify that we actually get a Value of the expected type. Now we return
an `Expected<Value>` so that we can match against the full value
contents.

The resulting change improves the quality of the existing checks and in
some cases eliminates the need for special code to explicitly check
value types.
@llvmbot
Copy link
Member

llvmbot commented Nov 11, 2025

@llvm/pr-subscribers-lldb

Author: David Peixotto (dmpots)

Changes

This commit adds a new ValueMatcher class that can be used in gtest matching contexts to match against lldb_private::Value objects. We always match against the values value_type and context_type. For HostAddress values we will also match against the expected host buffer contents. For Scalar, FileAddress, and LoadAddress values we match against an expected Scalar value.

The matcher is used to improve the quality of the tests in the DwarfExpressionTest.cpp file. Previously, the local Evaluate function would return an Expected&lt;Scalar&gt; value which makes it hard to verify that we actually get a Value of the expected type without adding custom evaluation code. Now we return an Expected&lt;Value&gt; so that we can match against the full value contents.

The resulting change improves the quality of the existing checks and in some cases eliminates the need for special code to explicitly check value types.


Patch is 28.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167427.diff

2 Files Affected:

  • (modified) lldb/unittests/Expression/DWARFExpressionTest.cpp (+60-142)
  • (added) lldb/unittests/Expression/ValueMatcher.h (+283)
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 9d11060becfae..8c5568d9e4e65 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -5,8 +5,8 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-
 #include "lldb/Expression/DWARFExpression.h"
+#include "ValueMatcher.h"
 #ifdef ARCH_AARCH64
 #include "Plugins/ABI/AArch64/ABISysV_arm64.h"
 #endif
@@ -135,40 +135,18 @@ class MockRegisterContext : public RegisterContext {
 };
 } // namespace
 
-static llvm::Expected<Scalar> Evaluate(llvm::ArrayRef<uint8_t> expr,
-                                       lldb::ModuleSP module_sp = {},
-                                       DWARFUnit *unit = nullptr,
-                                       ExecutionContext *exe_ctx = nullptr,
-                                       RegisterContext *reg_ctx = nullptr) {
+static llvm::Expected<Value> Evaluate(llvm::ArrayRef<uint8_t> expr,
+                                      lldb::ModuleSP module_sp = {},
+                                      DWARFUnit *unit = nullptr,
+                                      ExecutionContext *exe_ctx = nullptr,
+                                      RegisterContext *reg_ctx = nullptr) {
   DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle,
                           /*addr_size*/ 4);
 
-  llvm::Expected<Value> result = DWARFExpression::Evaluate(
-      exe_ctx, reg_ctx, module_sp, extractor, unit, lldb::eRegisterKindLLDB,
-      /*initial_value_ptr=*/nullptr,
-      /*object_address_ptr=*/nullptr);
-  if (!result)
-    return result.takeError();
-
-  switch (result->GetValueType()) {
-  case Value::ValueType::Scalar:
-    return result->GetScalar();
-  case Value::ValueType::LoadAddress:
-    return LLDB_INVALID_ADDRESS;
-  case Value::ValueType::HostAddress: {
-    // Convert small buffers to scalars to simplify the tests.
-    DataBufferHeap &buf = result->GetBuffer();
-    if (buf.GetByteSize() <= 8) {
-      uint64_t val = 0;
-      memcpy(&val, buf.GetBytes(), buf.GetByteSize());
-      return Scalar(llvm::APInt(buf.GetByteSize() * 8, val, false));
-    }
-  }
-    [[fallthrough]];
-  default:
-    break;
-  }
-  return llvm::createStringError("unsupported value type");
+  return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, extractor, unit,
+                                   lldb::eRegisterKindLLDB,
+                                   /*initial_value_ptr=*/nullptr,
+                                   /*object_address_ptr=*/nullptr);
 }
 
 class DWARFExpressionTester : public YAMLModuleTester {
@@ -177,18 +155,11 @@ class DWARFExpressionTester : public YAMLModuleTester {
       : YAMLModuleTester(yaml_data, cu_index) {}
 
   using YAMLModuleTester::YAMLModuleTester;
-  llvm::Expected<Scalar> Eval(llvm::ArrayRef<uint8_t> expr) {
+  llvm::Expected<Value> Eval(llvm::ArrayRef<uint8_t> expr) {
     return ::Evaluate(expr, m_module_sp, m_dwarf_unit);
   }
 };
 
-/// Unfortunately Scalar's operator==() is really picky.
-static Scalar GetScalar(unsigned bits, uint64_t value, bool sign) {
-  Scalar scalar(value);
-  scalar.TruncOrExtendTo(bits, sign);
-  return scalar;
-}
-
 /// This is needed for the tests that use a mock process.
 class DWARFExpressionMockProcessTest : public ::testing::Test {
 public:
@@ -255,48 +226,48 @@ class MockTarget : public Target {
 
 TEST(DWARFExpression, DW_OP_pick) {
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0}),
-                       llvm::HasValue(0));
+                       ExpectScalar(0));
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 1}),
-                       llvm::HasValue(1));
+                       ExpectScalar(1));
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 2}),
                        llvm::Failed());
 }
 
 TEST(DWARFExpression, DW_OP_const) {
   // Extend to address size.
-  EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x88}), llvm::HasValue(0x88));
+  EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x88}), ExpectScalar(0x88));
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1s, 0x88}),
-                       llvm::HasValue(0xffffff88));
+                       ExpectScalar(0xffffff88));
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x47, 0x88}),
-                       llvm::HasValue(0x8847));
+                       ExpectScalar(0x8847));
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2s, 0x47, 0x88}),
-                       llvm::HasValue(0xffff8847));
+                       ExpectScalar(0xffff8847));
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4u, 0x44, 0x42, 0x47, 0x88}),
-                       llvm::HasValue(0x88474244));
+                       ExpectScalar(0x88474244));
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4s, 0x44, 0x42, 0x47, 0x88}),
-                       llvm::HasValue(0x88474244));
+                       ExpectScalar(0x88474244));
 
   // Truncate to address size.
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_const8u, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
-      llvm::HasValue(0x33221100));
+      ExpectScalar(0x33221100));
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_const8s, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
-      llvm::HasValue(0x33221100));
+      ExpectScalar(0x33221100));
 
   // Don't truncate to address size for compatibility with clang (pr48087).
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_constu, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
-      llvm::HasValue(0x01010101010101));
+      ExpectScalar(0x01010101010101));
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_consts, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
-      llvm::HasValue(0xffff010101010101));
+      ExpectScalar(0xffff010101010101));
 }
 
 TEST(DWARFExpression, DW_OP_skip) {
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x42, DW_OP_skip, 0x02, 0x00,
                                  DW_OP_const1u, 0xff}),
-                       llvm::HasValue(0x42));
+                       ExpectScalar(0x42));
 }
 
 TEST(DWARFExpression, DW_OP_bra) {
@@ -309,7 +280,7 @@ TEST(DWARFExpression, DW_OP_bra) {
         DW_OP_const1u, 0xff,     // push 0xff
       }),
       // clang-format on
-      llvm::HasValue(0x42));
+      ExpectScalar(0x42));
 
   EXPECT_THAT_ERROR(Evaluate({DW_OP_bra, 0x01, 0x00}).takeError(),
                     llvm::Failed());
@@ -414,42 +385,42 @@ TEST(DWARFExpression, DW_OP_convert) {
   EXPECT_THAT_EXPECTED(
       t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //
               DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),
-      llvm::HasValue(GetScalar(64, 0x44332211, not_signed)));
+      ExpectScalar(64, 0x44332211, not_signed));
 
   // Zero-extend to 64 bits.
   EXPECT_THAT_EXPECTED(
       t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //
               DW_OP_convert, offs_uint64_t, DW_OP_stack_value}),
-      llvm::HasValue(GetScalar(64, 0x44332211, not_signed)));
+      ExpectScalar(64, 0x44332211, not_signed));
 
   // Sign-extend to 64 bits.
   EXPECT_THAT_EXPECTED(
       t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
               DW_OP_convert, offs_sint64_t, DW_OP_stack_value}),
-      llvm::HasValue(GetScalar(64, 0xffffffffffeeddcc, is_signed)));
+      ExpectScalar(64, 0xffffffffffeeddcc, is_signed));
 
   // Sign-extend, then truncate.
   EXPECT_THAT_EXPECTED(
       t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
               DW_OP_convert, offs_sint64_t,          //
               DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),
-      llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed)));
+      ExpectScalar(32, 0xffeeddcc, not_signed));
 
   // Truncate to default unspecified (pointer-sized) type.
   EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
                                DW_OP_convert, offs_sint64_t,          //
                                DW_OP_convert, 0x00, DW_OP_stack_value}),
-                       llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed)));
+                       ExpectScalar(32, 0xffeeddcc, not_signed));
 
   // Truncate to 8 bits.
   EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,
                                offs_uchar, DW_OP_stack_value}),
-                       llvm::HasValue(GetScalar(8, 'A', not_signed)));
+                       ExpectScalar(8, 'A', not_signed));
 
   // Also truncate to 8 bits.
   EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,
                                offs_schar, DW_OP_stack_value}),
-                       llvm::HasValue(GetScalar(8, 'A', is_signed)));
+                       ExpectScalar(8, 'A', is_signed));
 
   //
   // Errors.
@@ -479,33 +450,21 @@ TEST(DWARFExpression, DW_OP_stack_value) {
 TEST(DWARFExpression, DW_OP_piece) {
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x11, 0x22, DW_OP_piece, 2,
                                  DW_OP_const2u, 0x33, 0x44, DW_OP_piece, 2}),
-                       llvm::HasValue(GetScalar(32, 0x44332211, true)));
+                       ExpectHostAddress({0x11, 0x22, 0x33, 0x44}));
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_piece, 1, DW_OP_const1u, 0xff, DW_OP_piece, 1}),
       // Note that the "00" should really be "undef", but we can't
       // represent that yet.
-      llvm::HasValue(GetScalar(16, 0xff00, true)));
-}
-
-TEST(DWARFExpression, DW_OP_piece_host_address) {
-  static const uint8_t expr_data[] = {DW_OP_lit2, DW_OP_stack_value,
-                                      DW_OP_piece, 40};
-  llvm::ArrayRef<uint8_t> expr(expr_data, sizeof(expr_data));
-  DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle, 4);
+      ExpectHostAddress({0x00, 0xff}));
 
   // This tests if ap_int is extended to the right width.
   // expect 40*8 = 320 bits size.
-  llvm::Expected<Value> result =
-      DWARFExpression::Evaluate(nullptr, nullptr, nullptr, extractor, nullptr,
-                                lldb::eRegisterKindDWARF, nullptr, nullptr);
-  ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
-  ASSERT_EQ(result->GetValueType(), Value::ValueType::HostAddress);
-  ASSERT_EQ(result->GetBuffer().GetByteSize(), 40ul);
-  const uint8_t *data = result->GetBuffer().GetBytes();
-  ASSERT_EQ(data[0], 2);
-  for (int i = 1; i < 40; i++) {
-    ASSERT_EQ(data[i], 0);
-  }
+  std::vector<uint8_t> expected_host_buffer(40, 0);
+  expected_host_buffer[0] = 2;
+
+  EXPECT_THAT_EXPECTED(
+      Evaluate({{DW_OP_lit2, DW_OP_stack_value, DW_OP_piece, 40}}),
+      ExpectHostAddress(expected_host_buffer));
 }
 
 TEST(DWARFExpression, DW_OP_implicit_value) {
@@ -513,7 +472,7 @@ TEST(DWARFExpression, DW_OP_implicit_value) {
 
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_implicit_value, bytes, 0x11, 0x22, 0x33, 0x44}),
-      llvm::HasValue(GetScalar(8 * bytes, 0x44332211, true)));
+      ExpectHostAddress({0x11, 0x22, 0x33, 0x44}));
 }
 
 TEST(DWARFExpression, DW_OP_unknown) {
@@ -548,20 +507,13 @@ TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {
   // Implicit location: *0x4.
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx),
-      llvm::HasValue(GetScalar(32, 0x07060504, false)));
+      ExpectScalar(32, 0x07060504, false));
   // Memory location: *(*0x4).
-  // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref}, {}, {}, &exe_ctx),
-                       llvm::HasValue(Scalar(LLDB_INVALID_ADDRESS)));
+                       ExpectLoadAddress(0x07060504));
   // Memory location: *0x4.
-  // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
   EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4}, {}, {}, &exe_ctx),
-                       llvm::HasValue(Scalar(4)));
-  // Implicit location: *0x4.
-  // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
-  EXPECT_THAT_EXPECTED(
-      Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx),
-      llvm::HasValue(GetScalar(32, 0x07060504, false)));
+                       ExpectScalar(Scalar(4)));
 }
 
 TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr) {
@@ -581,18 +533,9 @@ TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr) {
 
   ExecutionContext exe_ctx(target_sp, false);
   // DW_OP_addr takes a single operand of address size width:
-  uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0};
-  DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
-                          /*addr_size*/ 4);
-
-  llvm::Expected<Value> result = DWARFExpression::Evaluate(
-      &exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor,
-      /*unit*/ nullptr, lldb::eRegisterKindLLDB,
-      /*initial_value_ptr*/ nullptr,
-      /*object_address_ptr*/ nullptr);
-
-  ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
-  ASSERT_EQ(result->GetValueType(), Value::ValueType::LoadAddress);
+  EXPECT_THAT_EXPECTED(
+      Evaluate({DW_OP_addr, 0x40, 0x0, 0x0, 0x0}, {}, {}, &exe_ctx),
+      ExpectLoadAddress(0x40));
 }
 
 TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) {
@@ -676,15 +619,11 @@ TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) {
   DWARFExpression expr(extractor);
 
   llvm::Expected<Value> result = evaluate(expr);
-  ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
-  ASSERT_EQ(result->GetValueType(), Value::ValueType::LoadAddress);
-  ASSERT_EQ(result->GetScalar().UInt(), 0x5678u);
+  EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0x5678u));
 
   ASSERT_TRUE(expr.Update_DW_OP_addr(dwarf_cu, 0xdeadbeef));
   result = evaluate(expr);
-  ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
-  ASSERT_EQ(result->GetValueType(), Value::ValueType::LoadAddress);
-  ASSERT_EQ(result->GetScalar().UInt(), 0xdeadbeefu);
+  EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0xdeadbeefu));
 }
 
 class CustomSymbolFileDWARF : public SymbolFileDWARF {
@@ -778,11 +717,12 @@ static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp,
                                            RegisterContext *reg_ctx) {
   // Test that expression extensions can be evaluated, for example
   // DW_OP_WASM_location which is not currently handled by DWARFExpression:
-  EXPECT_THAT_EXPECTED(Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03
-                                 0x04, 0x00, 0x00,          // index:u32
-                                 0x00, DW_OP_stack_value},
-                                module_sp, &dwarf_unit, nullptr, reg_ctx),
-                       llvm::HasValue(GetScalar(32, 42, false)));
+  EXPECT_THAT_EXPECTED(
+      Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03
+                0x04, 0x00, 0x00,          // index:u32
+                0x00, DW_OP_stack_value},
+               module_sp, &dwarf_unit, nullptr, reg_ctx),
+      ExpectScalar(32, 42, false, Value::ContextType::RegisterInfo));
 
   // Test that searches for opcodes work in the presence of extensions:
   uint8_t expr[] = {DW_OP_WASM_location,   0x03, 0x04, 0x00, 0x00, 0x00,
@@ -1148,17 +1088,8 @@ TEST_F(DWARFExpressionMockProcessTest, DW_OP_piece_file_addr) {
 
   uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0, DW_OP_piece, 1,
                     DW_OP_addr, 0x50, 0x0, 0x0, 0x0, DW_OP_piece, 1};
-  DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
-                          /*addr_size=*/4);
-  llvm::Expected<Value> result = DWARFExpression::Evaluate(
-      &exe_ctx, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor,
-      /*unit=*/nullptr, lldb::eRegisterKindLLDB,
-      /*initial_value_ptr=*/nullptr,
-      /*object_address_ptr=*/nullptr);
-
-  ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
-  ASSERT_EQ(result->GetValueType(), Value::ValueType::HostAddress);
-  ASSERT_THAT(result->GetBuffer().GetData(), ElementsAre(0x11, 0x22));
+  EXPECT_THAT_EXPECTED(Evaluate(expr, {}, {}, &exe_ctx),
+                       ExpectHostAddress({0x11, 0x22}));
 }
 
 /// A Process whose `ReadMemory` override queries a DenseMap.
@@ -1228,28 +1159,15 @@ TEST_F(DWARFExpressionMockProcessTestWithAArch, DW_op_deref_no_ptr_fixing) {
   process_sp->GetThreadList().AddThread(thread);
 
   auto evaluate_expr = [&](auto &expr_data) {
-    DataExtractor extractor(expr_data, sizeof(expr_data),
-                            lldb::eByteOrderLittle,
-                            /*addr_size*/ 8);
-    DWARFExpression expr(extractor);
-
     ExecutionContext exe_ctx(process_sp);
-    llvm::Expected<Value> result = DWARFExpression::Evaluate(
-        &exe_ctx, reg_ctx_sp.get(), /*module_sp*/ nullptr, extractor,
-        /*unit*/ nullptr, lldb::eRegisterKindLLDB,
-        /*initial_value_ptr=*/nullptr,
-        /*object_address_ptr=*/nullptr);
-    return result;
+    return Evaluate(expr_data, {}, {}, &exe_ctx, reg_ctx_sp.get());
   };
 
   uint8_t expr_reg[] = {DW_OP_breg22, 0};
   llvm::Expected<Value> result_reg = evaluate_expr(expr_reg);
-  ASSERT_THAT_EXPECTED(result_reg, llvm::Succeeded());
-  ASSERT_EQ(result_reg->GetValueType(), Value::ValueType::LoadAddress);
-  ASSERT_EQ(result_reg->GetScalar().ULongLong(), addr);
+  EXPECT_THAT_EXPECTED(result_reg, ExpectLoadAddress(addr));
 
   uint8_t expr_deref[] = {DW_OP_breg22, 0, DW_OP_deref};
   llvm::Expected<Value> result_deref = evaluate_expr(expr_deref);
-  ASSERT_THAT_EXPECTED(result_deref, llvm::Succeeded());
-  ASSERT_EQ(result_deref->GetScalar().ULongLong(), expected_value);
+  EXPECT_THAT_EXPECTED(result_deref, ExpectLoadAddress(expected_value));
 }
diff --git a/lldb/unittests/Expression/ValueMatcher.h b/lldb/unittests/Expression/ValueMatcher.h
new file mode 100644
index 0000000000000..38672bf054e0f
--- /dev/null
+++ b/lldb/unittests/Expression/ValueMatcher.h
@@ -0,0 +1,283 @@
+//===-- ValueMatcher.h ----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file contains the definition of the ValueMatcher class which is a used
+/// to match lldb_private::Value in gtest assert/expect macros. It also contains
+/// several helper functions to create matchers for common Value types.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UNITTESTS_EXPRESSION_VALUEMATCHER_H
+#define LLDB_UNITTESTS_EXPRESSION_VALUEMATCHER_H
+
+#include "lldb/Core/Value.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+#include <iomanip>
+#include <vector>
+
+namespace lldb_private {
+
+/// Helper function to format Value details to an ostream.
+///
+/// This is used for format the details coming from either a Value directly
+/// or the parts stored in the ValueMatcher object.
+inline void FormatValueDetails(std::ostream &os, Value::ValueType value_type,
+                               Value::ContextType context_type,
+                               const Scalar &scalar,
+                               llvm::ArrayRef<uint8_t> buffer_data) {
+  os << "Value(";
+  os << "value_type=" << Value::GetValueTypeAsCString(value_type);
+  os << ", context_type=" << Value::GetContextTypeAsCString(context_type);
+
+  if (value_type == Value::ValueType::HostAddress) {
+    os << ", buffer=[";
+    for (size_t i = 0; i < std::min(buffer_data.size(), size_t(16)); ++i) {
+      if (i > 0)
+        os << " ";
+      os << std::hex << std::setw(2) << std::setfill('0')
+         << static_cast<int>(buffer_data[i]);
+    }
+    if (buffer_data.size() > 16) {
+      os << " ...";
+    }
+    os << std::dec << "] (" << buffer_data.size() << " bytes)";
+  } else {
+    std::string scalar_str;
+    llvm::raw_string_ostream scalar_os(scalar_str);
+    scalar_os << scalar;
+    os << ", value=" << scalar_os.str();
+  }
+  os << ")";
+}
+
+/// Custom printer for Value objects to make test failures more readable.
+inline void PrintTo(const Value &val, std::ostream *os) {
+  if (!os)
+    return...
[truncated]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2 participants