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