333 lines · cpp
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-exception6//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>("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>(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(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>("target ast");75 auto *target_ast = holder->GetAST();76 77 ClangASTImporter importer;78 CompilerType imported = importer.CopyType(*target_ast, 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(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 the101 // same name as the one in the other source.102 auto holder =103 std::make_unique<clang_utils::TypeSystemClangHolder>("ast");104 auto *fwd_decl_source = holder->GetAST();105 CompilerType fwd_decl_type = clang_utils::createRecord(106 *fwd_decl_source, 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>("target ast");111 auto *target = target_holder->GetAST();112 ClangASTImporter importer;113 CompilerType imported = importer.CopyType(*target, 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(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>("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>(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>("target ast");157 auto *target_ast = holder->GetAST();158 159 ClangASTImporter importer;160 CompilerType imported = importer.DeportType(*target_ast, 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(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>("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 when198 // importing one declaration into a temporary context and then to the199 // 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>("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>("target ast");217 auto *target_ast = holder->GetAST();218 clang::Decl *imported =219 importer.CopyDecl(&target_ast->getASTContext(), 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 even229 // 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>("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 and244 // 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 later254 // 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(source.record_decl, 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(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 has283 // an origin with an already existing LayoutInfo. We expect284 // the layout to be retrieved from the ClangASTImporter of the285 // 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(source.record_decl, layout_info);299 300 auto holder =301 std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");302 auto *target_ast = holder->GetAST();303 304 // Import the decl into a new TypeSystemClang.305 CompilerType imported = importer.CopyType(*target_ast, source.record_type);306 ASSERT_TRUE(imported.IsValid());307 308 auto *imported_decl = cast<CXXRecordDecl>(ClangUtil::GetAsTagDecl(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 Having320 // 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