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