1 | //===-- TestClangASTImporter.cpp ------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "gtest/gtest.h" |
10 | |
11 | #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" |
12 | #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" |
13 | #include "Plugins/ExpressionParser/Clang/ClangUtil.h" |
14 | #include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" |
15 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
16 | #include "TestingSupport/SubsystemRAII.h" |
17 | #include "TestingSupport/Symbol/ClangTestUtils.h" |
18 | #include "lldb/Core/Declaration.h" |
19 | #include "lldb/Host/FileSystem.h" |
20 | #include "lldb/Host/HostInfo.h" |
21 | #include "clang/AST/DeclCXX.h" |
22 | |
23 | using namespace clang; |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | |
27 | class TestClangASTImporter : public testing::Test { |
28 | public: |
29 | SubsystemRAII<FileSystem, HostInfo> subsystems; |
30 | }; |
31 | |
32 | TEST_F(TestClangASTImporter, CanImportInvalidType) { |
33 | ClangASTImporter importer; |
34 | EXPECT_FALSE(importer.CanImport(CompilerType())); |
35 | } |
36 | |
37 | TEST_F(TestClangASTImporter, ImportInvalidType) { |
38 | ClangASTImporter importer; |
39 | EXPECT_FALSE(importer.Import(CompilerType())); |
40 | } |
41 | |
42 | TEST_F(TestClangASTImporter, CopyDeclTagDecl) { |
43 | // Tests that the ClangASTImporter::CopyDecl can copy TagDecls. |
44 | clang_utils::SourceASTWithRecord source; |
45 | |
46 | auto holder = |
47 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
48 | auto *target_ast = holder->GetAST(); |
49 | |
50 | ClangASTImporter importer; |
51 | clang::Decl *imported = |
52 | importer.CopyDecl(&target_ast->getASTContext(), source.record_decl); |
53 | ASSERT_NE(nullptr, imported); |
54 | |
55 | // Check that we got the correct decl by just comparing their qualified name. |
56 | clang::TagDecl *imported_tag_decl = llvm::cast<clang::TagDecl>(Val: imported); |
57 | EXPECT_EQ(source.record_decl->getQualifiedNameAsString(), |
58 | imported_tag_decl->getQualifiedNameAsString()); |
59 | // We did a minimal import of the tag decl. |
60 | EXPECT_TRUE(imported_tag_decl->hasExternalLexicalStorage()); |
61 | |
62 | // Check that origin was set for the imported declaration. |
63 | ClangASTImporter::DeclOrigin origin = importer.GetDeclOrigin(decl: imported); |
64 | EXPECT_TRUE(origin.Valid()); |
65 | EXPECT_EQ(origin.ctx, &source.ast->getASTContext()); |
66 | EXPECT_EQ(origin.decl, source.record_decl); |
67 | } |
68 | |
69 | TEST_F(TestClangASTImporter, CopyTypeTagDecl) { |
70 | // Tests that the ClangASTImporter::CopyType can copy TagDecls types. |
71 | clang_utils::SourceASTWithRecord source; |
72 | |
73 | auto holder = |
74 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
75 | auto *target_ast = holder->GetAST(); |
76 | |
77 | ClangASTImporter importer; |
78 | CompilerType imported = importer.CopyType(dst&: *target_ast, src_type: source.record_type); |
79 | ASSERT_TRUE(imported.IsValid()); |
80 | |
81 | // Check that we got the correct decl by just comparing their qualified name. |
82 | clang::TagDecl *imported_tag_decl = ClangUtil::GetAsTagDecl(type: imported); |
83 | EXPECT_EQ(source.record_decl->getQualifiedNameAsString(), |
84 | imported_tag_decl->getQualifiedNameAsString()); |
85 | // We did a minimal import of the tag decl. |
86 | EXPECT_TRUE(imported_tag_decl->hasExternalLexicalStorage()); |
87 | |
88 | // Check that origin was set for the imported declaration. |
89 | ClangASTImporter::DeclOrigin origin = |
90 | importer.GetDeclOrigin(imported_tag_decl); |
91 | EXPECT_TRUE(origin.Valid()); |
92 | EXPECT_EQ(origin.ctx, &source.ast->getASTContext()); |
93 | EXPECT_EQ(origin.decl, source.record_decl); |
94 | } |
95 | |
96 | TEST_F(TestClangASTImporter, CompleteFwdDeclWithOtherOrigin) { |
97 | // Create an AST with a full type that is defined. |
98 | clang_utils::SourceASTWithRecord source_with_definition; |
99 | |
100 | // Create an AST with a type thst is only a forward declaration with the |
101 | // same name as the one in the other source. |
102 | auto holder = |
103 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "ast" ); |
104 | auto *fwd_decl_source = holder->GetAST(); |
105 | CompilerType fwd_decl_type = clang_utils::createRecord( |
106 | ast&: *fwd_decl_source, name: source_with_definition.record_decl->getName()); |
107 | |
108 | // Create a target and import the forward decl. |
109 | auto target_holder = |
110 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
111 | auto *target = target_holder->GetAST(); |
112 | ClangASTImporter importer; |
113 | CompilerType imported = importer.CopyType(dst&: *target, src_type: fwd_decl_type); |
114 | ASSERT_TRUE(imported.IsValid()); |
115 | EXPECT_FALSE(imported.IsDefined()); |
116 | |
117 | // Now complete the forward decl with the definition from the other source. |
118 | // This should define the decl and give it the fields of the other origin. |
119 | clang::TagDecl *imported_tag_decl = ClangUtil::GetAsTagDecl(type: imported); |
120 | importer.CompleteTagDeclWithOrigin(imported_tag_decl, |
121 | source_with_definition.record_decl); |
122 | ASSERT_TRUE(imported.IsValid()); |
123 | EXPECT_TRUE(imported.IsDefined()); |
124 | EXPECT_EQ(1U, imported.GetNumFields()); |
125 | } |
126 | |
127 | TEST_F(TestClangASTImporter, DeportDeclTagDecl) { |
128 | // Tests that the ClangASTImporter::DeportDecl completely copies TagDecls. |
129 | clang_utils::SourceASTWithRecord source; |
130 | |
131 | auto holder = |
132 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
133 | auto *target_ast = holder->GetAST(); |
134 | |
135 | ClangASTImporter importer; |
136 | clang::Decl *imported = |
137 | importer.DeportDecl(&target_ast->getASTContext(), source.record_decl); |
138 | ASSERT_NE(nullptr, imported); |
139 | |
140 | // Check that we got the correct decl by just comparing their qualified name. |
141 | clang::TagDecl *imported_tag_decl = llvm::cast<clang::TagDecl>(Val: imported); |
142 | EXPECT_EQ(source.record_decl->getQualifiedNameAsString(), |
143 | imported_tag_decl->getQualifiedNameAsString()); |
144 | // The record should be completed as we deported it. |
145 | EXPECT_FALSE(imported_tag_decl->hasExternalLexicalStorage()); |
146 | |
147 | // Deporting doesn't update the origin map. |
148 | EXPECT_FALSE(importer.GetDeclOrigin(imported_tag_decl).Valid()); |
149 | } |
150 | |
151 | TEST_F(TestClangASTImporter, DeportTypeTagDecl) { |
152 | // Tests that the ClangASTImporter::CopyType can deport TagDecl types. |
153 | clang_utils::SourceASTWithRecord source; |
154 | |
155 | auto holder = |
156 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
157 | auto *target_ast = holder->GetAST(); |
158 | |
159 | ClangASTImporter importer; |
160 | CompilerType imported = importer.DeportType(dst&: *target_ast, src_type: source.record_type); |
161 | ASSERT_TRUE(imported.IsValid()); |
162 | |
163 | // Check that we got the correct decl by just comparing their qualified name. |
164 | clang::TagDecl *imported_tag_decl = ClangUtil::GetAsTagDecl(type: imported); |
165 | EXPECT_EQ(source.record_decl->getQualifiedNameAsString(), |
166 | imported_tag_decl->getQualifiedNameAsString()); |
167 | // The record should be completed as we deported it. |
168 | EXPECT_FALSE(imported_tag_decl->hasExternalLexicalStorage()); |
169 | |
170 | // Deporting doesn't update the origin map. |
171 | EXPECT_FALSE(importer.GetDeclOrigin(imported_tag_decl).Valid()); |
172 | } |
173 | |
174 | TEST_F(TestClangASTImporter, MetadataPropagation) { |
175 | // Tests that AST metadata is propagated when copying declarations. |
176 | |
177 | clang_utils::SourceASTWithRecord source; |
178 | |
179 | const lldb::user_id_t metadata = 123456; |
180 | source.ast->SetMetadataAsUserID(source.record_decl, metadata); |
181 | |
182 | auto holder = |
183 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
184 | auto *target_ast = holder->GetAST(); |
185 | |
186 | ClangASTImporter importer; |
187 | clang::Decl *imported = |
188 | importer.CopyDecl(&target_ast->getASTContext(), source.record_decl); |
189 | ASSERT_NE(nullptr, imported); |
190 | |
191 | // Check that we got the same Metadata. |
192 | ASSERT_NE(std::nullopt, importer.GetDeclMetadata(imported)); |
193 | EXPECT_EQ(metadata, importer.GetDeclMetadata(imported)->GetUserID()); |
194 | } |
195 | |
196 | TEST_F(TestClangASTImporter, MetadataPropagationIndirectImport) { |
197 | // Tests that AST metadata is propagated when copying declarations when |
198 | // importing one declaration into a temporary context and then to the |
199 | // actual destination context. |
200 | |
201 | clang_utils::SourceASTWithRecord source; |
202 | |
203 | const lldb::user_id_t metadata = 123456; |
204 | source.ast->SetMetadataAsUserID(source.record_decl, metadata); |
205 | |
206 | auto tmp_holder = |
207 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "tmp ast" ); |
208 | auto *temporary_ast = tmp_holder->GetAST(); |
209 | |
210 | ClangASTImporter importer; |
211 | clang::Decl *temporary_imported = |
212 | importer.CopyDecl(&temporary_ast->getASTContext(), source.record_decl); |
213 | ASSERT_NE(nullptr, temporary_imported); |
214 | |
215 | auto holder = |
216 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
217 | auto *target_ast = holder->GetAST(); |
218 | clang::Decl *imported = |
219 | importer.CopyDecl(dst_ctx: &target_ast->getASTContext(), decl: temporary_imported); |
220 | ASSERT_NE(nullptr, imported); |
221 | |
222 | // Check that we got the same Metadata. |
223 | ASSERT_NE(std::nullopt, importer.GetDeclMetadata(imported)); |
224 | EXPECT_EQ(metadata, importer.GetDeclMetadata(imported)->GetUserID()); |
225 | } |
226 | |
227 | TEST_F(TestClangASTImporter, MetadataPropagationAfterCopying) { |
228 | // Tests that AST metadata is propagated when copying declarations even |
229 | // when the metadata was set after the declaration has already been copied. |
230 | |
231 | clang_utils::SourceASTWithRecord source; |
232 | const lldb::user_id_t metadata = 123456; |
233 | |
234 | auto holder = |
235 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
236 | auto *target_ast = holder->GetAST(); |
237 | |
238 | ClangASTImporter importer; |
239 | clang::Decl *imported = |
240 | importer.CopyDecl(&target_ast->getASTContext(), source.record_decl); |
241 | ASSERT_NE(nullptr, imported); |
242 | |
243 | // The TagDecl has been imported. Now set the metadata of the source and |
244 | // make sure the imported one will directly see it. |
245 | source.ast->SetMetadataAsUserID(source.record_decl, metadata); |
246 | |
247 | // Check that we got the same Metadata. |
248 | ASSERT_NE(std::nullopt, importer.GetDeclMetadata(imported)); |
249 | EXPECT_EQ(metadata, importer.GetDeclMetadata(imported)->GetUserID()); |
250 | } |
251 | |
252 | TEST_F(TestClangASTImporter, RecordLayout) { |
253 | // Test that it is possible to register RecordDecl layouts and then later |
254 | // correctly retrieve them. |
255 | |
256 | clang_utils::SourceASTWithRecord source; |
257 | |
258 | ClangASTImporter importer; |
259 | ClangASTImporter::LayoutInfo layout_info; |
260 | layout_info.bit_size = 15; |
261 | layout_info.alignment = 2; |
262 | layout_info.field_offsets[source.field_decl] = 1; |
263 | importer.SetRecordLayout(decl: source.record_decl, layout: layout_info); |
264 | |
265 | uint64_t bit_size; |
266 | uint64_t alignment; |
267 | llvm::DenseMap<const clang::FieldDecl *, uint64_t> field_offsets; |
268 | llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> base_offsets; |
269 | llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> vbase_offsets; |
270 | importer.LayoutRecordType(record_decl: source.record_decl, bit_size, alignment, |
271 | field_offsets, base_offsets, vbase_offsets); |
272 | |
273 | EXPECT_EQ(15U, bit_size); |
274 | EXPECT_EQ(2U, alignment); |
275 | EXPECT_EQ(1U, field_offsets.size()); |
276 | EXPECT_EQ(1U, field_offsets[source.field_decl]); |
277 | EXPECT_EQ(0U, base_offsets.size()); |
278 | EXPECT_EQ(0U, vbase_offsets.size()); |
279 | } |
280 | |
281 | TEST_F(TestClangASTImporter, RecordLayoutFromOrigin) { |
282 | // Tests that we can retrieve the layout of a record that has |
283 | // an origin with an already existing LayoutInfo. We expect |
284 | // the layout to be retrieved from the ClangASTImporter of the |
285 | // origin decl. |
286 | |
287 | clang_utils::SourceASTWithRecord source; |
288 | |
289 | auto *dwarf_parser = |
290 | static_cast<DWARFASTParserClang *>(source.ast->GetDWARFParser()); |
291 | auto &importer = dwarf_parser->GetClangASTImporter(); |
292 | |
293 | // Set the layout for the origin decl in the origin ClangASTImporter. |
294 | ClangASTImporter::LayoutInfo layout_info; |
295 | layout_info.bit_size = 32; |
296 | layout_info.alignment = 16; |
297 | layout_info.field_offsets[source.field_decl] = 1; |
298 | importer.SetRecordLayout(decl: source.record_decl, layout: layout_info); |
299 | |
300 | auto holder = |
301 | std::make_unique<clang_utils::TypeSystemClangHolder>(args: "target ast" ); |
302 | auto *target_ast = holder->GetAST(); |
303 | |
304 | // Import the decl into a new TypeSystemClang. |
305 | CompilerType imported = importer.CopyType(dst&: *target_ast, src_type: source.record_type); |
306 | ASSERT_TRUE(imported.IsValid()); |
307 | |
308 | auto *imported_decl = cast<CXXRecordDecl>(Val: ClangUtil::GetAsTagDecl(type: imported)); |
309 | ClangASTImporter::DeclOrigin origin = importer.GetDeclOrigin(imported_decl); |
310 | ASSERT_TRUE(origin.Valid()); |
311 | ASSERT_EQ(origin.decl, source.record_decl); |
312 | |
313 | uint64_t bit_size; |
314 | uint64_t alignment; |
315 | llvm::DenseMap<const clang::FieldDecl *, uint64_t> field_offsets; |
316 | llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> base_offsets; |
317 | llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> vbase_offsets; |
318 | |
319 | // Make sure we correctly read out the layout (despite not Having |
320 | // called SetRecordLayout on the new TypeSystem's ClangASTImporter). |
321 | auto success = |
322 | importer.LayoutRecordType(imported_decl, bit_size, alignment, |
323 | field_offsets, base_offsets, vbase_offsets); |
324 | EXPECT_TRUE(success); |
325 | |
326 | EXPECT_EQ(32U, bit_size); |
327 | EXPECT_EQ(16U, alignment); |
328 | EXPECT_EQ(1U, field_offsets.size()); |
329 | EXPECT_EQ(1U, field_offsets[*imported_decl->field_begin()]); |
330 | EXPECT_EQ(0U, base_offsets.size()); |
331 | EXPECT_EQ(0U, vbase_offsets.size()); |
332 | } |
333 | |