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