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

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