-
Notifications
You must be signed in to change notification settings - Fork 12.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[APINotes] Support annotating C++ methods #99512
Conversation
|
@llvm/pr-subscribers-clang Author: Egor Zhdan (egorzhdan) ChangesThis adds support for adding Clang attributes to C++ methods declared within C++ records by using API Notes. For instance: 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 Patch is 33.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/99512.diff 15 Files Affected:
diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h
index 37a4ff7a69712..03657352c49a5 100644
--- a/clang/include/clang/APINotes/APINotesReader.h
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -141,6 +141,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<CXXMethodInfo> lookupCXXMethod(ContextID CtxID,
+ llvm::StringRef Name);
+
/// Look for information regarding the given global variable.
///
/// \param Name The name of the global variable.
@@ -166,6 +176,17 @@ class APINotesReader {
/// \returns information about the enumerator, if known.
VersionedInfo<EnumConstantInfo> 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<ContextID>
+ lookupTagID(llvm::StringRef Name,
+ std::optional<Context> 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 e82dbc7c9540e..e0fe5eacef725 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 b389aa8d56f16..c8e5e4df25d17 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -656,6 +656,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:
@@ -789,6 +795,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<CXX_METHOD_DATA, // record ID
+ llvm::BCVBR<16>, // 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<ContextID> ParentContextID, ContextKind Kind,
+ uint32_t ContextID)
+ : parentContextID(ParentContextID ? ParentContextID->Value : -1),
+ contextKind(static_cast<uint8_t>(Kind)), contextID(ContextID) {}
+
+ ContextTableKey(std::optional<Context> 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 7600738374840..768fdc231bd57 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<CXXMethodTableInfo,
+ SingleDeclTableKey, CXXMethodInfo> {
+public:
+ static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
+ auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
+ auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
+ return {CtxID, NameID};
+ }
+
+ hash_value_type ComputeHash(internal_key_type Key) {
+ return static_cast<size_t>(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<EnumConstantTableInfo, uint32_t,
@@ -630,6 +653,12 @@ class APINotesReader::Implementation {
/// The Objective-C method table.
std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;
+ using SerializedCXXMethodTable =
+ llvm::OnDiskIterableChainedHashTable<CXXMethodTableInfo>;
+
+ /// The C++ method table.
+ std::unique_ptr<SerializedCXXMethodTable> CXXMethodTable;
+
using SerializedObjCSelectorTable =
llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;
@@ -683,6 +712,8 @@ class APINotesReader::Implementation {
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
+ bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
+ llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
@@ -1140,6 +1171,81 @@ bool APINotesReader::Implementation::readObjCMethodBlock(
return false;
}
+bool APINotesReader::Implementation::readCXXMethodBlock(
+ llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
+ if (Cursor.EnterSubBlock(CXX_METHOD_BLOCK_ID))
+ return true;
+
+ llvm::Expected<llvm::BitstreamEntry> 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
@@ -1692,6 +1798,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)) {
@@ -1911,6 +2025,23 @@ auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
return {Implementation->SwiftVersion, *Known};
}
+auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
+ -> VersionedInfo<CXXMethodInfo> {
+ if (!Implementation->CXXMethodTable)
+ return std::nullopt;
+
+ std::optional<IdentifierID> 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<Context> Ctx)
-> VersionedInfo<GlobalVariableInfo> {
@@ -1965,6 +2096,24 @@ auto APINotesReader::lookupEnumConstant(llvm::StringRef Name)
return {Implementation->SwiftVersion, *Known};
}
+auto APINotesReader::lookupTagID(llvm::StringRef Name,
+ std::optional<Context> ParentCtx)
+ -> std::optional<ContextID> {
+ if (!Implementation->ContextIDTable)
+ return std::nullopt;
+
+ std::optional<IdentifierID> 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<Context> Ctx)
-> VersionedInfo<TagInfo> {
if (!Implementation->TagTable)
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index 1090d3f20df21..e259c761591ba 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -70,6 +70,13 @@ class APINotesWriter::Implementation {
llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>>
ObjCMethods;
+ /// Information about C++ methods.
+ ///
+ /// Indexed by the context ID and name ID.
+ llvm::DenseMap<SingleDeclTableKey,
+ llvm::SmallVector<std::pair<VersionTuple, CXXMethodInfo>, 1>>
+ CXXMethods;
+
/// Mapping from selectors to selector ID.
llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs;
@@ -150,6 +157,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);
@@ -181,6 +189,7 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) {
writeContextBlock(Stream);
writeObjCPropertyBlock(Stream);
writeObjCMethodBlock(Stream);
+ writeCXXMethodBlock(Stream);
writeObjCSelectorBlock(Stream);
writeGlobalVariableBlock(Stream);
writeGlobalFunctionBlock(Stream);
@@ -765,6 +774,34 @@ class ObjCMethodTableInfo
emitFunctionInfo(OS, OMI);
}
};
+
+/// Used to serialize the on-disk C++ method table.
+class CXXMethodTableInfo
+ : public VersionedTableInfo<CXXMethodTableInfo, SingleDeclTableKey,
+ CXXMethodInfo> {
+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<uint32_t>(Key.parentContextID);
+ writer.write<uint32_t>(Key.nameID);
+ }
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return static_cast<size_t>(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(
@@ -794,6 +831,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<CXXMethodTableInfo> Generator;
+ for (auto &OM : CXXMethods)
+ Generator.insert(OM.first, OM.second);
+
+ llvm::raw_svector_ostream BlobStream(HashTableBlob);
+ // Make sure that no bucket is at offset 0
+ llvm::support::endian::write<uint32_t>(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 {
@@ -1344,6 +1408,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<Context> Ctx,
llvm::StringRef Name,
const GlobalVariableInfo &Info,
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 870b64e3b7a9b..060e1fdaf2fd9 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -420,6 +420,7 @@ struct Tag {
std::optional<bool> FlagEnum;
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
std::optional<bool> SwiftCopyable;
+ FunctionsSeq Methods;
};
typedef std::vector<Tag> TagsSeq;
@@ -454,6 +455,7 @@ template <> struct MappingTraits<Tag> {
IO.mapOptional("FlagEnum", T.FlagEnum);
IO.mapOptional("EnumKind", T.EnumConvenienceKind);
IO.mapOptional("SwiftCopyable", T.SwiftCopyable);
+ IO.mapOptional("Methods", T.Methods);
}
};
} // namespace yaml
@@ -874,6 +876,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<Context> ParentContext, const Tag &T,
+ VersionTuple SwiftVersion) {
+ TagInfo TI;
+ std::optional<ContextID> ParentContextID =
+ ParentContext ? std::optional<ContextID>(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;
+ ...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
33ac937 to
0a0dbf2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit on naming, don't think it matters.
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
0a0dbf2 to
117596b
Compare
This adds support for adding Clang attributes to C++ methods declared within C++ records by using API Notes.
For instance:
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