1"""
2Test the diagnostics emitted by our embeded Clang instance that parses expressions.
3"""
4
5import lldb
6from lldbsuite.test.lldbtest import *
7from lldbsuite.test import lldbutil
8from lldbsuite.test.decorators import *
9
10class ExprDiagnosticsTestCase(TestBase):
11
12    mydir = TestBase.compute_mydir(__file__)
13
14    def setUp(self):
15        # Call super's setUp().
16        TestBase.setUp(self)
17
18        self.main_source = "main.cpp"
19        self.main_source_spec = lldb.SBFileSpec(self.main_source)
20
21    def test_source_and_caret_printing(self):
22        """Test that the source and caret positions LLDB prints are correct"""
23        self.build()
24
25        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
26                                          '// Break here', self.main_source_spec)
27        frame = thread.GetFrameAtIndex(0)
28
29        # Test that source/caret are at the right position.
30        value = frame.EvaluateExpression("unknown_identifier")
31        self.assertFalse(value.GetError().Success())
32        # We should get a nice diagnostic with a caret pointing at the start of
33        # the identifier.
34        self.assertIn("\nunknown_identifier\n^\n", value.GetError().GetCString())
35        self.assertIn("<user expression 0>:1:1", value.GetError().GetCString())
36
37        # Same as above but with the identifier in the middle.
38        value = frame.EvaluateExpression("1 + unknown_identifier  ")
39        self.assertFalse(value.GetError().Success())
40        self.assertIn("\n1 + unknown_identifier", value.GetError().GetCString())
41        self.assertIn("\n    ^\n", value.GetError().GetCString())
42
43        # Multiline expressions.
44        value = frame.EvaluateExpression("int a = 0;\nfoobar +=1;\na")
45        self.assertFalse(value.GetError().Success())
46        # We should still get the right line information and caret position.
47        self.assertIn("\nfoobar +=1;\n^\n", value.GetError().GetCString())
48        # It's the second line of the user expression.
49        self.assertIn("<user expression 2>:2:1", value.GetError().GetCString())
50
51        # Top-level expressions.
52        top_level_opts = lldb.SBExpressionOptions();
53        top_level_opts.SetTopLevel(True)
54
55        value = frame.EvaluateExpression("void foo(unknown_type x) {}", top_level_opts)
56        self.assertFalse(value.GetError().Success())
57        self.assertIn("\nvoid foo(unknown_type x) {}\n         ^\n", value.GetError().GetCString())
58        # Top-level expressions might use a different wrapper code, but the file name should still
59        # be the same.
60        self.assertIn("<user expression 3>:1:10", value.GetError().GetCString())
61
62        # Multiline top-level expressions.
63        value = frame.EvaluateExpression("void x() {}\nvoid foo;", top_level_opts)
64        self.assertFalse(value.GetError().Success())
65        self.assertIn("\nvoid foo;\n     ^", value.GetError().GetCString())
66        self.assertIn("<user expression 4>:2:6", value.GetError().GetCString())
67
68        # Test that we render Clang's 'notes' correctly.
69        value = frame.EvaluateExpression("struct SFoo{}; struct SFoo { int x; };", top_level_opts)
70        self.assertFalse(value.GetError().Success())
71        self.assertIn("<user expression 5>:1:8: previous definition is here\nstruct SFoo{}; struct SFoo { int x; };\n       ^\n", value.GetError().GetCString())
72
73        # Declarations from the debug information currently have no debug information. It's not clear what
74        # we should do in this case, but we should at least not print anything that's wrong.
75        # In the future our declarations should have valid source locations.
76        value = frame.EvaluateExpression("struct FooBar { double x };", top_level_opts)
77        self.assertFalse(value.GetError().Success())
78        self.assertEqual("error: <user expression 6>:1:8: redefinition of 'FooBar'\nstruct FooBar { double x };\n       ^\n", value.GetError().GetCString())
79
80        value = frame.EvaluateExpression("foo(1, 2)")
81        self.assertFalse(value.GetError().Success())
82        self.assertEqual("error: <user expression 7>:1:1: no matching function for call to 'foo'\nfoo(1, 2)\n^~~\nnote: candidate function not viable: requires single argument 'x', but 2 arguments were provided\n\n", value.GetError().GetCString())
83
84        # Redefine something that we defined in a user-expression. We should use the previous expression file name
85        # for the original decl.
86        value = frame.EvaluateExpression("struct Redef { double x; };", top_level_opts)
87        value = frame.EvaluateExpression("struct Redef { float y; };", top_level_opts)
88        self.assertFalse(value.GetError().Success())
89        self.assertIn("error: <user expression 9>:1:8: redefinition of 'Redef'\nstruct Redef { float y; };\n       ^\n<user expression 8>:1:8: previous definition is here\nstruct Redef { double x; };\n       ^", value.GetError().GetCString())
90
91    @add_test_categories(["objc"])
92    def test_source_locations_from_objc_modules(self):
93        self.build()
94
95        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
96                                          '// Break here', self.main_source_spec)
97        frame = thread.GetFrameAtIndex(0)
98
99        # Import foundation so that the Obj-C module is loaded (which contains source locations
100        # that can be used by LLDB).
101        self.runCmd("expr @import Foundation")
102        value = frame.EvaluateExpression("NSLog(1);")
103        self.assertFalse(value.GetError().Success())
104        # LLDB should print the source line that defines NSLog. To not rely on any
105        # header paths/line numbers or the actual formatting of the Foundation headers, only look
106        # for a few tokens in the output.
107        # File path should come from Foundation framework.
108        self.assertIn("/Foundation.framework/", value.GetError().GetCString())
109        # The NSLog definition source line should be printed. Return value and
110        # the first argument are probably stable enough that this test can check for them.
111        self.assertIn("void NSLog(NSString *format", value.GetError().GetCString())
112
113