From 549fc30858c833a12d47e433fc8010a8b1e9121f Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Thu, 18 Jul 2024 13:27:24 +0100 Subject: [PATCH 1/2] [APINotes] Reduce memory footprint for Obj-C/C++ contexts We were storing extraneous data for certain Objective-C/C++ entities. Specifically, for declarations that can be nested in another context (such as functions) we were storing the kind of the parent context in addition to its ID. The ID is always sufficient. This removes the logically incorrect usages of `ContextTableKey` that don't actually describe a context, but rather describe a single declaration. This introduces `SingleDeclTableKey` to store that kind of entities in a more compact and reasonable way. (cherry picked from commit c5f402f95d9617882b26d5799f503383b895c2e4) --- clang/lib/APINotes/APINotesFormat.h | 57 +++++++++++++++++++++++---- clang/lib/APINotes/APINotesReader.cpp | 32 ++++++--------- clang/lib/APINotes/APINotesWriter.cpp | 46 +++++++++++---------- 3 files changed, 84 insertions(+), 51 deletions(-) diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index e3aa76df8316c..42dfe7a773a97 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 26; // SwiftCopyable +const uint16_t VERSION_MINOR = 27; // SingleDeclTableKey const uint8_t kSwiftCopyable = 1; const uint8_t kSwiftNonCopyable = 2; @@ -269,12 +269,6 @@ struct ContextTableKey { : parentContextID(parentContextID), contextKind(contextKind), contextID(contextID) {} - ContextTableKey(std::optional context, IdentifierID nameID) - : parentContextID(context ? context->id.Value : (uint32_t)-1), - contextKind(context ? static_cast(context->kind) - : static_cast(-1)), - contextID(nameID) {} - llvm::hash_code hashValue() const { return llvm::hash_value( std::tuple{parentContextID, contextKind, contextID}); @@ -286,6 +280,32 @@ inline bool operator==(const ContextTableKey &lhs, const ContextTableKey &rhs) { lhs.contextKind == rhs.contextKind && lhs.contextID == rhs.contextID; } +/// A stored Objective-C or C++ declaration, represented by the ID of its parent +/// context, and the name of the declaration. +struct SingleDeclTableKey { + uint32_t parentContextID; + uint32_t nameID; + + SingleDeclTableKey() : parentContextID(-1), nameID(-1) {} + + SingleDeclTableKey(uint32_t ParentContextID, uint32_t NameID) + : parentContextID(ParentContextID), nameID(NameID) {} + + SingleDeclTableKey(std::optional ParentCtx, IdentifierID NameID) + : parentContextID(ParentCtx ? ParentCtx->id.Value + : static_cast(-1)), + nameID(NameID) {} + + llvm::hash_code hashValue() const { + return llvm::hash_value(std::make_pair(parentContextID, nameID)); + } +}; + +inline bool operator==(const SingleDeclTableKey &lhs, + const SingleDeclTableKey &rhs) { + return lhs.parentContextID == rhs.parentContextID && lhs.nameID == rhs.nameID; +} + } // namespace api_notes } // namespace clang @@ -341,6 +361,29 @@ template <> struct DenseMapInfo { return lhs == rhs; } }; + +template <> struct DenseMapInfo { + static inline clang::api_notes::SingleDeclTableKey getEmptyKey() { + return clang::api_notes::SingleDeclTableKey(); + } + + static inline clang::api_notes::SingleDeclTableKey getTombstoneKey() { + return clang::api_notes::SingleDeclTableKey{ + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()}; + } + + static unsigned + getHashValue(const clang::api_notes::SingleDeclTableKey &value) { + return value.hashValue(); + } + + static bool isEqual(const clang::api_notes::SingleDeclTableKey &lhs, + const clang::api_notes::SingleDeclTableKey &rhs) { + return lhs == rhs; + } +}; + } // namespace llvm #endif diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index ceb9fb10db8bf..ad2fbed7a6308 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -429,15 +429,13 @@ class ObjCSelectorTableInfo { /// Used to deserialize the on-disk global variable table. class GlobalVariableTableInfo - : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto NameID = endian::readNext(Data); - return {CtxID, ContextKind, NameID}; + return {CtxID, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -454,15 +452,13 @@ class GlobalVariableTableInfo /// Used to deserialize the on-disk global function table. class GlobalFunctionTableInfo - : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto NameID = endian::readNext(Data); - return {CtxID, ContextKind, NameID}; + return {CtxID, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -501,15 +497,13 @@ class EnumConstantTableInfo /// Used to deserialize the on-disk tag table. class TagTableInfo - : public VersionedTableInfo { + : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto NameID = endian::readNext(Data); - return {CtxID, ContextKind, NameID}; + return {CtxID, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -563,16 +557,14 @@ class TagTableInfo /// Used to deserialize the on-disk typedef table. class TypedefTableInfo - : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); - auto ContextKind = - endian::readNext(Data); auto nameID = endian::readNext(Data); - return {CtxID, ContextKind, nameID}; + return {CtxID, nameID}; } hash_value_type ComputeHash(internal_key_type Key) { @@ -1941,7 +1933,7 @@ auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name, if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->GlobalVariableTable->find(Key); if (Known == Implementation->GlobalVariableTable->end()) @@ -1960,7 +1952,7 @@ auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name, if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->GlobalFunctionTable->find(Key); if (Known == Implementation->GlobalFunctionTable->end()) @@ -1994,7 +1986,7 @@ auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional Ctx) if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->TagTable->find(Key); if (Known == Implementation->TagTable->end()) @@ -2013,7 +2005,7 @@ auto APINotesReader::lookupTypedef(llvm::StringRef Name, if (!NameID) return std::nullopt; - ContextTableKey Key(Ctx, *NameID); + SingleDeclTableKey Key(Ctx, *NameID); auto Known = Implementation->TypedefTable->find(Key); if (Known == Implementation->TypedefTable->end()) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 0068270060d6b..77fef363f280e 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -77,17 +77,17 @@ class APINotesWriter::Implementation { /// Information about global variables. /// - /// Indexed by the context ID, contextKind, identifier ID. + /// Indexed by the context ID, identifier ID. llvm::DenseMap< - ContextTableKey, + SingleDeclTableKey, llvm::SmallVector, 1>> GlobalVariables; /// Information about global functions. /// - /// Indexed by the context ID, contextKind, identifier ID. + /// Indexed by the context ID, identifier ID. llvm::DenseMap< - ContextTableKey, + SingleDeclTableKey, llvm::SmallVector, 1>> GlobalFunctions; @@ -100,15 +100,15 @@ class APINotesWriter::Implementation { /// Information about tags. /// - /// Indexed by the context ID, contextKind, identifier ID. - llvm::DenseMap, 1>> Tags; /// Information about typedefs. /// - /// Indexed by the context ID, contextKind, identifier ID. - llvm::DenseMap, 1>> Typedefs; @@ -872,18 +872,17 @@ void APINotesWriter::Implementation::writeObjCSelectorBlock( namespace { /// Used to serialize the on-disk global variable table. class GlobalVariableTableInfo - : public VersionedTableInfo { public: unsigned getKeyLength(key_type_ref) { - return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); + return sizeof(uint32_t) + sizeof(uint32_t); } void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write(Key.parentContextID); - writer.write(Key.contextKind); - writer.write(Key.contextID); + writer.write(Key.nameID); } hash_value_type ComputeHash(key_type_ref Key) { @@ -986,18 +985,17 @@ void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) { /// Used to serialize the on-disk global function table. class GlobalFunctionTableInfo - : public VersionedTableInfo { public: unsigned getKeyLength(key_type_ref) { - return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); + return sizeof(uint32_t) + sizeof(uint32_t); } void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write(Key.parentContextID); - writer.write(Key.contextKind); - writer.write(Key.contextID); + writer.write(Key.nameID); } hash_value_type ComputeHash(key_type_ref Key) { @@ -1098,20 +1096,20 @@ void APINotesWriter::Implementation::writeEnumConstantBlock( namespace { template class CommonTypeTableInfo - : public VersionedTableInfo { + : public VersionedTableInfo { public: using key_type_ref = typename CommonTypeTableInfo::key_type_ref; using hash_value_type = typename CommonTypeTableInfo::hash_value_type; unsigned getKeyLength(key_type_ref) { - return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(IdentifierID); + return sizeof(uint32_t) + sizeof(IdentifierID); } void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write(Key.parentContextID); - writer.write(Key.contextKind); - writer.write(Key.contextID); + writer.write(Key.nameID); } hash_value_type ComputeHash(key_type_ref Key) { @@ -1358,7 +1356,7 @@ void APINotesWriter::addGlobalVariable(std::optional Ctx, const GlobalVariableInfo &Info, VersionTuple SwiftVersion) { IdentifierID VariableID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, VariableID); + SingleDeclTableKey Key(Ctx, VariableID); Implementation->GlobalVariables[Key].push_back({SwiftVersion, Info}); } @@ -1367,7 +1365,7 @@ void APINotesWriter::addGlobalFunction(std::optional Ctx, const GlobalFunctionInfo &Info, VersionTuple SwiftVersion) { IdentifierID NameID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, NameID); + SingleDeclTableKey Key(Ctx, NameID); Implementation->GlobalFunctions[Key].push_back({SwiftVersion, Info}); } @@ -1381,7 +1379,7 @@ void APINotesWriter::addEnumConstant(llvm::StringRef Name, void APINotesWriter::addTag(std::optional Ctx, llvm::StringRef Name, const TagInfo &Info, VersionTuple SwiftVersion) { IdentifierID TagID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, TagID); + SingleDeclTableKey Key(Ctx, TagID); Implementation->Tags[Key].push_back({SwiftVersion, Info}); } @@ -1389,7 +1387,7 @@ void APINotesWriter::addTypedef(std::optional Ctx, llvm::StringRef Name, const TypedefInfo &Info, VersionTuple SwiftVersion) { IdentifierID TypedefID = Implementation->getIdentifier(Name); - ContextTableKey Key(Ctx, TypedefID); + SingleDeclTableKey Key(Ctx, TypedefID); Implementation->Typedefs[Key].push_back({SwiftVersion, Info}); } From 0aae94bf352dede80b6748315f60e7c3ffb7a5d2 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Fri, 19 Jul 2024 13:35:13 +0100 Subject: [PATCH 2/2] [APINotes] Support annotating C++ methods This adds support for adding Clang attributes to C++ methods declared within C++ records by using API Notes. For instance: ``` Tags: - Name: IntWrapper Methods: - Name: getIncremented Availability: none ``` This is the first instance of something within a C++ record being annotated with API Notes, so it adds the necessary infra to make a C++ record an "API Notes context". Notably this does not add support for nested C++ tags. That will be added in a follow-up patch. rdar://131387880 (cherry picked from commit 8a79dc7e6f765f3f49c5dd9330fc0826d3362858) --- clang/include/clang/APINotes/APINotesReader.h | 21 +++ clang/include/clang/APINotes/APINotesWriter.h | 8 + clang/include/clang/APINotes/Types.h | 7 + clang/lib/APINotes/APINotesFormat.h | 29 ++++ clang/lib/APINotes/APINotesReader.cpp | 149 ++++++++++++++++ clang/lib/APINotes/APINotesWriter.cpp | 72 ++++++++ clang/lib/APINotes/APINotesYAMLCompiler.cpp | 164 ++++++++++-------- clang/lib/Sema/SemaAPINotes.cpp | 50 +++++- .../APINotes/Inputs/Headers/Methods.apinotes | 8 + clang/test/APINotes/Inputs/Headers/Methods.h | 14 ++ .../Inputs/Headers/Namespaces.apinotes | 3 + .../test/APINotes/Inputs/Headers/Namespaces.h | 1 + .../APINotes/Inputs/Headers/module.modulemap | 4 + clang/test/APINotes/methods.cpp | 9 + clang/test/APINotes/namespaces.cpp | 5 + 15 files changed, 465 insertions(+), 79 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Headers/Methods.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/Methods.h create mode 100644 clang/test/APINotes/methods.cpp diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index b62055a76a09d..6cc8a2fe66b09 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -148,6 +148,16 @@ class APINotesReader { ObjCSelectorRef Selector, bool IsInstanceMethod); + /// Look for information regarding the given C++ method in the given C++ tag + /// context. + /// + /// \param CtxID The ID that references the parent context, i.e. a C++ tag. + /// \param Name The name of the C++ method we're looking for. + /// + /// \returns Information about the method, if known. + VersionedInfo lookupCXXMethod(ContextID CtxID, + llvm::StringRef Name); + /// Look for information regarding the given global variable. /// /// \param Name The name of the global variable. @@ -173,6 +183,17 @@ class APINotesReader { /// \returns information about the enumerator, if known. VersionedInfo lookupEnumConstant(llvm::StringRef Name); + /// Look for the context ID of the given C++ tag. + /// + /// \param Name The name of the tag we're looking for. + /// \param ParentCtx The context in which this tag is declared, e.g. a C++ + /// namespace. + /// + /// \returns The ID, if known. + std::optional + lookupTagID(llvm::StringRef Name, + std::optional ParentCtx = std::nullopt); + /// Look for information regarding the given tag /// (struct/union/enum/C++ class). /// diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index f2c2d0815b196..1bb5dfc793594 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -78,6 +78,14 @@ class APINotesWriter { bool IsInstanceMethod, const ObjCMethodInfo &Info, llvm::VersionTuple SwiftVersion); + /// Add information about a specific C++ method. + /// + /// \param CtxID The context in which this method resides, i.e. a C++ tag. + /// \param Name The name of the method. + /// \param Info Information about this method. + void addCXXMethod(ContextID CtxID, llvm::StringRef Name, + const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion); + /// Add information about a global variable. /// /// \param Name The name of this global variable. diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index d00058f9f8857..e03e366ad763d 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -659,6 +659,12 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() {} }; +/// Describes API notes data for a C++ method. +class CXXMethodInfo : public FunctionInfo { +public: + CXXMethodInfo() {} +}; + /// Describes API notes data for an enumerator. class EnumConstantInfo : public CommonEntityInfo { public: @@ -792,6 +798,7 @@ enum class ContextKind : uint8_t { ObjCClass = 0, ObjCProtocol = 1, Namespace = 2, + Tag = 3, }; struct Context { diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 42dfe7a773a97..cd6456dbe37b2 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -63,6 +63,10 @@ enum BlockID { /// about the method. OBJC_METHOD_BLOCK_ID, + /// The C++ method data block, which maps C++ (context id, method name) pairs + /// to information about the method. + CXX_METHOD_BLOCK_ID, + /// The Objective-C selector data block, which maps Objective-C /// selector names (# of pieces, identifier IDs) to the selector ID /// used in other tables. @@ -181,6 +185,20 @@ using ObjCMethodDataLayout = >; } // namespace objc_method_block +namespace cxx_method_block { +enum { + CXX_METHOD_DATA = 1, +}; + +using CXXMethodDataLayout = + llvm::BCRecordLayout, // table offset within the blob (see + // below) + llvm::BCBlob // map from C++ (context id, name) + // tuples to C++ method information + >; +} // namespace cxx_method_block + namespace objc_selector_block { enum { OBJC_SELECTOR_DATA = 1, @@ -269,6 +287,17 @@ struct ContextTableKey { : parentContextID(parentContextID), contextKind(contextKind), contextID(contextID) {} + ContextTableKey(std::optional ParentContextID, ContextKind Kind, + uint32_t ContextID) + : parentContextID(ParentContextID ? ParentContextID->Value : -1), + contextKind(static_cast(Kind)), contextID(ContextID) {} + + ContextTableKey(std::optional ParentContext, ContextKind Kind, + uint32_t ContextID) + : ContextTableKey(ParentContext ? std::make_optional(ParentContext->id) + : std::nullopt, + Kind, ContextID) {} + llvm::hash_code hashValue() const { return llvm::hash_value( std::tuple{parentContextID, contextKind, contextID}); diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index ad2fbed7a6308..7475e63c90845 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -473,6 +473,29 @@ class GlobalFunctionTableInfo } }; +/// Used to deserialize the on-disk C++ method table. +class CXXMethodTableInfo + : public VersionedTableInfo { +public: + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + auto CtxID = endian::readNext(Data); + auto NameID = endian::readNext(Data); + return {CtxID, NameID}; + } + + hash_value_type ComputeHash(internal_key_type Key) { + return static_cast(Key.hashValue()); + } + + static CXXMethodInfo readUnversioned(internal_key_type Key, + const uint8_t *&Data) { + CXXMethodInfo Info; + ReadFunctionInfo(Data, Info); + return Info; + } +}; + /// Used to deserialize the on-disk enumerator table. class EnumConstantTableInfo : public VersionedTableInfo ObjCMethodTable; + using SerializedCXXMethodTable = + llvm::OnDiskIterableChainedHashTable; + + /// The C++ method table. + std::unique_ptr CXXMethodTable; + using SerializedObjCSelectorTable = llvm::OnDiskIterableChainedHashTable; @@ -686,6 +715,8 @@ class APINotesReader::Implementation { llvm::SmallVectorImpl &Scratch); bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); + bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor, + llvm::SmallVectorImpl &Scratch); bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor, @@ -1144,6 +1175,81 @@ bool APINotesReader::Implementation::readObjCMethodBlock( return false; } +bool APINotesReader::Implementation::readCXXMethodBlock( + llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { + if (Cursor.EnterSubBlock(CXX_METHOD_BLOCK_ID)) + return true; + + llvm::Expected MaybeNext = Cursor.advance(); + if (!MaybeNext) { + // FIXME this drops the error on the floor. + consumeError(MaybeNext.takeError()); + return false; + } + llvm::BitstreamEntry Next = MaybeNext.get(); + while (Next.Kind != llvm::BitstreamEntry::EndBlock) { + if (Next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (Next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (Cursor.SkipBlock()) + return true; + + MaybeNext = Cursor.advance(); + if (!MaybeNext) { + // FIXME this drops the error on the floor. + consumeError(MaybeNext.takeError()); + return false; + } + Next = MaybeNext.get(); + continue; + } + + Scratch.clear(); + llvm::StringRef BlobData; + llvm::Expected MaybeKind = + Cursor.readRecord(Next.ID, Scratch, &BlobData); + if (!MaybeKind) { + // FIXME this drops the error on the floor. + consumeError(MaybeKind.takeError()); + return false; + } + unsigned Kind = MaybeKind.get(); + switch (Kind) { + case cxx_method_block::CXX_METHOD_DATA: { + // Already saw C++ method table. + if (CXXMethodTable) + return true; + + uint32_t tableOffset; + cxx_method_block::CXXMethodDataLayout::readRecord(Scratch, tableOffset); + auto base = reinterpret_cast(BlobData.data()); + + CXXMethodTable.reset(SerializedCXXMethodTable::Create( + base + tableOffset, base + sizeof(uint32_t), base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + MaybeNext = Cursor.advance(); + if (!MaybeNext) { + // FIXME this drops the error on the floor. + consumeError(MaybeNext.takeError()); + return false; + } + Next = MaybeNext.get(); + } + + return false; +} + bool APINotesReader::Implementation::readObjCSelectorBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) @@ -1696,6 +1802,14 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer, } break; + case CXX_METHOD_BLOCK_ID: + if (!HasValidControlBlock || + Implementation->readCXXMethodBlock(Cursor, Scratch)) { + Failed = true; + return; + } + break; + case OBJC_SELECTOR_BLOCK_ID: if (!HasValidControlBlock || Implementation->readObjCSelectorBlock(Cursor, Scratch)) { @@ -1923,6 +2037,23 @@ auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, return {Implementation->SwiftVersion, *Known}; } +auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name) + -> VersionedInfo { + if (!Implementation->CXXMethodTable) + return std::nullopt; + + std::optional NameID = Implementation->getIdentifier(Name); + if (!NameID) + return std::nullopt; + + auto Known = Implementation->CXXMethodTable->find( + SingleDeclTableKey(CtxID.Value, *NameID)); + if (Known == Implementation->CXXMethodTable->end()) + return std::nullopt; + + return {Implementation->SwiftVersion, *Known}; +} + auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { @@ -1977,6 +2108,24 @@ auto APINotesReader::lookupEnumConstant(llvm::StringRef Name) return {Implementation->SwiftVersion, *Known}; } +auto APINotesReader::lookupTagID(llvm::StringRef Name, + std::optional ParentCtx) + -> std::optional { + if (!Implementation->ContextIDTable) + return std::nullopt; + + std::optional TagID = Implementation->getIdentifier(Name); + if (!TagID) + return std::nullopt; + + auto KnownID = Implementation->ContextIDTable->find( + ContextTableKey(ParentCtx, ContextKind::Tag, *TagID)); + if (KnownID == Implementation->ContextIDTable->end()) + return std::nullopt; + + return ContextID(*KnownID); +} + auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { if (!Implementation->TagTable) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 77fef363f280e..2428f0a96829d 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -72,6 +72,13 @@ class APINotesWriter::Implementation { llvm::SmallVector, 1>> ObjCMethods; + /// Information about C++ methods. + /// + /// Indexed by the context ID and name ID. + llvm::DenseMap, 1>> + CXXMethods; + /// Mapping from selectors to selector ID. llvm::DenseMap SelectorIDs; @@ -152,6 +159,7 @@ class APINotesWriter::Implementation { void writeContextBlock(llvm::BitstreamWriter &Stream); void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream); void writeObjCMethodBlock(llvm::BitstreamWriter &Stream); + void writeCXXMethodBlock(llvm::BitstreamWriter &Stream); void writeObjCSelectorBlock(llvm::BitstreamWriter &Stream); void writeGlobalVariableBlock(llvm::BitstreamWriter &Stream); void writeGlobalFunctionBlock(llvm::BitstreamWriter &Stream); @@ -183,6 +191,7 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) { writeContextBlock(Stream); writeObjCPropertyBlock(Stream); writeObjCMethodBlock(Stream); + writeCXXMethodBlock(Stream); writeObjCSelectorBlock(Stream); writeGlobalVariableBlock(Stream); writeGlobalFunctionBlock(Stream); @@ -772,6 +781,34 @@ class ObjCMethodTableInfo emitFunctionInfo(OS, OMI); } }; + +/// Used to serialize the on-disk C++ method table. +class CXXMethodTableInfo + : public VersionedTableInfo { +public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t); + } + + void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { + llvm::support::endian::Writer writer(OS, llvm::endianness::little); + writer.write(Key.parentContextID); + writer.write(Key.nameID); + } + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(key.hashValue()); + } + + unsigned getUnversionedInfoSize(const CXXMethodInfo &OMI) { + return getFunctionInfoSize(OMI); + } + + void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &OMI) { + emitFunctionInfo(OS, OMI); + } +}; } // namespace void APINotesWriter::Implementation::writeObjCMethodBlock( @@ -801,6 +838,33 @@ void APINotesWriter::Implementation::writeObjCMethodBlock( } } +void APINotesWriter::Implementation::writeCXXMethodBlock( + llvm::BitstreamWriter &Stream) { + llvm::BCBlockRAII Scope(Stream, CXX_METHOD_BLOCK_ID, 3); + + if (CXXMethods.empty()) + return; + + { + llvm::SmallString<4096> HashTableBlob; + uint32_t Offset; + { + llvm::OnDiskChainedHashTableGenerator Generator; + for (auto &MD : CXXMethods) + Generator.insert(MD.first, MD.second); + + llvm::raw_svector_ostream BlobStream(HashTableBlob); + // Make sure that no bucket is at offset 0 + llvm::support::endian::write(BlobStream, 0, + llvm::endianness::little); + Offset = Generator.Emit(BlobStream); + } + + cxx_method_block::CXXMethodDataLayout CXXMethodData(Stream); + CXXMethodData.emit(Scratch, Offset, HashTableBlob); + } +} + namespace { /// Used to serialize the on-disk Objective-C selector table. class ObjCSelectorTableInfo { @@ -1351,6 +1415,14 @@ void APINotesWriter::addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, } } +void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name, + const CXXMethodInfo &Info, + VersionTuple SwiftVersion) { + IdentifierID NameID = Implementation->getIdentifier(Name); + SingleDeclTableKey Key(CtxID.Value, NameID); + Implementation->CXXMethods[Key].push_back({SwiftVersion, Info}); +} + void APINotesWriter::addGlobalVariable(std::optional Ctx, llvm::StringRef Name, const GlobalVariableInfo &Info, diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index cc37a771a56c3..c3bc3d25f2ba0 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -533,6 +533,7 @@ struct Tag { std::optional FlagEnum; std::optional EnumConvenienceKind; std::optional SwiftCopyable; + FunctionsSeq Methods; }; typedef std::vector TagsSeq; @@ -567,6 +568,7 @@ template <> struct MappingTraits { IO.mapOptional("FlagEnum", T.FlagEnum); IO.mapOptional("EnumKind", T.EnumConvenienceKind); IO.mapOptional("SwiftCopyable", T.SwiftCopyable); + IO.mapOptional("Methods", T.Methods); } }; } // namespace yaml @@ -987,6 +989,96 @@ class YAMLConverter { TheNamespace.Items, SwiftVersion); } + void convertFunction(const Function &Function, FunctionInfo &FI) { + convertAvailability(Function.Availability, FI, Function.Name); + FI.setSwiftPrivate(Function.SwiftPrivate); + FI.SwiftName = std::string(Function.SwiftName); + convertParams(Function.Params, FI); + convertNullability(Function.Nullability, Function.NullabilityOfRet, FI, + Function.Name); + FI.ResultType = std::string(Function.ResultType); + FI.setRetainCountConvention(Function.RetainCountConvention); + } + + void convertTagContext(std::optional ParentContext, const Tag &T, + VersionTuple SwiftVersion) { + TagInfo TI; + std::optional ParentContextID = + ParentContext ? std::optional(ParentContext->id) + : std::nullopt; + convertCommonType(T, TI, T.Name); + + if ((T.SwiftRetainOp || T.SwiftReleaseOp) && !T.SwiftImportAs) { + emitError(llvm::Twine("should declare SwiftImportAs to use " + "SwiftRetainOp and SwiftReleaseOp (for ") + + T.Name + ")"); + return; + } + if (T.SwiftReleaseOp.has_value() != T.SwiftRetainOp.has_value()) { + emitError(llvm::Twine("should declare both SwiftReleaseOp and " + "SwiftRetainOp (for ") + + T.Name + ")"); + return; + } + + if (T.SwiftImportAs) + TI.SwiftImportAs = T.SwiftImportAs; + if (T.SwiftRetainOp) + TI.SwiftRetainOp = T.SwiftRetainOp; + if (T.SwiftReleaseOp) + TI.SwiftReleaseOp = T.SwiftReleaseOp; + + if (T.SwiftCopyable) + TI.setSwiftCopyable(T.SwiftCopyable); + + if (T.EnumConvenienceKind) { + if (T.EnumExtensibility) { + emitError( + llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") + + T.Name + ")"); + return; + } + if (T.FlagEnum) { + emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") + + T.Name + ")"); + return; + } + switch (*T.EnumConvenienceKind) { + case EnumConvenienceAliasKind::None: + TI.EnumExtensibility = EnumExtensibilityKind::None; + TI.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFEnum: + TI.EnumExtensibility = EnumExtensibilityKind::Open; + TI.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFOptions: + TI.EnumExtensibility = EnumExtensibilityKind::Open; + TI.setFlagEnum(true); + break; + case EnumConvenienceAliasKind::CFClosedEnum: + TI.EnumExtensibility = EnumExtensibilityKind::Closed; + TI.setFlagEnum(false); + break; + } + } else { + TI.EnumExtensibility = T.EnumExtensibility; + TI.setFlagEnum(T.FlagEnum); + } + + Writer.addTag(ParentContext, T.Name, TI, SwiftVersion); + + ContextInfo CI; + auto TagCtxID = Writer.addContext(ParentContextID, T.Name, ContextKind::Tag, + CI, SwiftVersion); + + for (const auto &CXXMethod : T.Methods) { + CXXMethodInfo MI; + convertFunction(CXXMethod, MI); + Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion); + } + } + void convertTopLevelItems(std::optional Ctx, const TopLevelItems &TLItems, VersionTuple SwiftVersion) { @@ -1063,14 +1155,7 @@ class YAMLConverter { } GlobalFunctionInfo GFI; - convertAvailability(Function.Availability, GFI, Function.Name); - GFI.setSwiftPrivate(Function.SwiftPrivate); - GFI.SwiftName = std::string(Function.SwiftName); - convertParams(Function.Params, GFI); - convertNullability(Function.Nullability, Function.NullabilityOfRet, GFI, - Function.Name); - GFI.ResultType = std::string(Function.ResultType); - GFI.setRetainCountConvention(Function.RetainCountConvention); + convertFunction(Function, GFI); Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion); } @@ -1101,68 +1186,7 @@ class YAMLConverter { continue; } - TagInfo TI; - convertCommonType(Tag, TI, Tag.Name); - - if ((Tag.SwiftRetainOp || Tag.SwiftReleaseOp) && !Tag.SwiftImportAs) { - emitError(llvm::Twine("should declare SwiftImportAs to use " - "SwiftRetainOp and SwiftReleaseOp (for ") + - Tag.Name + ")"); - continue; - } - if (Tag.SwiftReleaseOp.has_value() != Tag.SwiftRetainOp.has_value()) { - emitError(llvm::Twine("should declare both SwiftReleaseOp and " - "SwiftRetainOp (for ") + - Tag.Name + ")"); - continue; - } - - if (Tag.SwiftImportAs) - TI.SwiftImportAs = Tag.SwiftImportAs; - if (Tag.SwiftRetainOp) - TI.SwiftRetainOp = Tag.SwiftRetainOp; - if (Tag.SwiftReleaseOp) - TI.SwiftReleaseOp = Tag.SwiftReleaseOp; - - if (Tag.SwiftCopyable) - TI.setSwiftCopyable(Tag.SwiftCopyable); - - if (Tag.EnumConvenienceKind) { - if (Tag.EnumExtensibility) { - emitError( - llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") + - Tag.Name + ")"); - continue; - } - if (Tag.FlagEnum) { - emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") + - Tag.Name + ")"); - continue; - } - switch (*Tag.EnumConvenienceKind) { - case EnumConvenienceAliasKind::None: - TI.EnumExtensibility = EnumExtensibilityKind::None; - TI.setFlagEnum(false); - break; - case EnumConvenienceAliasKind::CFEnum: - TI.EnumExtensibility = EnumExtensibilityKind::Open; - TI.setFlagEnum(false); - break; - case EnumConvenienceAliasKind::CFOptions: - TI.EnumExtensibility = EnumExtensibilityKind::Open; - TI.setFlagEnum(true); - break; - case EnumConvenienceAliasKind::CFClosedEnum: - TI.EnumExtensibility = EnumExtensibilityKind::Closed; - TI.setFlagEnum(false); - break; - } - } else { - TI.EnumExtensibility = Tag.EnumExtensibility; - TI.setFlagEnum(Tag.FlagEnum); - } - - Writer.addTag(Ctx, Tag.Name, TI, SwiftVersion); + convertTagContext(Ctx, Tag, SwiftVersion); } // Write all typedefs. diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 3482f3741fce6..055e66a0c3486 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -546,6 +546,13 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, Metadata); } +/// Process API notes for a C++ method. +static void ProcessAPINotes(Sema &S, CXXMethodDecl *Method, + const api_notes::CXXMethodInfo &Info, + VersionedInfoMetadata Metadata) { + ProcessAPINotes(S, (FunctionOrMethod)Method, Info, Metadata); +} + /// Process API notes for a global function. static void ProcessAPINotes(Sema &S, FunctionDecl *D, const api_notes::GlobalFunctionInfo &Info, @@ -782,13 +789,9 @@ void Sema::ProcessAPINotes(Decl *D) { if (!D) return; - // Globals. - if (D->getDeclContext()->isFileContext() || - D->getDeclContext()->isNamespace() || - D->getDeclContext()->isExternCContext() || - D->getDeclContext()->isExternCXXContext()) { - std::optional APINotesContext; - if (auto NamespaceContext = dyn_cast(D->getDeclContext())) { + auto GetNamespaceContext = + [&](DeclContext *DC) -> std::optional { + if (auto NamespaceContext = dyn_cast(DC)) { for (auto Reader : APINotes.findAPINotes(NamespaceContext->getLocation())) { // Retrieve the context ID for the parent namespace of the decl. @@ -811,11 +814,20 @@ void Sema::ProcessAPINotes(Decl *D) { break; } if (NamespaceID) - APINotesContext = api_notes::Context( - *NamespaceID, api_notes::ContextKind::Namespace); + return api_notes::Context(*NamespaceID, + api_notes::ContextKind::Namespace); } } + return std::nullopt; + }; + // Globals. + if (D->getDeclContext()->isFileContext() || + D->getDeclContext()->isNamespace() || + D->getDeclContext()->isExternCContext() || + D->getDeclContext()->isExternCXXContext()) { + std::optional APINotesContext = + GetNamespaceContext(D->getDeclContext()); // Global variables. if (auto VD = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { @@ -1001,4 +1013,24 @@ void Sema::ProcessAPINotes(Decl *D) { return; } } + + if (auto CXXRecord = dyn_cast(D->getDeclContext())) { + auto GetRecordContext = [&](api_notes::APINotesReader *Reader) + -> std::optional { + auto ParentContext = GetNamespaceContext(CXXRecord->getDeclContext()); + if (auto Found = Reader->lookupTagID(CXXRecord->getName(), ParentContext)) + return *Found; + + return std::nullopt; + }; + + if (auto CXXMethod = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetRecordContext(Reader)) { + auto Info = Reader->lookupCXXMethod(*Context, CXXMethod->getName()); + ProcessVersionedAPINotes(*this, CXXMethod, Info); + } + } + } + } } diff --git a/clang/test/APINotes/Inputs/Headers/Methods.apinotes b/clang/test/APINotes/Inputs/Headers/Methods.apinotes new file mode 100644 index 0000000000000..0fa6991a51ff4 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/Methods.apinotes @@ -0,0 +1,8 @@ +--- +Name: Methods +Tags: +- Name: IntWrapper + Methods: + - Name: getIncremented + Availability: none + AvailabilityMsg: "oh no" diff --git a/clang/test/APINotes/Inputs/Headers/Methods.h b/clang/test/APINotes/Inputs/Headers/Methods.h new file mode 100644 index 0000000000000..f46fe31533e5d --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/Methods.h @@ -0,0 +1,14 @@ +struct IntWrapper { + int value; + + IntWrapper getIncremented() const { return {value + 1}; } +}; + +// TODO: support nested tags +struct Outer { + struct Inner { + int value; + + Inner getDecremented() const { return {value - 1}; } + }; +}; diff --git a/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes b/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes index e9da36787b638..68073932d600e 100644 --- a/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes +++ b/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes @@ -38,6 +38,9 @@ Namespaces: Tags: - Name: char_box SwiftName: NestedCharBox + Methods: + - Name: methodInNestedNamespace + SwiftName: swiftMethodInNestedNamespace() Namespaces: - Name: Namespace1 Tags: diff --git a/clang/test/APINotes/Inputs/Headers/Namespaces.h b/clang/test/APINotes/Inputs/Headers/Namespaces.h index 6a79e996be86c..e996b8ffa6b6e 100644 --- a/clang/test/APINotes/Inputs/Headers/Namespaces.h +++ b/clang/test/APINotes/Inputs/Headers/Namespaces.h @@ -9,6 +9,7 @@ namespace Nested1 { void funcInNestedNamespace(int i); struct char_box { char c; + void methodInNestedNamespace(); }; } diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index d515169184f4f..faf6042c78d57 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -24,6 +24,10 @@ module BrokenTypes { header "BrokenTypes.h" } +module Methods { + header "Methods.h" +} + module ModuleWithWrongCase { header "ModuleWithWrongCase.h" } diff --git a/clang/test/APINotes/methods.cpp b/clang/test/APINotes/methods.cpp new file mode 100644 index 0000000000000..692f750ed66c7 --- /dev/null +++ b/clang/test/APINotes/methods.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Methods -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -x c++ +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Methods -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter IntWrapper::getIncremented -x c++ | FileCheck --check-prefix=CHECK-METHOD %s + +#include "Methods.h" + +// CHECK-METHOD: Dumping IntWrapper::getIncremented: +// CHECK-METHOD-NEXT: CXXMethodDecl {{.+}} getIncremented +// CHECK-METHOD: UnavailableAttr {{.+}} <> "oh no" diff --git a/clang/test/APINotes/namespaces.cpp b/clang/test/APINotes/namespaces.cpp index c19eee565c2da..a6517a324b9c5 100644 --- a/clang/test/APINotes/namespaces.cpp +++ b/clang/test/APINotes/namespaces.cpp @@ -9,6 +9,7 @@ // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested2::varInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-ANOTHER-GLOBAL-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::funcInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-FUNC-IN-NESTED-NAMESPACE %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::char_box::methodInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-METHOD-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::Namespace1::char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter varInInlineNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-GLOBAL-IN-INLINE-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter funcInInlineNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-FUNC-IN-INLINE-NAMESPACE %s @@ -55,6 +56,10 @@ // CHECK-STRUCT-IN-NESTED-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in Namespaces struct char_box // CHECK-STRUCT-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <> "NestedCharBox" +// CHECK-METHOD-IN-NESTED-NAMESPACE: Dumping Namespace1::Nested1::char_box::methodInNestedNamespace: +// CHECK-METHOD-IN-NESTED-NAMESPACE-NEXT: CXXMethodDecl {{.+}} imported in Namespaces methodInNestedNamespace +// CHECK-METHOD-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <> "swiftMethodInNestedNamespace()" + // CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE: Dumping Namespace1::Nested1::Namespace1::char_box: // CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in Namespaces struct char_box // CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <> "DeepNestedCharBox"