llvm-project/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
Michael Buch 012680faf4
[clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function (#122265)
When we generate the debug-info for a `VarDecl` we try to determine
whether it was introduced as part of a structure binding (aka a "holding
var"). If it was then we don't mark it as `artificial`.

The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer until
we hit a `DeclRefExpr` that refers to a `Decomposition`. For "tuple-like
decompositions", Clang will generate a call to a `template<size_t I> Foo
get(Bar)` function that retrieves the `Ith` element from the tuple-like
structure. If that function is a member function, we get an AST that
looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups <col:10> 'int' xvalue
  `-MaterializeTemporaryExpr <col:10> 'int' xvalue extended by Var 0x11d110cf8 'z1' 'std::tuple_element<0, B>::type &&'
    `-CXXMemberCallExpr <col:10> 'int'
      `-MemberExpr <col:10> '<bound member function type>' .get 0x11d104390
        `-ImplicitCastExpr <col:10> 'B' xvalue <NoOp>
          `-DeclRefExpr <col:10> 'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function (which
it is for `std::pair` in libc++ for example), then the AST is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple<int, int>>::type &' cinit
`-CallExpr <col:16> 'const typename tuple_element<0UL, tuple<int, int>>::type':'const int' lvalue adl
  |-ImplicitCastExpr <col:16> 'const typename tuple_element<0UL, tuple<int, int>>::type &(*)(const tuple<int, int> &) noexcept' <FunctionToPointerDecay>
  | `-DeclRefExpr <col:16> 'const typename tuple_element<0UL, tuple<int, int>>::type &(const tuple<int, int> &) noexcept' lvalue Function 0x1210262d8 'get' 'const typename tuple_element<0UL, tuple<int, int>>::type &(const tuple<int, int> &) noexcept' (FunctionTemplate 0x11d068088 'get')
  `-DeclRefExpr <col:16> 'const std::tuple<int, int>' lvalue Decomposition 0x121021518 '' 'const std::tuple<int, int> &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.

This patch adjusts `IgnoreUnlessSpelledInSource` so it unwraps implicit
`CallExpr`s. It's almost identical to how we treat implicit constructor
calls (unfortunately the code can't quite be re-used because a
`CXXConstructExpr` is-not a `CallExpr`, and we check `isElidable`, which
doesn't exist for regular function calls. So I added a new
`IgnoreImplicitCallSingleStep`).

Fixes https://github.com/llvm/llvm-project/issues/122028
2025-09-20 18:30:18 +01:00

115 lines
4.8 KiB
Python

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestStructuredBinding(TestBase):
@skipIf(oslist=["linux"], archs=["arm$"])
@skipIf(compiler="clang", compiler_version=["<", "14.0"])
def test(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "// break here", lldb.SBFileSpec("main.cpp")
)
self.expect_expr(
"a1",
result_type="A",
result_children=[
ValueCheck(name="x", type="int"),
ValueCheck(name="y", type="int"),
],
)
self.expect_expr("b1", result_type="char", result_value="'a'")
self.expect_expr("c1", result_type="char", result_value="'b'")
self.expect_expr("d1", result_type="short", result_value="50")
self.expect_expr("e1", result_type="int", result_value="60")
self.expect_expr("f1", result_type="char", result_value="'c'")
self.expect_expr(
"a2",
result_type="A",
result_children=[
ValueCheck(name="x", type="int"),
ValueCheck(name="y", type="int"),
],
)
self.expect_expr("b2", result_type="char", result_value="'a'")
self.expect_expr("c2", result_type="char", result_value="'b'")
self.expect_expr("d2", result_type="short", result_value="50")
self.expect_expr("e2", result_type="int", result_value="60")
self.expect_expr("f2", result_type="char", result_value="'c'")
self.expect_expr(
"a3",
result_type="A",
result_children=[
ValueCheck(name="x", type="int"),
ValueCheck(name="y", type="int"),
],
)
self.expect_expr("b3", result_type="char", result_value="'a'")
self.expect_expr("c3", result_type="char", result_value="'b'")
self.expect_expr("d3", result_type="short", result_value="50")
self.expect_expr("e3", result_type="int", result_value="60")
self.expect_expr("f3", result_type="char", result_value="'c'")
self.expect_expr("carr_ref1", result_type="char", result_value="'a'")
self.expect_expr("carr_ref2", result_type="char", result_value="'b'")
self.expect_expr("carr_ref3", result_type="char", result_value="'c'")
self.expect_expr("sarr_ref1", result_type="short", result_value="11")
self.expect_expr("sarr_ref2", result_type="short", result_value="12")
self.expect_expr("sarr_ref3", result_type="short", result_value="13")
self.expect_expr("iarr_ref1", result_type="int", result_value="22")
self.expect_expr("iarr_ref2", result_type="int", result_value="33")
self.expect_expr("iarr_ref3", result_type="int", result_value="44")
self.expect_expr("carr_rref1", result_type="char", result_value="'a'")
self.expect_expr("carr_rref2", result_type="char", result_value="'b'")
self.expect_expr("carr_rref3", result_type="char", result_value="'c'")
self.expect_expr("sarr_rref1", result_type="short", result_value="11")
self.expect_expr("sarr_rref2", result_type="short", result_value="12")
self.expect_expr("sarr_rref3", result_type="short", result_value="13")
self.expect_expr("iarr_rref1", result_type="int", result_value="22")
self.expect_expr("iarr_rref2", result_type="int", result_value="33")
self.expect_expr("iarr_rref3", result_type="int", result_value="44")
self.expect_expr("carr_copy1", result_type="char", result_value="'a'")
self.expect_expr("carr_copy2", result_type="char", result_value="'b'")
self.expect_expr("carr_copy3", result_type="char", result_value="'c'")
self.expect_expr("sarr_copy1", result_type="short", result_value="11")
self.expect_expr("sarr_copy2", result_type="short", result_value="12")
self.expect_expr("sarr_copy3", result_type="short", result_value="13")
self.expect_expr("iarr_copy1", result_type="int", result_value="22")
self.expect_expr("iarr_copy2", result_type="int", result_value="33")
self.expect_expr("iarr_copy3", result_type="int", result_value="44")
self.expect_expr("tx1", result_value="4")
self.expect_expr("ty1", result_value="'z'")
self.expect_expr("tz1", result_value="10")
self.expect_expr("tx2", result_value="4")
self.expect_expr("ty2", result_value="'z'")
self.expect_expr("tz2", result_value="10")
self.expect(
"frame variable",
substrs=[
"tx1 =",
"ty1 =",
"tz1 =",
"tx2 =",
"ty2 =",
"tz2 =",
"mp1 =",
"mp2 =",
],
)