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 | |
22 | using namespace clang; |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | |
26 | class TestClangASTImporter : public testing::Test { |
27 | public: |
28 | SubsystemRAII<FileSystem, HostInfo> subsystems; |
29 | }; |
30 | |
31 | TEST_F(TestClangASTImporter, CanImportInvalidType) { |
32 | ClangASTImporter importer; |
33 | EXPECT_FALSE(importer.CanImport(CompilerType())); |
34 | } |
35 | |
36 | TEST_F(TestClangASTImporter, ImportInvalidType) { |
37 | ClangASTImporter importer; |
38 | EXPECT_FALSE(importer.Import(CompilerType())); |
39 | } |
40 | |
41 | TEST_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 | |
68 | TEST_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 | |
95 | TEST_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 | |
126 | TEST_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 | |
150 | TEST_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 | |
173 | TEST_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 | |
195 | TEST_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 | |
226 | TEST_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 | |
251 | TEST_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 | |