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
23using namespace clang;
24using namespace lldb;
25using namespace lldb_private;
26
27class TestClangASTImporter : public testing::Test {
28public:
29 SubsystemRAII<FileSystem, HostInfo> subsystems;
30};
31
32TEST_F(TestClangASTImporter, CanImportInvalidType) {
33 ClangASTImporter importer;
34 EXPECT_FALSE(importer.CanImport(CompilerType()));
35}
36
37TEST_F(TestClangASTImporter, ImportInvalidType) {
38 ClangASTImporter importer;
39 EXPECT_FALSE(importer.Import(CompilerType()));
40}
41
42TEST_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
69TEST_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
96TEST_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
127TEST_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
151TEST_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
174TEST_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
196TEST_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
227TEST_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
252TEST_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
281TEST_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

source code of lldb/unittests/Symbol/TestClangASTImporter.cpp