#include "gtest/gtest.h"
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h"
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "TestingSupport/SubsystemRAII.h"
#include "TestingSupport/Symbol/ClangTestUtils.h"
#include "lldb/Core/Declaration.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
#include "clang/AST/DeclCXX.h"
using namespace clang;
using namespace lldb;
using namespace lldb_private;
class TestClangASTImporter : public testing::Test {
public:
SubsystemRAII<FileSystem, HostInfo> subsystems;
};
TEST_F(TestClangASTImporter, CanImportInvalidType) {
ClangASTImporter importer;
EXPECT_FALSE(importer.CanImport(CompilerType()));
}
TEST_F(TestClangASTImporter, ImportInvalidType) {
ClangASTImporter importer;
EXPECT_FALSE(importer.Import(CompilerType()));
}
TEST_F(TestClangASTImporter, CopyDeclTagDecl) {
clang_utils::SourceASTWithRecord source;
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target_ast = holder->GetAST();
ClangASTImporter importer;
clang::Decl *imported =
importer.CopyDecl(&target_ast->getASTContext(), source.record_decl);
ASSERT_NE(nullptr, imported);
clang::TagDecl *imported_tag_decl = llvm::cast<clang::TagDecl>(imported);
EXPECT_EQ(source.record_decl->getQualifiedNameAsString(),
imported_tag_decl->getQualifiedNameAsString());
EXPECT_TRUE(imported_tag_decl->hasExternalLexicalStorage());
ClangASTImporter::DeclOrigin origin = importer.GetDeclOrigin(imported);
EXPECT_TRUE(origin.Valid());
EXPECT_EQ(origin.ctx, &source.ast->getASTContext());
EXPECT_EQ(origin.decl, source.record_decl);
}
TEST_F(TestClangASTImporter, CopyTypeTagDecl) {
clang_utils::SourceASTWithRecord source;
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target_ast = holder->GetAST();
ClangASTImporter importer;
CompilerType imported = importer.CopyType(*target_ast, source.record_type);
ASSERT_TRUE(imported.IsValid());
clang::TagDecl *imported_tag_decl = ClangUtil::GetAsTagDecl(imported);
EXPECT_EQ(source.record_decl->getQualifiedNameAsString(),
imported_tag_decl->getQualifiedNameAsString());
EXPECT_TRUE(imported_tag_decl->hasExternalLexicalStorage());
ClangASTImporter::DeclOrigin origin =
importer.GetDeclOrigin(imported_tag_decl);
EXPECT_TRUE(origin.Valid());
EXPECT_EQ(origin.ctx, &source.ast->getASTContext());
EXPECT_EQ(origin.decl, source.record_decl);
}
TEST_F(TestClangASTImporter, CompleteFwdDeclWithOtherOrigin) {
clang_utils::SourceASTWithRecord source_with_definition;
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
auto *fwd_decl_source = holder->GetAST();
CompilerType fwd_decl_type = clang_utils::createRecord(
*fwd_decl_source, source_with_definition.record_decl->getName());
auto target_holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target = target_holder->GetAST();
ClangASTImporter importer;
CompilerType imported = importer.CopyType(*target, fwd_decl_type);
ASSERT_TRUE(imported.IsValid());
EXPECT_FALSE(imported.IsDefined());
clang::TagDecl *imported_tag_decl = ClangUtil::GetAsTagDecl(imported);
importer.CompleteTagDeclWithOrigin(imported_tag_decl,
source_with_definition.record_decl);
ASSERT_TRUE(imported.IsValid());
EXPECT_TRUE(imported.IsDefined());
EXPECT_EQ(1U, imported.GetNumFields());
}
TEST_F(TestClangASTImporter, DeportDeclTagDecl) {
clang_utils::SourceASTWithRecord source;
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target_ast = holder->GetAST();
ClangASTImporter importer;
clang::Decl *imported =
importer.DeportDecl(&target_ast->getASTContext(), source.record_decl);
ASSERT_NE(nullptr, imported);
clang::TagDecl *imported_tag_decl = llvm::cast<clang::TagDecl>(imported);
EXPECT_EQ(source.record_decl->getQualifiedNameAsString(),
imported_tag_decl->getQualifiedNameAsString());
EXPECT_FALSE(imported_tag_decl->hasExternalLexicalStorage());
EXPECT_FALSE(importer.GetDeclOrigin(imported_tag_decl).Valid());
}
TEST_F(TestClangASTImporter, DeportTypeTagDecl) {
clang_utils::SourceASTWithRecord source;
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target_ast = holder->GetAST();
ClangASTImporter importer;
CompilerType imported = importer.DeportType(*target_ast, source.record_type);
ASSERT_TRUE(imported.IsValid());
clang::TagDecl *imported_tag_decl = ClangUtil::GetAsTagDecl(imported);
EXPECT_EQ(source.record_decl->getQualifiedNameAsString(),
imported_tag_decl->getQualifiedNameAsString());
EXPECT_FALSE(imported_tag_decl->hasExternalLexicalStorage());
EXPECT_FALSE(importer.GetDeclOrigin(imported_tag_decl).Valid());
}
TEST_F(TestClangASTImporter, MetadataPropagation) {
clang_utils::SourceASTWithRecord source;
const lldb::user_id_t metadata = 123456;
source.ast->SetMetadataAsUserID(source.record_decl, metadata);
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target_ast = holder->GetAST();
ClangASTImporter importer;
clang::Decl *imported =
importer.CopyDecl(&target_ast->getASTContext(), source.record_decl);
ASSERT_NE(nullptr, imported);
ASSERT_NE(nullptr, importer.GetDeclMetadata(imported));
EXPECT_EQ(metadata, importer.GetDeclMetadata(imported)->GetUserID());
}
TEST_F(TestClangASTImporter, MetadataPropagationIndirectImport) {
clang_utils::SourceASTWithRecord source;
const lldb::user_id_t metadata = 123456;
source.ast->SetMetadataAsUserID(source.record_decl, metadata);
auto tmp_holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("tmp ast");
auto *temporary_ast = tmp_holder->GetAST();
ClangASTImporter importer;
clang::Decl *temporary_imported =
importer.CopyDecl(&temporary_ast->getASTContext(), source.record_decl);
ASSERT_NE(nullptr, temporary_imported);
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target_ast = holder->GetAST();
clang::Decl *imported =
importer.CopyDecl(&target_ast->getASTContext(), temporary_imported);
ASSERT_NE(nullptr, imported);
ASSERT_NE(nullptr, importer.GetDeclMetadata(imported));
EXPECT_EQ(metadata, importer.GetDeclMetadata(imported)->GetUserID());
}
TEST_F(TestClangASTImporter, MetadataPropagationAfterCopying) {
clang_utils::SourceASTWithRecord source;
const lldb::user_id_t metadata = 123456;
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("target ast");
auto *target_ast = holder->GetAST();
ClangASTImporter importer;
clang::Decl *imported =
importer.CopyDecl(&target_ast->getASTContext(), source.record_decl);
ASSERT_NE(nullptr, imported);
source.ast->SetMetadataAsUserID(source.record_decl, metadata);
ASSERT_NE(nullptr, importer.GetDeclMetadata(imported));
EXPECT_EQ(metadata, importer.GetDeclMetadata(imported)->GetUserID());
}
TEST_F(TestClangASTImporter, RecordLayout) {
clang_utils::SourceASTWithRecord source;
ClangASTImporter importer;
ClangASTImporter::LayoutInfo layout_info;
layout_info.bit_size = 15;
layout_info.alignment = 2;
layout_info.field_offsets[source.field_decl] = 1;
importer.SetRecordLayout(source.record_decl, layout_info);
uint64_t bit_size;
uint64_t alignment;
llvm::DenseMap<const clang::FieldDecl *, uint64_t> field_offsets;
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> base_offsets;
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> vbase_offsets;
importer.LayoutRecordType(source.record_decl, bit_size, alignment,
field_offsets, base_offsets, vbase_offsets);
EXPECT_EQ(15U, bit_size);
EXPECT_EQ(2U, alignment);
EXPECT_EQ(1U, field_offsets.size());
EXPECT_EQ(1U, field_offsets[source.field_decl]);
EXPECT_EQ(0U, base_offsets.size());
EXPECT_EQ(0U, vbase_offsets.size());
}