Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h +++ lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h @@ -36,6 +36,15 @@ static clang::TagDecl *GetAsTagDecl(const CompilerType &type); + /// If the given Decl is redeclarable, return the first declaration in its + /// redeclaration chain. Otherwise return d itself. + static clang::Decl *GetFirstDecl(clang::Decl *d); + + /// \see ClangUtil::GetFirstDecl + static const clang::Decl *GetFirstDecl(const clang::Decl *d) { + return GetFirstDecl(const_cast(d)); + } + /// Returns a textual representation of the given Decl's AST. Does not /// deserialize any child nodes. static std::string DumpDecl(const clang::Decl *d); Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp @@ -10,6 +10,7 @@ #include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "clang/AST/DeclObjC.h" using namespace clang; using namespace lldb_private; @@ -65,6 +66,16 @@ return qual_type->getAsTagDecl(); } +clang::Decl *ClangUtil::GetFirstDecl(clang::Decl *d) { + if (!d) + return nullptr; + if (auto *td = llvm::dyn_cast(d)) + return td->getFirstDecl(); + if (auto *od = llvm::dyn_cast(d)) + return od->getFirstDecl(); + return d; +} + std::string ClangUtil::DumpDecl(const clang::Decl *d) { if (!d) return "nullptr"; Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -243,7 +243,7 @@ CompilerType GetTypeForDecl(clang::TagDecl *decl); - CompilerType GetTypeForDecl(clang::ObjCInterfaceDecl *objc_decl); + CompilerType GetTypeForDecl(const clang::ObjCInterfaceDecl *objc_decl); template CompilerType @@ -313,10 +313,34 @@ llvm::StringRef name, int kind, lldb::LanguageType language, ClangASTMetadata *metadata = nullptr, - bool exports_symbols = false); + bool exports_symbols = false) { + clang::NamedDecl *d = + CreateRecordDecl(decl_ctx, owning_module, access_type, name, kind, + language, metadata, exports_symbols); + return GetTypeForDecl(d); + } + + clang::NamedDecl *CreateRecordDecl(clang::DeclContext *decl_ctx, + OptionalClangModuleID owning_module, + lldb::AccessType access_type, + llvm::StringRef name, int kind, + lldb::LanguageType language, + ClangASTMetadata *metadata = nullptr, + bool exports_symbols = false); class TemplateParameterInfos { public: + TemplateParameterInfos() = default; + TemplateParameterInfos(const TemplateParameterInfos &o) { *this = o; } + TemplateParameterInfos &operator=(const TemplateParameterInfos &o) { + names = o.names; + args = o.args; + pack_name = o.pack_name; + if (o.packed_args) + packed_args = std::make_unique(*o.packed_args); + return *this; + } + bool IsValid() const { // Having a pack name but no packed args doesn't make sense, so mark // these template parameters as invalid. @@ -330,8 +354,8 @@ llvm::SmallVector names; llvm::SmallVector args; - - const char * pack_name = nullptr; + + llvm::Optional pack_name; std::unique_ptr packed_args; }; @@ -375,7 +399,16 @@ clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, bool isForwardDecl, bool isInternal, - ClangASTMetadata *metadata = nullptr); + ClangASTMetadata *metadata = nullptr) { + clang::ObjCInterfaceDecl *d = CreateObjCDecl( + name, decl_ctx, owning_module, isForwardDecl, isInternal, metadata); + return GetTypeForDecl(d); + } + + clang::ObjCInterfaceDecl * + CreateObjCDecl(llvm::StringRef name, clang::DeclContext *decl_ctx, + OptionalClangModuleID owning_module, bool isForwardDecl, + bool isInternal, ClangASTMetadata *metadata = nullptr); // Returns a mask containing bits from the TypeSystemClang::eTypeXXX // enumerations @@ -416,12 +449,23 @@ size_t element_count, bool is_vector); // Enumeration Types + clang::EnumDecl *CreateEnumerationDecl(llvm::StringRef name, + clang::DeclContext *decl_ctx, + OptionalClangModuleID owning_module, + const Declaration &decl, + const CompilerType &integer_qual_type, + bool is_scoped); + CompilerType CreateEnumerationType(llvm::StringRef name, clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, const Declaration &decl, const CompilerType &integer_qual_type, - bool is_scoped); + bool is_scoped) { + clang::EnumDecl *enum_decl = CreateEnumerationDecl( + name, decl_ctx, owning_module, decl, integer_qual_type, is_scoped); + return GetType(getASTContext().getTagDeclType(enum_decl)); + } // Integer type functions @@ -443,6 +487,15 @@ void CompleteObjCInterfaceDecl(clang::ObjCInterfaceDecl *); + /// Creates a redeclaration for the declaration specified by the given type. + /// The redeclaration will be at the end of the redeclaration chain. The + /// passed declaration has to be created via a TypeSystemClang interface. + /// + /// \param type The type which declaration should be redeclared. Has to be + /// an Objective-C interface type (or Objective-C type), RecordType or + /// EnumType. + void CreateRedeclaration(CompilerType ct); + bool LayoutRecordType( const clang::RecordDecl *record_decl, uint64_t &size, uint64_t &alignment, llvm::DenseMap &field_offsets, @@ -1080,6 +1133,18 @@ /// Maps Types to their associated ClangASTMetadata. TypeMetadataMap m_type_metadata; + /// The information we need to redeclare a class template but that we can't + /// gather from the forward declaration. + struct ClassTemplateRedeclInfo { + TemplateParameterInfos m_template_args; + }; + typedef llvm::DenseMap + ClassTemplateRedeclInfoMap; + // FIXME: This is in theory redundant. Instead we should change the way we + // create ClassTemplateSpecializationDecls in TypeSystemClang so that we can + // just pass the data from the forward declaration. + ClassTemplateRedeclInfoMap m_class_template_redecl_infos; + /// The sema associated that is currently used to build this ASTContext. /// May be null if we are already done parsing this ASTContext or the /// ASTContext wasn't created by parsing source code. Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -1213,11 +1213,18 @@ } CompilerType TypeSystemClang::GetTypeForDecl(TagDecl *decl) { - return GetType(getASTContext().getTagDeclType(decl)); + // Create the type for the TagDecl. Pass the previous decl to make sure that + // all redeclarations share the same type. + return GetType( + getASTContext().getTypeDeclType(decl, decl->getPreviousDecl())); } -CompilerType TypeSystemClang::GetTypeForDecl(ObjCInterfaceDecl *decl) { - return GetType(getASTContext().getObjCInterfaceType(decl)); +CompilerType TypeSystemClang::GetTypeForDecl(const ObjCInterfaceDecl *decl) { + // FIXME: getObjCInterfaceType second parameter could be const. + // Create the type for the ObjCInterfaceDecl. Pass the previous decl to make + // sure that all redeclarations share the same type. + return GetType(getASTContext().getObjCInterfaceType( + decl, const_cast(decl->getPreviousDecl()))); } #pragma mark Structure, Unions, Classes @@ -1267,7 +1274,7 @@ return ast_source->RegisterModule(module); } -CompilerType TypeSystemClang::CreateRecordType( +clang::NamedDecl *TypeSystemClang::CreateRecordDecl( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, AccessType access_type, llvm::StringRef name, int kind, LanguageType language, ClangASTMetadata *metadata, bool exports_symbols) { @@ -1275,13 +1282,14 @@ if (decl_ctx == nullptr) decl_ctx = ast.getTranslationUnitDecl(); + decl_ctx = decl_ctx->getPrimaryContext(); if (language == eLanguageTypeObjC || language == eLanguageTypeObjC_plus_plus) { bool isForwardDecl = true; bool isInternal = false; - return CreateObjCClass(name, decl_ctx, owning_module, isForwardDecl, - isInternal, metadata); + return CreateObjCDecl(name, decl_ctx, owning_module, isForwardDecl, + isInternal, metadata); } // NOTE: Eventually CXXRecordDecl will be merged back into RecordDecl and @@ -1336,7 +1344,7 @@ if (decl_ctx) decl_ctx->addDecl(decl); - return GetType(ast.getTagDeclType(decl)); + return decl; } namespace { @@ -1379,8 +1387,10 @@ if (template_param_infos.packed_args) { IdentifierInfo *identifier_info = nullptr; - if (template_param_infos.pack_name && template_param_infos.pack_name[0]) - identifier_info = &ast.Idents.get(template_param_infos.pack_name); + if (template_param_infos.pack_name && + !template_param_infos.pack_name->empty()) + identifier_info = + &ast.Idents.get(template_param_infos.pack_name->c_str()); const bool parameter_pack_true = true; if (!template_param_infos.packed_args->args.empty() && @@ -1550,6 +1560,7 @@ ClassTemplateDecl *class_template_decl = nullptr; if (decl_ctx == nullptr) decl_ctx = ast.getTranslationUnitDecl(); + decl_ctx = decl_ctx->getPrimaryContext(); IdentifierInfo &identifier_info = ast.Idents.get(class_name); DeclarationName decl_name(&identifier_info); @@ -1641,6 +1652,8 @@ DeclContext *decl_ctx, OptionalClangModuleID owning_module, ClassTemplateDecl *class_template_decl, int kind, const TemplateParameterInfos &template_param_infos) { + decl_ctx = decl_ctx->getPrimaryContext(); + ASTContext &ast = getASTContext(); llvm::SmallVector args( template_param_infos.args.size() + @@ -1659,7 +1672,6 @@ class_template_specialization_decl->setInstantiationOf(class_template_decl); class_template_specialization_decl->setTemplateArgs( TemplateArgumentList::CreateCopy(ast, args)); - ast.getTypeDeclType(class_template_specialization_decl, nullptr); class_template_specialization_decl->setDeclName( class_template_decl->getDeclName()); SetOwningModule(class_template_specialization_decl, owning_module); @@ -1668,6 +1680,13 @@ class_template_specialization_decl->setSpecializationKind( TSK_ExplicitSpecialization); + // Store the information that is needed to later redeclare this exact + // template specialization. + ClassTemplateRedeclInfo redecl_info; + redecl_info.m_template_args = template_param_infos; + m_class_template_redecl_infos[class_template_specialization_decl] = + redecl_info; + return class_template_specialization_decl; } @@ -1782,7 +1801,7 @@ #pragma mark Objective-C Classes -CompilerType TypeSystemClang::CreateObjCClass( +clang::ObjCInterfaceDecl *TypeSystemClang::CreateObjCDecl( llvm::StringRef name, clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, bool isForwardDecl, bool isInternal, ClangASTMetadata *metadata) { @@ -1801,7 +1820,7 @@ if (metadata) SetMetadata(decl, *metadata); - return GetType(ast.getObjCInterfaceType(decl)); + return decl; } static inline bool BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) { @@ -2281,7 +2300,7 @@ #pragma mark Enumeration Types -CompilerType TypeSystemClang::CreateEnumerationType( +clang::EnumDecl *TypeSystemClang::CreateEnumerationDecl( llvm::StringRef name, clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, const Declaration &decl, const CompilerType &integer_clang_type, bool is_scoped) { @@ -2307,7 +2326,7 @@ enum_decl->setAccess(AS_public); // TODO respect what's in the debug info - return GetType(ast.getTagDeclType(enum_decl)); + return enum_decl; } CompilerType TypeSystemClang::GetIntTypeFromBitSize(size_t bit_size, @@ -2508,7 +2527,10 @@ ast_source->CompleteType(objc_interface_decl); - return !objc_interface_decl->getTypeForDecl()->isIncompleteType(); + objc_interface_decl = objc_interface_decl->getDefinition(); + + return objc_interface_decl && + !objc_interface_decl->getTypeForDecl()->isIncompleteType(); } else { return false; } @@ -2528,9 +2550,17 @@ SetMetadata(type, meta_data); } +/// Returns the Decl in a redeclaration chain that is used to store the +/// the ClangASTMetadata in the metadata map. +static const clang::Decl *GetDeclForMetadataStorage(const clang::Decl *d) { + // Only the first Decl never changes and never requires any loading from + // the ExternalASTSource, so it can be a stable key for the map. + return ClangUtil::GetFirstDecl(d); +} + void TypeSystemClang::SetMetadata(const clang::Decl *object, ClangASTMetadata &metadata) { - m_decl_metadata[object] = metadata; + m_decl_metadata[GetDeclForMetadataStorage(object)] = metadata; } void TypeSystemClang::SetMetadata(const clang::Type *object, @@ -2539,7 +2569,7 @@ } ClangASTMetadata *TypeSystemClang::GetMetadata(const clang::Decl *object) { - auto It = m_decl_metadata.find(object); + auto It = m_decl_metadata.find(GetDeclForMetadataStorage(object)); if (It != m_decl_metadata.end()) return &It->second; return nullptr; @@ -4569,6 +4599,7 @@ TypeSystemClang::DeclContextGetAsDeclContext(compiler_decl_ctx); if (!decl_ctx) decl_ctx = getASTContext().getTranslationUnitDecl(); + decl_ctx = decl_ctx->getPrimaryContext(); clang::TypedefDecl *decl = clang::TypedefDecl::CreateDeserialized(clang_ast, 0); @@ -5506,6 +5537,9 @@ if (enum_type) { const clang::EnumDecl *enum_decl = enum_type->getDecl(); if (enum_decl) { + enum_decl = enum_decl->getDefinition(); + if (!enum_decl) + return; CompilerType integer_type = GetType(enum_decl->getIntegerType()); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; @@ -7508,6 +7542,8 @@ clang::CXXRecordDecl *cxx_record_decl = record_qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + cxx_record_decl = cxx_record_decl->getDefinition(); if (cxx_record_decl == nullptr) return nullptr; @@ -8048,8 +8084,7 @@ auto *objc_method_decl = clang::ObjCMethodDecl::CreateDeserialized(ast, 0); objc_method_decl->setDeclName(method_selector); objc_method_decl->setReturnType(method_function_prototype->getReturnType()); - objc_method_decl->setDeclContext( - lldb_ast->GetDeclContextForType(ClangUtil::GetQualType(type))); + objc_method_decl->setDeclContext(class_interface_decl); objc_method_decl->setInstanceMethod(isInstance); objc_method_decl->setVariadic(isVariadic); objc_method_decl->setPropertyAccessor(isPropertyAccessor); @@ -8159,7 +8194,11 @@ if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { - tag_decl->startDefinition(); + // There are several declarations in the redeclaration chain that could + // define this type. The most logical declaration that we could turn + // into a definition is the most recent one. + clang::TagDecl *def = tag_decl->getMostRecentDecl(); + def->startDefinition(); return true; } } @@ -8169,7 +8208,11 @@ if (object_type) { clang::ObjCInterfaceDecl *interface_decl = object_type->getInterface(); if (interface_decl) { - interface_decl->startDefinition(); + // There are several declarations in the redeclaration chain that could + // define this type. The most logical declaration that we could turn + // into a definition is the most recent one. + clang::ObjCInterfaceDecl *def = interface_decl->getMostRecentDecl(); + def->startDefinition(); return true; } } @@ -8566,6 +8609,7 @@ const clang::EnumType *enutype = llvm::cast(qual_type.getTypePtr()); const clang::EnumDecl *enum_decl = enutype->getDecl(); + enum_decl = enum_decl->getDefinition(); assert(enum_decl); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; lldb::offset_t offset = data_byte_offset; @@ -8781,7 +8825,7 @@ uint32_t bitfield_bit_size) { const clang::EnumType *enutype = llvm::cast(qual_type.getTypePtr()); - const clang::EnumDecl *enum_decl = enutype->getDecl(); + const clang::EnumDecl *enum_decl = enutype->getDecl()->getDefinition(); assert(enum_decl); lldb::offset_t offset = byte_offset; const uint64_t enum_svalue = data.GetMaxS64Bitfield( @@ -9058,7 +9102,7 @@ if (!objc_class_type) break; clang::ObjCInterfaceDecl *class_interface_decl = - objc_class_type->getInterface(); + objc_class_type->getInterface()->getDefinition(); if (!class_interface_decl) break; if (level == eDescriptionLevelVerbose) @@ -9237,6 +9281,87 @@ } } +/// Appends an existing declaration to the redeclaration chain. +/// \param ts The TypeSystemClang that contains the two declarations. +/// \param prev The most recent existing declaration. +/// \param redecl The new declaration which should be appended to the end of +/// redeclaration chain. +template +static void ConnectRedeclToPrev(TypeSystemClang &ts, T *prev, T *redecl) { + assert(&ts.getASTContext() == &prev->getASTContext() && "Not "); + redecl->setPreviousDecl(prev); + // Now that the redecl chain is done, create the type explicitly via + // the TypeSystemClang interface that will reuse the type of the previous + // decl. + ts.GetTypeForDecl(redecl); + // The previous decl and the redeclaration both declare the same type. + assert(prev->getTypeForDecl() == redecl->getTypeForDecl()); +} + +/// Returns the ClangModuleID for the given declaration. +static OptionalClangModuleID GetModuleForDecl(clang::Decl *d) { + if (!d->isFromASTFile() || !d->getOwningModuleID()) + return OptionalClangModuleID(); + return OptionalClangModuleID(d->getOwningModuleID()); +} + +void TypeSystemClang::CreateRedeclaration(CompilerType ct) { + // All the cases below just check for a specific declaration kind, create + // a new declaration with matching data. We don't care about metadata which + // should only be tracked in the first redeclaration and should be identical + // for all redeclarations. + + if (clang::ObjCInterfaceDecl *interface = GetAsObjCInterfaceDecl(ct)) { + clang::NamedDecl *res = CreateObjCDecl( + interface->getName(), interface->getDeclContext()->getRedeclContext(), + GetModuleForDecl(interface), /*isForwardDecl=*/false, + interface->isImplicit()); + clang::ObjCInterfaceDecl *redecl = llvm::cast(res); + ConnectRedeclToPrev(*this, interface, redecl); + return; + } + + clang::TagDecl *tag_decl = ClangUtil::GetAsTagDecl(ct); + if (!tag_decl) + return; + + if (clang::EnumDecl *enum_decl = dyn_cast(tag_decl)) { + Declaration decl; + clang::EnumDecl *redecl = CreateEnumerationDecl( + enum_decl->getNameAsString().c_str(), + tag_decl->getDeclContext()->getRedeclContext(), + GetModuleForDecl(enum_decl), decl, GetType(enum_decl->getIntegerType()), + enum_decl->isScoped()); + ConnectRedeclToPrev(*this, enum_decl, redecl); + return; + } + + if (auto *template_decl = + dyn_cast(tag_decl)) { + auto redecl_info = m_class_template_redecl_infos.find(template_decl); + // If we are asked to redeclare a template that we haven't declared, then + // there is nothing we can do. + assert(redecl_info != m_class_template_redecl_infos.end()); + TemplateParameterInfos template_infos = redecl_info->second.m_template_args; + auto *redecl = CreateClassTemplateSpecializationDecl( + tag_decl->getDeclContext()->getRedeclContext(), + GetModuleForDecl(template_decl), + template_decl->getSpecializedTemplate(), tag_decl->getTagKind(), + template_infos); + ConnectRedeclToPrev(*this, template_decl, redecl); + return; + } + + assert(llvm::isa(tag_decl)); + clang::NamedDecl *redecl_record = CreateRecordDecl( + tag_decl->getDeclContext()->getRedeclContext(), + GetModuleForDecl(tag_decl), lldb::eAccessPublic, tag_decl->getName(), + tag_decl->getTagKind(), eLanguageTypeC_plus_plus, nullptr); + clang::TagDecl *redecl = llvm::cast(redecl_record); + ConnectRedeclToPrev(*this, tag_decl, redecl); + return; +} + DWARFASTParser *TypeSystemClang::GetDWARFParser() { if (!m_dwarf_ast_parser_up) m_dwarf_ast_parser_up = std::make_unique(*this); Index: lldb/unittests/CMakeLists.txt =================================================================== --- lldb/unittests/CMakeLists.txt +++ lldb/unittests/CMakeLists.txt @@ -89,6 +89,7 @@ add_subdirectory(Signals) add_subdirectory(Symbol) add_subdirectory(SymbolFile) +add_subdirectory(TypeSystem) add_subdirectory(Target) add_subdirectory(tools) add_subdirectory(UnwindAssembly) Index: lldb/unittests/TypeSystem/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/unittests/TypeSystem/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Clang) Index: lldb/unittests/TypeSystem/Clang/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/unittests/TypeSystem/Clang/CMakeLists.txt @@ -0,0 +1,14 @@ +add_lldb_unittest(TypeSystemClangTests + TestClangRedeclarations.cpp + + LINK_LIBS + lldbHost + lldbSymbol + lldbUtilityHelpers + lldbPluginObjectFileELF + lldbPluginObjectFileMachO + lldbPluginSymbolFileDWARF + lldbPluginSymbolFileSymtab + lldbPluginTypeSystemClang + LLVMTestingSupport + ) Index: lldb/unittests/TypeSystem/Clang/TestClangRedeclarations.cpp =================================================================== --- /dev/null +++ lldb/unittests/TypeSystem/Clang/TestClangRedeclarations.cpp @@ -0,0 +1,327 @@ +//===-- TestClangRedeclarations.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace lldb; +using namespace lldb_private; + +struct TestClangRedeclarations : public testing::Test { + SubsystemRAII subsystems; + + void SetUp() override { + m_ast.reset( + new TypeSystemClang("test ASTContext", HostInfo::GetTargetTriple())); + } + + void TearDown() override { m_ast.reset(); } + + std::unique_ptr m_ast; +}; + +TEST_F(TestClangRedeclarations, RedeclareCppClass) { + // Test redeclaring C++ classes. + + OptionalClangModuleID module_id(1); + CompilerType class_type = m_ast->CreateRecordType( + m_ast->GetTranslationUnitDecl(), module_id, lldb::eAccessNone, "A", + TTK_Class, lldb::eLanguageTypeC_plus_plus); + auto *record = llvm::cast(ClangUtil::GetAsTagDecl(class_type)); + + m_ast->CreateRedeclaration(class_type); + m_ast->StartTagDeclarationDefinition(class_type); + // For C++ classes, the definition is already available (but it is currently + // being defined). Make sure the definition is last in the redecl chain. + CXXRecordDecl *def = record->getDefinition(); + ASSERT_TRUE(def); + ASSERT_TRUE(def->isBeingDefined()); + ASSERT_NE(def, record); + EXPECT_EQ(def->getPreviousDecl(), record); + + // Add a method. + std::vector args; + CompilerType func_type = + m_ast->CreateFunctionType(m_ast->GetBasicType(lldb::eBasicTypeInt), + args.data(), args.size(), /*is_variadic=*/false, + /*type_quals=*/0, clang::CallingConv::CC_C); + const bool is_virtual = false; + const bool is_static = false; + const bool is_inline = false; + const bool is_explicit = false; + const bool is_attr_used = false; + const bool is_artificial = false; + clang::CXXMethodDecl *method = m_ast->AddMethodToCXXRecordType( + class_type.GetOpaqueQualType(), "A", nullptr, func_type, + lldb::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, + is_attr_used, is_artificial); + // Check that the method was created and is in the definition. + ASSERT_NE(method, nullptr); + EXPECT_EQ(method->getParent(), def); + + // Add an ivar and check that it was added to the definition. + FieldDecl *member_var = m_ast->AddFieldToRecordType( + class_type, "f", m_ast->GetBasicType(lldb::eBasicTypeInt), + lldb::eAccessPublic, + /*bitfield_bit_size=*/0); + ASSERT_TRUE(member_var); + EXPECT_EQ(member_var->getParent(), def); + + // Complete the class and check that the last decl is the definition. + m_ast->CompleteTagDeclarationDefinition(class_type); + EXPECT_FALSE(record->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), record->getTypeForDecl()); +} + +TEST_F(TestClangRedeclarations, RedeclareCppTemplateClass) { + // Test redeclaring C++ template classes. + + OptionalClangModuleID module_id(1); + auto args = std::make_unique(); + args->args.push_back(TemplateArgument(m_ast->getASTContext().IntTy)); + args->names.push_back("T"); + + ClassTemplateDecl *template_decl = + m_ast->CreateClassTemplateDecl(m_ast->GetTranslationUnitDecl(), module_id, + lldb::eAccessNone, "A", TTK_Struct, *args); + ClassTemplateSpecializationDecl *fwd_decl = + m_ast->CreateClassTemplateSpecializationDecl( + m_ast->GetTranslationUnitDecl(), module_id, template_decl, TTK_Struct, + *args); + CompilerType spec_type = + m_ast->CreateClassTemplateSpecializationType(fwd_decl); + + // Delete the TemplateParameterInfos to make sure TypeSystemClang doesn't + // rely on the caller to keep them around. + args.reset(); + + m_ast->CreateRedeclaration(spec_type); + m_ast->StartTagDeclarationDefinition(spec_type); + // For C++ classes, the definition is already available (but it is currently + // being defined). Make sure the definition is last in the redecl chain. + CXXRecordDecl *def = fwd_decl->getDefinition(); + ASSERT_TRUE(def); + ASSERT_TRUE(def->isBeingDefined()); + ASSERT_NE(def, fwd_decl); + EXPECT_EQ(def->getPreviousDecl(), fwd_decl); + + // Add an ivar and check that it was added to the definition. + FieldDecl *member_var = m_ast->AddFieldToRecordType( + spec_type, "f", m_ast->GetBasicType(lldb::eBasicTypeInt), + lldb::eAccessPublic, + /*bitfield_bit_size=*/0); + ASSERT_TRUE(member_var); + EXPECT_EQ(member_var->getParent(), def); + + // Complete the class and check that the last decl is the definition. + m_ast->CompleteTagDeclarationDefinition(spec_type); + EXPECT_FALSE(fwd_decl->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), fwd_decl->getTypeForDecl()); +} + +TEST_F(TestClangRedeclarations, RedeclareObjCClass) { + // Test redeclaring Objective-C interfaces. + + OptionalClangModuleID module_id(1); + CompilerType objc_class = + m_ast->CreateObjCClass("A", m_ast->GetTranslationUnitDecl(), module_id, + /*isForwardDecl=*/false, + /*isInternal=*/false); + ObjCInterfaceDecl *interface = m_ast->GetAsObjCInterfaceDecl(objc_class); + m_ast->CreateRedeclaration(objc_class); + m_ast->StartTagDeclarationDefinition(objc_class); + ObjCInterfaceDecl *def = interface->getDefinition(); + ASSERT_TRUE(def); + ASSERT_NE(def, interface); + EXPECT_EQ(def->getPreviousDecl(), interface); + + // Add a method. + std::vector args; + CompilerType func_type = + m_ast->CreateFunctionType(m_ast->GetBasicType(lldb::eBasicTypeInt), + args.data(), args.size(), /*is_variadic=*/false, + /*type_quals=*/0, clang::CallingConv::CC_C); + const bool variadic = false; + const bool artificial = false; + const bool objc_direct = false; + clang::ObjCMethodDecl *method = TypeSystemClang::AddMethodToObjCObjectType( + objc_class, "-[A foo]", func_type, lldb::eAccessPublic, artificial, + variadic, objc_direct); + // Check that the method was created and is in the definition. + ASSERT_NE(method, nullptr); + EXPECT_EQ(*def->meth_begin(), method); + + // Add an ivar and check that it was added to the definition. + FieldDecl *ivar = m_ast->AddFieldToRecordType( + objc_class, "f", m_ast->GetBasicType(lldb::eBasicTypeInt), + lldb::eAccessPublic, + /*bitfield_bit_size=*/0); + ASSERT_TRUE(ivar); + EXPECT_EQ(*def->ivar_begin(), ivar); + + m_ast->CompleteTagDeclarationDefinition(objc_class); + // The forward declaration should be unchanged. + EXPECT_FALSE(interface->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), interface->getTypeForDecl()); +} + +TEST_F(TestClangRedeclarations, RedeclareEnum) { + // Test redeclaring enums. + + OptionalClangModuleID module_id(1); + Declaration decl; + CompilerType enum_type = m_ast->CreateEnumerationType( + "A", m_ast->GetTranslationUnitDecl(), module_id, decl, + m_ast->GetBasicType(lldb::eBasicTypeInt), /*is_scoped=*/true); + + EnumDecl *fwd_decl = m_ast->GetAsEnumDecl(enum_type); + m_ast->CreateRedeclaration(enum_type); + m_ast->StartTagDeclarationDefinition(enum_type); + m_ast->AddEnumerationValueToEnumerationType( + enum_type, decl, "case1", /*enum_value=*/1, /*enum_value_bit_size=*/32); + m_ast->CompleteTagDeclarationDefinition(enum_type); + + // There should now be a definition at the end of the redeclaration chain. + EnumDecl *def = fwd_decl->getDefinition(); + ASSERT_TRUE(def); + ASSERT_NE(def, fwd_decl); + EXPECT_EQ(def->getPreviousDecl(), fwd_decl); + // The forward declaration should be unchanged. + EXPECT_FALSE(fwd_decl->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), fwd_decl->getTypeForDecl()); + + // Check that ForEachEnumerator uses the definition. + bool seen_value = false; + m_ast->ForEachEnumerator(enum_type.GetOpaqueQualType(), + [&seen_value](const CompilerType &, ConstString name, + const llvm::APSInt &) { + EXPECT_EQ(name, "case1"); + seen_value = true; + return true; + }); + EXPECT_TRUE(seen_value); +} + +TEST_F(TestClangRedeclarations, NestedDecls) { + // Tests that nested declarations pick the right redeclaration as their + // DeclContext. + + // Create a class. + CompilerType context_class = m_ast->CreateRecordType( + m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), + lldb::eAccessNone, "A", TTK_Class, lldb::eLanguageTypeC_plus_plus); + auto *fwd_decl = + llvm::cast(ClangUtil::GetAsTagDecl(context_class)); + + // Give it a redeclaration that defines it. + m_ast->CreateRedeclaration(context_class); + m_ast->StartTagDeclarationDefinition(context_class); + m_ast->CompleteTagDeclarationDefinition(context_class); + + // Check that there is one forward declaration and a definition now. + CXXRecordDecl *def = fwd_decl->getDefinition(); + ASSERT_TRUE(def); + EXPECT_FALSE(fwd_decl->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + + // Create a nested class and make sure it picks the definition as its + // DeclContext. + CompilerType nested_class = m_ast->CreateRecordType( + fwd_decl, OptionalClangModuleID(), lldb::eAccessPublic, "A", TTK_Class, + lldb::eLanguageTypeC_plus_plus); + EXPECT_EQ(ClangUtil::GetAsTagDecl(nested_class)->getDeclContext(), def); + + CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt); + + // Create a typedef and make sure it picks the definition as its DeclContext. + CompilerType nested_typedef = int_type.CreateTypedef( + "t", + CompilerDeclContext(m_ast.get(), static_cast(fwd_decl)), + /*payload=*/0); + const TypedefType *typedef_type = + ClangUtil::GetQualType(nested_typedef)->getAs(); + ASSERT_TRUE(typedef_type); + TypedefNameDecl *nested_typedef_decl = typedef_type->getDecl(); + ASSERT_TRUE(nested_typedef_decl); + EXPECT_EQ(nested_typedef_decl->getDeclContext(), def); + + TypeSystemClang::TemplateParameterInfos args; + args.args.push_back(TemplateArgument(m_ast->getASTContext().IntTy)); + args.names.push_back("T"); + + // Create a class template and specialization and check that their DeclContext + // is the definition. + ClassTemplateDecl *template_decl = m_ast->CreateClassTemplateDecl( + fwd_decl, OptionalClangModuleID(), lldb::eAccessPublic, "A", TTK_Struct, + args); + EXPECT_EQ(template_decl->getDeclContext(), def); + ClassTemplateSpecializationDecl *template_spec_decl = + m_ast->CreateClassTemplateSpecializationDecl( + fwd_decl, OptionalClangModuleID(), template_decl, TTK_Struct, args); + EXPECT_EQ(template_spec_decl->getDeclContext(), def); +} + +TEST_F(TestClangRedeclarations, MetadataRedeclaration) { + // Tests that metadata is shared between redeclarations. + + // Create a class with the test metadata. + CompilerType class_with_metadata = m_ast->CreateRecordType( + m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), + lldb::eAccessPublic, "A", TTK_Class, lldb::eLanguageTypeC_plus_plus); + auto *record = + llvm::cast(ClangUtil::GetAsTagDecl(class_with_metadata)); + ClangASTMetadata metadata; + metadata.SetUserID(1234); + m_ast->SetMetadata(record, metadata); + ASSERT_EQ(m_ast->GetMetadata(record)->GetUserID(), 1234U); + + // Redeclare and define the redeclaration. + m_ast->CreateRedeclaration(class_with_metadata); + m_ast->StartTagDeclarationDefinition(class_with_metadata); + m_ast->CompleteTagDeclarationDefinition(class_with_metadata); + CXXRecordDecl *def = record->getDefinition(); + ASSERT_TRUE(def); + ASSERT_NE(def, record); + EXPECT_EQ(def->getPreviousDecl(), record); + + // Check that the redeclaration has the right metadata; + ASSERT_TRUE(m_ast->GetMetadata(def)); + EXPECT_EQ(m_ast->GetMetadata(def)->GetUserID(), 1234U); +}