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