xref: /llvm-project/lldb/test/API/commands/expression/fixits/TestFixIts.py (revision fbaf36721783c3bcbd45f81294e6980eaef165e4)
1"""
2Test calling an expression with errors that a FixIt can fix.
3"""
4
5import lldb
6from lldbsuite.test.decorators import *
7from lldbsuite.test.lldbtest import *
8from lldbsuite.test import lldbutil
9
10
11class ExprCommandWithFixits(TestBase):
12
13    mydir = TestBase.compute_mydir(__file__)
14
15    def test_with_dummy_target(self):
16        """Test calling expressions in the dummy target with errors that can be fixed by the FixIts."""
17
18        # Enable fix-its as they were intentionally disabled by TestBase.setUp.
19        self.runCmd("settings set target.auto-apply-fixits true")
20
21        ret_val = lldb.SBCommandReturnObject()
22        result = self.dbg.GetCommandInterpreter().HandleCommand("expression ((1 << 16) - 1))", ret_val)
23        self.assertEqual(result, lldb.eReturnStatusSuccessFinishResult, ret_val.GetError())
24        self.assertIn("Fix-it applied", ret_val.GetError())
25
26    def test_with_target(self):
27        """Test calling expressions with errors that can be fixed by the FixIts."""
28        self.build()
29        (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
30                                        'Stop here to evaluate expressions',
31                                         lldb.SBFileSpec("main.cpp"))
32
33        options = lldb.SBExpressionOptions()
34        options.SetAutoApplyFixIts(True)
35
36        top_level_options = lldb.SBExpressionOptions()
37        top_level_options.SetAutoApplyFixIts(True)
38        top_level_options.SetTopLevel(True)
39
40        frame = self.thread.GetFrameAtIndex(0)
41
42        # Try with one error:
43        value = frame.EvaluateExpression("my_pointer.first", options)
44        self.assertTrue(value.IsValid())
45        self.assertSuccess(value.GetError())
46        self.assertEquals(value.GetValueAsUnsigned(), 10)
47
48        # Try with one error in a top-level expression.
49        # The Fix-It changes "ptr.m" to "ptr->m".
50        expr = "struct X { int m; }; X x; X *ptr = &x; int m = ptr.m;"
51        value = frame.EvaluateExpression(expr, top_level_options)
52        # A successfully parsed top-level expression will yield an error
53        # that there is 'no value'. If a parsing error would have happened we
54        # would get a different error kind, so let's check the error kind here.
55        self.assertEquals(value.GetError().GetCString(), "error: No value")
56
57        # Try with two errors:
58        two_error_expression = "my_pointer.second->a"
59        value = frame.EvaluateExpression(two_error_expression, options)
60        self.assertTrue(value.IsValid())
61        self.assertSuccess(value.GetError())
62        self.assertEquals(value.GetValueAsUnsigned(), 20)
63
64        # Try a Fix-It that is stored in the 'note:' diagnostic of an error.
65        # The Fix-It here is adding parantheses around the ToStr parameters.
66        fixit_in_note_expr ="#define ToStr(x) #x\nToStr(0 {, })"
67        value = frame.EvaluateExpression(fixit_in_note_expr, options)
68        self.assertTrue(value.IsValid())
69        self.assertSuccess(value.GetError())
70        self.assertEquals(value.GetSummary(), '"(0 {, })"')
71
72        # Now turn off the fixits, and the expression should fail:
73        options.SetAutoApplyFixIts(False)
74        value = frame.EvaluateExpression(two_error_expression, options)
75        self.assertTrue(value.IsValid())
76        self.assertTrue(value.GetError().Fail())
77        error_string = value.GetError().GetCString()
78        self.assertTrue(
79            error_string.find("fixed expression suggested:") != -1,
80            "Fix was suggested")
81        self.assertTrue(
82            error_string.find("my_pointer->second.a") != -1,
83            "Fix was right")
84
85    def test_with_target_error_applies_fixit(self):
86        """ Check that applying a Fix-it which fails to execute correctly still
87         prints that the Fix-it was applied. """
88        self.build()
89        (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
90                                        'Stop here to evaluate expressions',
91                                         lldb.SBFileSpec("main.cpp"))
92        # Enable fix-its as they were intentionally disabled by TestBase.setUp.
93        self.runCmd("settings set target.auto-apply-fixits true")
94        ret_val = lldb.SBCommandReturnObject()
95        result = self.dbg.GetCommandInterpreter().HandleCommand("expression null_pointer.first", ret_val)
96        self.assertEqual(result, lldb.eReturnStatusFailed, ret_val.GetError())
97
98        self.assertIn("Fix-it applied, fixed expression was:", ret_val.GetError())
99        self.assertIn("null_pointer->first", ret_val.GetError())
100
101    # The final function call runs into SIGILL on aarch64-linux.
102    @expectedFailureAll(archs=["aarch64"], oslist=["freebsd", "linux"],
103                        bugnumber="llvm.org/pr49407")
104    def test_with_multiple_retries(self):
105        """Test calling expressions with errors that can be fixed by the FixIts."""
106        self.build()
107        (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
108                                        'Stop here to evaluate expressions',
109                                         lldb.SBFileSpec("main.cpp"))
110
111        # Test repeatedly applying Fix-Its to expressions and reparsing them.
112        multiple_runs_options = lldb.SBExpressionOptions()
113        multiple_runs_options.SetAutoApplyFixIts(True)
114        multiple_runs_options.SetTopLevel(True)
115
116        frame = self.thread.GetFrameAtIndex(0)
117
118        # An expression that needs two parse attempts with one Fix-It each
119        # to be successfully parsed.
120        two_runs_expr = """
121        struct Data { int m; };
122
123        template<typename T>
124        struct S1 : public T {
125          using T::TypeDef;
126          int f() {
127            Data data;
128            data.m = 123;
129            // The first error as the using above requires a 'typename '.
130            // Will trigger a Fix-It that puts 'typename' in the right place.
131            typename S1<T>::TypeDef i = &data;
132            // i has the type "Data *", so this should be i.m.
133            // The second run will change the . to -> via the Fix-It.
134            return i.m;
135          }
136        };
137
138        struct ClassWithTypeDef {
139          typedef Data *TypeDef;
140        };
141
142        int test_X(int i) {
143          S1<ClassWithTypeDef> s1;
144          return s1.f();
145        }
146        """
147
148        # Disable retries which will fail.
149        multiple_runs_options.SetRetriesWithFixIts(0)
150        value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options)
151        errmsg = value.GetError().GetCString()
152        self.assertIn("expression failed to parse", errmsg)
153        self.assertIn("using declaration resolved to type without 'typename'",
154                      errmsg)
155        self.assertIn("fixed expression suggested:", errmsg)
156        self.assertIn("using typename T::TypeDef", errmsg)
157        # The second Fix-It shouldn't be suggested here as Clang should have
158        # aborted the parsing process.
159        self.assertNotIn("i->m", errmsg)
160
161        # Retry once, but the expression needs two retries.
162        multiple_runs_options.SetRetriesWithFixIts(1)
163        value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options)
164        errmsg = value.GetError().GetCString()
165        self.assertIn("expression failed to parse", errmsg)
166        self.assertIn("fixed expression suggested:", errmsg)
167        # Both our fixed expressions should be in the suggested expression.
168        self.assertIn("using typename T::TypeDef", errmsg)
169        self.assertIn("i->m", errmsg)
170
171        # Retry twice, which will get the expression working.
172        multiple_runs_options.SetRetriesWithFixIts(2)
173        value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options)
174        # This error signals success for top level expressions.
175        self.assertEquals(value.GetError().GetCString(), "error: No value")
176
177        # Test that the code above compiles to the right thing.
178        self.expect_expr("test_X(1)", result_type="int", result_value="123")
179