2 Test the diagnostics emitted by our embeded Clang instance that parses expressions.
6 from lldbsuite
.test
.lldbtest
import *
7 from lldbsuite
.test
import lldbutil
8 from lldbsuite
.test
.decorators
import *
11 class ExprDiagnosticsTestCase(TestBase
):
13 # Call super's setUp().
16 self
.main_source
= "main.cpp"
17 self
.main_source_spec
= lldb
.SBFileSpec(self
.main_source
)
19 def test_source_and_caret_printing(self
):
20 """Test that the source and caret positions LLDB prints are correct"""
23 (target
, process
, thread
, bkpt
) = lldbutil
.run_to_source_breakpoint(
24 self
, "// Break here", self
.main_source_spec
26 frame
= thread
.GetFrameAtIndex(0)
28 # Test that source/caret are at the right position.
29 value
= frame
.EvaluateExpression("unknown_identifier")
30 self
.assertFalse(value
.GetError().Success())
31 # We should get a nice diagnostic with a caret pointing at the start of
35 1 | unknown_identifier
38 value
.GetError().GetCString(),
40 self
.assertIn("<user expression 0>:1:1", value
.GetError().GetCString())
42 # Same as above but with the identifier in the middle.
43 value
= frame
.EvaluateExpression("1 + unknown_identifier")
44 self
.assertFalse(value
.GetError().Success())
47 1 | 1 + unknown_identifier
50 value
.GetError().GetCString(),
53 # Multiline expressions.
54 value
= frame
.EvaluateExpression("int a = 0;\nfoobar +=1;\na")
55 self
.assertFalse(value
.GetError().Success())
56 # We should still get the right line information and caret position.
62 value
.GetError().GetCString(),
65 # It's the second line of the user expression.
66 self
.assertIn("<user expression 2>:2:1", value
.GetError().GetCString())
68 # Top-level expressions.
69 top_level_opts
= lldb
.SBExpressionOptions()
70 top_level_opts
.SetTopLevel(True)
72 value
= frame
.EvaluateExpression("void foo(unknown_type x) {}", top_level_opts
)
73 self
.assertFalse(value
.GetError().Success())
76 1 | void foo(unknown_type x) {}
79 value
.GetError().GetCString(),
82 # Top-level expressions might use a different wrapper code, but the file name should still
84 self
.assertIn("<user expression 3>:1:10", value
.GetError().GetCString())
86 # Multiline top-level expressions.
87 value
= frame
.EvaluateExpression("void x() {}\nvoid foo;", top_level_opts
)
88 self
.assertFalse(value
.GetError().Success())
94 value
.GetError().GetCString(),
97 self
.assertIn("<user expression 4>:2:6", value
.GetError().GetCString())
99 # Test that we render Clang's 'notes' correctly.
100 value
= frame
.EvaluateExpression(
101 "struct SFoo{}; struct SFoo { int x; };", top_level_opts
103 self
.assertFalse(value
.GetError().Success())
105 "<user expression 5>:1:8: previous definition is here\n",
106 value
.GetError().GetCString(),
110 1 | struct SFoo{}; struct SFoo { int x; };
113 value
.GetError().GetCString(),
116 # Declarations from the debug information currently have no debug information. It's not clear what
117 # we should do in this case, but we should at least not print anything that's wrong.
118 # In the future our declarations should have valid source locations.
119 value
= frame
.EvaluateExpression("struct FooBar { double x };", top_level_opts
)
120 self
.assertFalse(value
.GetError().Success())
122 "error: <user expression 6>:1:8: redefinition of 'FooBar'\n",
123 value
.GetError().GetCString(),
127 1 | struct FooBar { double x };
130 value
.GetError().GetCString(),
133 value
= frame
.EvaluateExpression("foo(1, 2)")
134 self
.assertFalse(value
.GetError().Success())
136 "error: <user expression 7>:1:1: no matching function for call to 'foo'\n",
137 value
.GetError().GetCString(),
143 note: candidate function not viable: requires single argument 'x', but 2 arguments were provided
145 value
.GetError().GetCString(),
148 # Redefine something that we defined in a user-expression. We should use the previous expression file name
149 # for the original decl.
150 value
= frame
.EvaluateExpression("struct Redef { double x; };", top_level_opts
)
151 value
= frame
.EvaluateExpression("struct Redef { float y; };", top_level_opts
)
152 self
.assertFalse(value
.GetError().Success())
154 """error: <user expression 9>:1:8: redefinition of 'Redef'
155 1 | struct Redef { float y; };
157 <user expression 8>:1:8: previous definition is here
158 1 | struct Redef { double x; };
161 value
.GetError().GetCString(),
164 @add_test_categories(["objc"])
165 def test_source_locations_from_objc_modules(self
):
168 (target
, process
, thread
, bkpt
) = lldbutil
.run_to_source_breakpoint(
169 self
, "// Break here", self
.main_source_spec
171 frame
= thread
.GetFrameAtIndex(0)
173 # Import foundation so that the Obj-C module is loaded (which contains source locations
174 # that can be used by LLDB).
175 self
.runCmd("expr --language objective-c++ -- @import Foundation")
176 value
= frame
.EvaluateExpression("NSLog(1);")
177 self
.assertFalse(value
.GetError().Success())
178 # LLDB should print the source line that defines NSLog. To not rely on any
179 # header paths/line numbers or the actual formatting of the Foundation headers, only look
180 # for a few tokens in the output.
181 # File path should come from Foundation framework.
182 self
.assertIn("/Foundation.framework/", value
.GetError().GetCString())
183 # The NSLog definition source line should be printed. Return value and
184 # the first argument are probably stable enough that this test can check for them.
185 self
.assertIn("void NSLog(NSString *format", value
.GetError().GetCString())
187 def test_error_type(self
):
188 """Test the error reporting in the API"""
191 (target
, process
, thread
, bkpt
) = lldbutil
.run_to_source_breakpoint(
192 self
, "// Break here", self
.main_source_spec
194 frame
= thread
.GetFrameAtIndex(0)
195 value
= frame
.EvaluateExpression('#error("I am error.")')
196 error
= value
.GetError()
197 self
.assertEqual(error
.GetType(), lldb
.eErrorTypeExpression
)
198 value
= frame
.FindVariable("f")
199 self
.assertTrue(value
.IsValid())
200 desc
= value
.GetObjectDescription()
201 self
.assertEqual(desc
, None)
203 def test_command_expr_sbdata(self
):
204 """Test the structured diagnostics data"""
207 (target
, process
, thread
, bkpt
) = lldbutil
.run_to_source_breakpoint(
208 self
, "// Break here", self
.main_source_spec
211 def check_error(diags
):
213 version
= diags
.GetValueForKey("version")
214 self
.assertEqual(version
.GetIntegerValue(), 1)
216 details
= diags
.GetValueForKey("details")
218 # Detail 1/2: undeclared 'a'
219 diag
= details
.GetItemAtIndex(0)
221 severity
= diag
.GetValueForKey("severity")
222 message
= diag
.GetValueForKey("message")
223 rendered
= diag
.GetValueForKey("rendered")
224 sloc
= diag
.GetValueForKey("source_location")
225 filename
= sloc
.GetValueForKey("file")
226 hidden
= sloc
.GetValueForKey("hidden")
227 in_user_input
= sloc
.GetValueForKey("in_user_input")
229 self
.assertEqual(str(severity
), "error")
230 self
.assertIn("undeclared identifier 'a'", str(message
))
231 # The rendered string should contain the source file.
232 self
.assertIn("user expression", str(rendered
))
233 self
.assertIn("user expression", str(filename
))
234 self
.assertFalse(hidden
.GetBooleanValue())
235 self
.assertTrue(in_user_input
.GetBooleanValue())
237 # Detail 1/2: undeclared 'b'
238 diag
= details
.GetItemAtIndex(1)
239 message
= diag
.GetValueForKey("message")
240 self
.assertIn("undeclared identifier 'b'", str(message
))
242 # Test diagnostics in CommandReturnObject
243 interp
= self
.dbg
.GetCommandInterpreter()
244 cro
= lldb
.SBCommandReturnObject()
245 interp
.HandleCommand("expression -- a+b", cro
)
247 diags
= cro
.GetErrorData()
250 # Test diagnostics in SBError
251 frame
= thread
.GetSelectedFrame()
252 value
= frame
.EvaluateExpression("a+b")
253 error
= value
.GetError()
254 self
.assertTrue(error
.Fail())
255 self
.assertEqual(error
.GetType(), lldb
.eErrorTypeExpression
)
256 data
= error
.GetErrorData()
257 version
= data
.GetValueForKey("version")
258 self
.assertEqual(version
.GetIntegerValue(), 1)
259 err_ty
= data
.GetValueForKey("type")
260 self
.assertEqual(err_ty
.GetIntegerValue(), lldb
.eErrorTypeExpression
)
261 diags
= data
.GetValueForKey("errors").GetItemAtIndex(0)