Skip to content

Commit 8eb01f6

Browse files
committed
[lldb][TypeSystemClang] Create FileIDs/SourceLocations from DWARF
1 parent 5d4cbd3 commit 8eb01f6

File tree

15 files changed

+317
-42
lines changed

15 files changed

+317
-42
lines changed

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "clang/AST/DeclBase.h"
1212
#include "clang/AST/ExprCXX.h"
1313
#include "llvm/Support/Casting.h"
14+
#include "llvm/Support/Error.h"
1415
#include "llvm/Support/FormatAdapters.h"
1516
#include "llvm/Support/FormatVariadic.h"
1617
#include "llvm/Support/SmallVectorMemoryBuffer.h"
@@ -9945,3 +9946,35 @@ void TypeSystemClang::LogCreation() const {
99459946
LLDB_LOG(log, "Created new TypeSystem for (ASTContext*){0:x} '{1}'",
99469947
&getASTContext(), getDisplayName());
99479948
}
9949+
9950+
clang::SourceLocation TypeSystemClang::GetLocForDecl(const Declaration &decl) {
9951+
// If the Declaration is invalid there is nothing to do.
9952+
if (!decl.IsValid())
9953+
return {};
9954+
9955+
clang::SourceManager &sm = getASTContext().getSourceManager();
9956+
clang::FileManager &fm = sm.getFileManager();
9957+
9958+
auto fe = fm.getFileRef(decl.GetFile().GetPath());
9959+
if (!fe) {
9960+
llvm::consumeError(fe.takeError());
9961+
return {};
9962+
}
9963+
9964+
clang::FileID fid = sm.translateFile(*fe);
9965+
if (fid.isInvalid()) {
9966+
// We see the file for the first time, so create a dummy file for it now.
9967+
9968+
// Connect the new dummy file to the main file via some fake include
9969+
// location. This is necessary as all file's in the SourceManager need to be
9970+
// reachable via an include chain from the main file.
9971+
SourceLocation ToIncludeLocOrFakeLoc;
9972+
assert(sm.getMainFileID().isValid());
9973+
ToIncludeLocOrFakeLoc = sm.getLocForStartOfFile(sm.getMainFileID());
9974+
fid = sm.createFileID(*fe, ToIncludeLocOrFakeLoc, clang::SrcMgr::C_User);
9975+
}
9976+
9977+
// Clang requires column numbers to be >= 1..
9978+
return sm.translateLineCol(fid, decl.GetLine(),
9979+
std::max<uint16_t>(decl.GetColumn(), 1));
9980+
}

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,15 @@ class TypeSystemClang : public TypeSystem {
11651165

11661166
bool SetDeclIsForcefullyCompleted(const clang::TagDecl *td);
11671167

1168+
/// Turns the given \c decl into a \c clang::SourceLocation.
1169+
///
1170+
/// Will create a \c FileID in this \c DWARFASTParserClang's \c ASTContext
1171+
/// if necessary.
1172+
///
1173+
/// If no \c FileID could be found/created, returns an empty \c
1174+
/// SourceLocation.
1175+
clang::SourceLocation GetLocForDecl(const lldb_private::Declaration &decl);
1176+
11681177
private:
11691178
/// Returns the PrintingPolicy used when generating the internal type names.
11701179
/// These type names are mostly used for the formatter selection.
@@ -1189,19 +1198,6 @@ class TypeSystemClang : public TypeSystem {
11891198
std::optional<uint64_t> GetObjCBitSize(clang::QualType qual_type,
11901199
ExecutionContextScope *exe_scope);
11911200

1192-
/// Turns the given \c decl into a \c clang::SourceLocation.
1193-
///
1194-
/// Will create a \c FileID in this \c DWARFASTParserClang's \c ASTContext
1195-
/// if necessary.
1196-
///
1197-
/// If no \c FileID could be found/created, returns an empty \c
1198-
/// SourceLocation.
1199-
///
1200-
/// FIXME: currently a no-op.
1201-
clang::SourceLocation GetLocForDecl(const lldb_private::Declaration &decl) {
1202-
return {};
1203-
}
1204-
12051201
// Classes that inherit from TypeSystemClang can see and modify these
12061202
std::string m_target_triple;
12071203
std::unique_ptr<clang::ASTContext> m_ast_up;

lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ def setUp(self):
1515

1616
self.main_source = "main.cpp"
1717
self.main_source_spec = lldb.SBFileSpec(self.main_source)
18+
self.main_source_diag_path = os.path.join("diagnostics", "main.cpp")
19+
self.header_source_diag_path = os.path.join("diagnostics", "lib.h")
1820

1921
def test_source_and_caret_printing(self):
2022
"""Test that the source and caret positions LLDB prints are correct"""
@@ -113,9 +115,6 @@ def test_source_and_caret_printing(self):
113115
value.GetError().GetCString(),
114116
)
115117

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.
119118
value = frame.EvaluateExpression("struct FooBar { double x };", top_level_opts)
120119
self.assertFalse(value.GetError().Success())
121120
self.assertIn(
@@ -140,10 +139,13 @@ def test_source_and_caret_printing(self):
140139
"""
141140
1 | foo(1, 2)
142141
| ^~~
143-
note: candidate function not viable: requires single argument 'x', but 2 arguments were provided
144142
""",
145143
value.GetError().GetCString(),
146144
)
145+
self.assertIn(
146+
"candidate function not viable: requires single argument 'x', but 2 arguments were provided",
147+
value.GetError().GetCString(),
148+
)
147149

148150
# Redefine something that we defined in a user-expression. We should use the previous expression file name
149151
# for the original decl.
@@ -260,3 +262,116 @@ def check_error(diags):
260262
self.assertEqual(err_ty.GetIntegerValue(), lldb.eErrorTypeExpression)
261263
diags = data.GetValueForKey("errors").GetItemAtIndex(0)
262264
check_error(diags)
265+
266+
def test_source_locations_from_debug_information(self):
267+
"""Test that the source locations from debug information are correct"""
268+
self.build()
269+
270+
(_, _, thread, _) = lldbutil.run_to_source_breakpoint(
271+
self, "// Break here", self.main_source_spec
272+
)
273+
274+
frame = thread.GetFrameAtIndex(0)
275+
276+
# Test source locations of functions
277+
value = frame.EvaluateExpression("foo(1, 2)")
278+
self.assertFalse(value.GetError().Success())
279+
error_msg = value.GetError().GetCString()
280+
self.assertIn(
281+
""":1:1: no matching function for call to 'foo'
282+
1 | foo(1, 2)
283+
| ^~~
284+
""",
285+
error_msg,
286+
)
287+
288+
self.assertIn(
289+
"""
290+
1 | void foo() {}
291+
| ^
292+
""",
293+
error_msg,
294+
)
295+
296+
self.assertIn(
297+
"""
298+
2 | void foo(char, char const *x) {}
299+
| ^
300+
""",
301+
error_msg,
302+
)
303+
304+
self.assertIn(
305+
"""
306+
3 | void foo(int x) {}
307+
| ^
308+
""",
309+
error_msg,
310+
)
311+
312+
self.assertIn(f"{self.header_source_diag_path}:", error_msg)
313+
self.assertIn(f"{self.main_source_diag_path}:", error_msg)
314+
315+
top_level_opts = lldb.SBExpressionOptions()
316+
top_level_opts.SetTopLevel(True)
317+
318+
# Test source locations of records
319+
value = frame.EvaluateExpression("struct FooBar { double x; };", top_level_opts)
320+
self.assertFalse(value.GetError().Success())
321+
error_msg = value.GetError().GetCString()
322+
self.assertIn(
323+
""":1:8: redefinition of 'FooBar'
324+
1 | struct FooBar { double x; };
325+
""",
326+
error_msg,
327+
)
328+
self.assertIn(
329+
"""previous definition is here
330+
5 | struct FooBar {
331+
| ^
332+
""",
333+
error_msg,
334+
)
335+
self.assertIn(f"{self.main_source_diag_path}:", error_msg)
336+
337+
# Test source locations of enums
338+
value = frame.EvaluateExpression("enum class EnumInSource {};", top_level_opts)
339+
self.assertFalse(value.GetError().Success())
340+
error_msg = value.GetError().GetCString()
341+
self.assertIn(
342+
"""previous declaration is here
343+
9 | enum class EnumInSource { A, B, C };
344+
| ^
345+
""",
346+
error_msg,
347+
)
348+
self.assertIn(f"{self.main_source_diag_path}:", error_msg)
349+
350+
@skipIf(
351+
debug_info=no_match("dsym"),
352+
bugnumber="Template function decl can only be found via dsym",
353+
)
354+
def test_source_locations_from_debug_information_template_func(self):
355+
"""Test that the source locations from debug information are correct
356+
for template functions"""
357+
self.build()
358+
359+
(_, _, thread, _) = lldbutil.run_to_source_breakpoint(
360+
self, "// Break here", self.main_source_spec
361+
)
362+
363+
frame = thread.GetFrameAtIndex(0)
364+
365+
# Test source locations of template functions
366+
value = frame.EvaluateExpression("templateFunc<int>(1)")
367+
self.assertFalse(value.GetError().Success())
368+
error_msg = value.GetError().GetCString()
369+
self.assertIn(
370+
"""11:1: non-template declaration found by name lookup
371+
11 | template <typename T> void templateFunc() {}
372+
| ^
373+
""",
374+
error_msg,
375+
)
376+
377+
self.assertIn(f"{self.main_source_diag_path}:", error_msg)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
void foo() {}
2+
void foo(char, char const *x) {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
#include "lib.h"
2+
13
void foo(int x) {}
24

35
struct FooBar {
46
int i;
57
};
68

9+
enum class EnumInSource { A, B, C };
10+
11+
template <typename T> void templateFunc() {}
12+
713
int main() {
814
FooBar f;
915
foo(1);
16+
foo('c', "abc");
17+
foo();
18+
EnumInSource e = EnumInSource::A;
19+
templateFunc<int>();
20+
1021
return 0; // Break here
1122
}

lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010

1111

1212
class ExprInsideLambdaTestCase(TestBase):
13-
def expectExprError(self, expr: str, expected: str):
13+
def expectExprError(self, expr: str, expected_errors: str):
1414
frame = self.thread.GetFrameAtIndex(0)
1515
value = frame.EvaluateExpression(expr)
1616
errmsg = value.GetError().GetCString()
17-
self.assertIn(expected, errmsg)
17+
for expected in expected_errors:
18+
self.assertIn(expected, errmsg)
1819

1920
def test_expr_inside_lambda(self):
2021
"""Test that lldb evaluating expressions inside lambda expressions works correctly."""
@@ -76,7 +77,7 @@ def test_expr_inside_lambda(self):
7677
self.expect_expr("local_var", result_type="int", result_value="137")
7778

7879
# Check access to variable in previous frame which we didn't capture
79-
self.expectExprError("local_var_copy", "use of undeclared identifier")
80+
self.expectExprError("local_var_copy", ["use of undeclared identifier"])
8081

8182
lldbutil.continue_to_breakpoint(process, bkpt)
8283

@@ -110,19 +111,17 @@ def test_expr_inside_lambda(self):
110111
# Check access to outer top-level structure's members
111112
self.expectExprError(
112113
"class_var",
113-
("use of non-static data member" " 'class_var' of 'Foo' from nested type"),
114+
["use of non-static data member 'class_var' of 'Foo' from nested type"],
114115
)
115116

116-
self.expectExprError(
117-
"base_var", ("use of non-static data member" " 'base_var'")
118-
)
117+
self.expectExprError("base_var", ["use of non-static data member 'base_var'"])
119118

120119
self.expectExprError(
121120
"local_var",
122-
(
123-
"use of non-static data member 'local_var'"
124-
" of '(unnamed class)' from nested type 'LocalLambdaClass'"
125-
),
121+
[
122+
"use of non-static data member 'local_var' of '(unnamed class at",
123+
"from nested type 'LocalLambdaClass'",
124+
],
126125
)
127126

128127
# Inside non_capturing_method
@@ -133,5 +132,5 @@ def test_expr_inside_lambda(self):
133132

134133
self.expectExprError(
135134
"class_var",
136-
("use of non-static data member" " 'class_var' of 'Foo' from nested type"),
135+
["use of non-static data member 'class_var' of 'Foo' from nested type"],
137136
)

lldb/test/API/lang/cpp/function-local-class/TestCppFunctionLocalClass.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,8 @@ def test(self):
3434
result_type="TypedefUnnamed2",
3535
result_children=[ValueCheck(name="b", value="3")],
3636
)
37-
self.expect_expr(
38-
"unnamed",
39-
result_type="(unnamed struct)",
40-
result_children=[ValueCheck(name="i", value="4")],
41-
)
42-
self.expect_expr(
43-
"unnamed2",
44-
result_type="(unnamed struct)",
45-
result_children=[ValueCheck(name="j", value="5")],
46-
)
37+
self.expect("expression unnamed", substrs=["(unnamed struct at", "i = 4"])
38+
self.expect("expression unnamed2", substrs=["(unnamed struct at", "j = 5"])
4739

4840
# Try a class that is only forward declared.
4941
self.expect_expr("fwd", result_type="Forward *")

lldb/test/Shell/Settings/TestFrameFormatName.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ run
1616
c
1717
# NAME_WITH_ARGS: frame int ns::foo<int ()>(str="bar")
1818
c
19-
# NAME_WITH_ARGS: frame int ns::foo<(anonymous namespace)::$_0>(t=(anonymous namespace)::(unnamed class) @ {{.*}})
19+
# NAME_WITH_ARGS: frame int ns::foo<(anonymous namespace)::$_0>(t=(anonymous namespace)::(unnamed class at {{.*}}) @ {{.*}})
2020
c
2121
# NAME_WITH_ARGS: frame int ns::foo<int (*)()>(t=({{.*}}`(anonymous namespace)::anon_bar() at {{.*}}))
2222
c

lldb/test/Shell/SymbolFile/DWARF/clang-ast-from-dwarf-unamed-and-anon-structs.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct A {
1515
} C;
1616
} a;
1717

18-
// CHECK: A::(anonymous struct)
18+
// CHECK: A::(anonymous struct at
1919
// CHECK: |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
20-
// CHECK: A::(unnamed struct)
20+
// CHECK: A::(unnamed struct at
2121
// CHECK: |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal

lldb/unittests/Symbol/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,8 @@ add_lldb_unittest(SymbolTests
2727

2828
set(test_inputs
2929
inlined-functions.yaml
30+
dummy.cpp
31+
dummy_header1.h
32+
dummy_header2.h
3033
)
3134
add_unittest_inputs(SymbolTests "${test_inputs}")

0 commit comments

Comments
 (0)