xref: /llvm-project/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py (revision 4e10b2bee71a45ffbf07fcce00d24ddf51867a0c)
1""" Test that evaluating expressions from within C++ lambdas works
2    Particularly, we test the case of capturing "this" and
3    using members of the captured object in expression evaluation
4    while we're on a breakpoint inside a lambda.
5"""
6
7
8import lldb
9from lldbsuite.test.lldbtest import *
10
11
12class ExprInsideLambdaTestCase(TestBase):
13
14    def expectExprError(self, expr : str, expected : str):
15        frame = self.thread.GetFrameAtIndex(0)
16        value = frame.EvaluateExpression(expr)
17        errmsg = value.GetError().GetCString()
18        self.assertIn(expected, errmsg)
19
20    def test_expr_inside_lambda(self):
21        """Test that lldb evaluating expressions inside lambda expressions works correctly."""
22        self.build()
23        (target, process, self.thread, bkpt) = \
24                lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.cpp"))
25
26        # Inside 'Foo::method'
27
28        # Check access to captured 'this'
29        self.expect_expr("class_var", result_type="int", result_value="109")
30        self.expect_expr("this->class_var", result_type="int", result_value="109")
31
32        # Check that captured shadowed variables take preference over the
33        # corresponding member variable
34        self.expect_expr("shadowed", result_type="int", result_value="5")
35        self.expect_expr("this->shadowed", result_type="int", result_value="-137")
36
37        # Check access to local captures
38        self.expect_expr("local_var", result_type="int", result_value="137")
39        self.expect_expr("*class_ptr", result_type="int", result_value="137")
40
41        # Check access to base class variables
42        self.expect_expr("base_var", result_type="int", result_value="14")
43        self.expect_expr("base_base_var", result_type="int", result_value="11")
44
45        # Check access to global variable
46        self.expect_expr("global_var", result_type="int", result_value="-5")
47
48        # Check access to multiple captures/member variables
49        self.expect_expr("(shadowed + this->shadowed) * (base_base_var + local_var - class_var)",
50                         result_type="int", result_value="-5148")
51
52        # Check base-class function call
53        self.expect_expr("baz_virt()", result_type="int", result_value="2")
54        self.expect_expr("base_var", result_type="int", result_value="14")
55        self.expect_expr("this->shadowed", result_type="int", result_value="-1")
56
57        # 'p this' should yield 'struct Foo*'
58        frame = self.thread.GetFrameAtIndex(0)
59        outer_class_addr = frame.GetValueForVariablePath("this->this")
60        self.expect_expr("this", result_value=outer_class_addr.GetValue())
61
62        lldbutil.continue_to_breakpoint(process, bkpt)
63
64        # Inside 'nested_lambda'
65
66        # Check access to captured 'this'. Should still be 'struct Foo*'
67        self.expect_expr("class_var", result_type="int", result_value="109")
68        self.expect_expr("global_var", result_type="int", result_value="-5")
69        self.expect_expr("this", result_value=outer_class_addr.GetValue())
70
71        # Check access to captures
72        self.expect_expr("lambda_local_var", result_type="int", result_value="5")
73        self.expect_expr("local_var", result_type="int", result_value="137")
74
75        # Check access to variable in previous frame which we didn't capture
76        self.expectExprError("local_var_copy", "use of undeclared identifier")
77
78        lldbutil.continue_to_breakpoint(process, bkpt)
79
80        # By-ref mutates source variable
81        self.expect_expr("lambda_local_var", result_type="int", result_value="0")
82
83        # By-value doesn't mutate source variable
84        self.expect_expr("local_var_copy", result_type="int", result_value="136")
85        self.expect_expr("local_var", result_type="int", result_value="137")
86
87        lldbutil.continue_to_breakpoint(process, bkpt)
88
89        # Inside 'LocalLambdaClass::inner_method'
90
91        # Check access to captured 'this'
92        self.expect_expr("lambda_class_local", result_type="int", result_value="-12345")
93        self.expect_expr("this->lambda_class_local", result_type="int", result_value="-12345")
94        self.expect_expr("outer_ptr->class_var", result_type="int", result_value="109")
95
96        # 'p this' should yield 'struct LocalLambdaClass*'
97        frame = self.thread.GetFrameAtIndex(0)
98        local_class_addr = frame.GetValueForVariablePath("this->this")
99        self.assertNotEqual(local_class_addr, outer_class_addr)
100        self.expect_expr("this", result_value=local_class_addr.GetValue())
101
102        # Can still access global variable
103        self.expect_expr("global_var", result_type="int", result_value="-5")
104
105        # Check access to outer top-level structure's members
106        self.expectExprError("class_var", ("use of non-static data member"
107                                           " 'class_var' of 'Foo' from nested type"))
108
109        self.expectExprError("base_var", ("use of non-static data member"
110                                           " 'base_var'"))
111
112        self.expectExprError("local_var", ("use of non-static data member 'local_var'"
113                                           " of '(unnamed class)' from nested type 'LocalLambdaClass'"))
114
115        # Inside non_capturing_method
116        lldbutil.continue_to_breakpoint(process, bkpt)
117        self.expect_expr("local", result_type="int", result_value="5")
118        self.expect_expr("local2", result_type="int", result_value="10")
119        self.expect_expr("local2 * local", result_type="int", result_value="50")
120
121        self.expectExprError("class_var", ("use of non-static data member"
122                                           " 'class_var' of 'Foo' from nested type"))
123