-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[APINotes] Upstream dependencies of Sema logic to apply API Notes to decls #73017
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
Conversation
@llvm/pr-subscribers-clang-modules @llvm/pr-subscribers-clang Author: Egor Zhdan (egorzhdan) ChangesThis upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes This is the largest chunk of the API Notes functionality in the upstreaming process. I will soon submit a follow-up patch to actually enable usage of this functionality by having a Clang driver flag that enables API Notes, along with tests. (it might be easier to review commit-by-commit) Patch is 74.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/73017.diff 19 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c2fbdfc66c540d6..acfb75a3dee3e7a 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -301,6 +301,9 @@ class VariadicEnumArgument<string name, string type, list<string> values,
bit IsExternalType = isExternalType;
}
+// Represents an attribute wrapped by another attribute.
+class AttrArgument<string name, bit opt = 0> : Argument<name, opt>;
+
// This handles one spelling of an attribute.
class Spelling<string name, string variety, int version = 1> {
string Name = name;
@@ -2257,7 +2260,7 @@ def ObjCBridgeRelated : InheritableAttr {
def NSErrorDomain : InheritableAttr {
let Spellings = [GNU<"ns_error_domain">];
let Subjects = SubjectList<[Enum], ErrorDiag>;
- let Args = [DeclArgument<Var, "ErrorDomain">];
+ let Args = [IdentifierArgument<"ErrorDomain">];
let Documentation = [NSErrorDomainDocs];
}
@@ -2593,6 +2596,22 @@ def SwiftError : InheritableAttr {
let Documentation = [SwiftErrorDocs];
}
+def SwiftImportAsNonGeneric : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let SemaHandler = 0;
+ let Documentation = [InternalOnly];
+}
+
+def SwiftImportPropertyAsAccessors : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let SemaHandler = 0;
+ let Documentation = [InternalOnly];
+}
+
def SwiftName : InheritableAttr {
let Spellings = [GNU<"swift_name">];
let Args = [StringArgument<"Name">];
@@ -2614,6 +2633,31 @@ def SwiftPrivate : InheritableAttr {
let SimpleHandler = 1;
}
+def SwiftVersioned : Attr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">,
+ BoolArgument<"IsReplacedByActive">];
+ let SemaHandler = 0;
+ let Documentation = [InternalOnly];
+}
+
+def SwiftVersionedRemoval : Attr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">,
+ BoolArgument<"IsReplacedByActive">];
+ let SemaHandler = 0;
+ let Documentation = [InternalOnly];
+ let AdditionalMembers = [{
+ attr::Kind getAttrKindToRemove() const {
+ return static_cast<attr::Kind>(getRawKind());
+ }
+ }];
+}
+
def NoDeref : TypeAttr {
let Spellings = [Clang<"noderef">];
let Documentation = [NoDerefDocs];
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c3753ca2828e25e..02c5b9527f5d5f3 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1606,6 +1606,9 @@ def err_pragma_invalid_keyword : Error<
def err_pragma_pipeline_invalid_keyword : Error<
"invalid argument; expected 'disable'">;
+// API notes.
+def err_type_unparsed : Error<"unparsed tokens following type">;
+
// Pragma unroll support.
def warn_pragma_unroll_cuda_value_in_parens : Warning<
"argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 990692c06d7d3a8..f09f2899754690d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10667,6 +10667,13 @@ def warn_imp_cast_drops_unaligned : Warning<
} // end of sema category
+let CategoryName = "API Notes Issue" in {
+
+def err_incompatible_replacement_type : Error<
+ "API notes replacement type %0 has a different size from original type %1">;
+
+} // end of API Notes category
+
let CategoryName = "OpenMP Issue" in {
// OpenMP support.
def err_omp_expected_var_arg : Error<
diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h
index 899e665e7454652..b6ecc7e5ded9e2a 100644
--- a/clang/include/clang/Lex/Lexer.h
+++ b/clang/include/clang/Lex/Lexer.h
@@ -198,11 +198,11 @@ class Lexer : public PreprocessorLexer {
/// from. Currently this is only used by _Pragma handling.
SourceLocation getFileLoc() const { return FileLoc; }
-private:
/// Lex - Return the next token in the file. If this is the end of file, it
/// return the tok::eof token. This implicitly involves the preprocessor.
bool Lex(Token &Result);
+private:
/// Called when the preprocessor is in 'dependency scanning lexing mode'.
bool LexDependencyDirectiveToken(Token &Result);
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 465453826c0b982..034a8f90abbfaf7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3609,6 +3609,18 @@ class Parser : public CodeCompletionHandler {
ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
SourceLocation &DeclEnd);
+ /// Parse the given string as a type.
+ ///
+ /// This is a dangerous utility function currently employed only by API notes.
+ /// It is not a general entry-point for safely parsing types from strings.
+ ///
+ /// \param TypeStr The string to be parsed as a type.
+ /// \param Context The name of the context in which this string is being
+ /// parsed, which will be used in diagnostics.
+ /// \param IncludeLoc The location at which this parse was triggered.
+ TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context,
+ SourceLocation IncludeLoc);
+
//===--------------------------------------------------------------------===//
// Modules
DeclGroupPtrTy ParseModuleDecl(Sema::ModuleImportState &ImportState);
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 59806bcbcbb2dbc..0b300df3ef5b666 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -955,6 +955,10 @@ class Sema final {
OpaqueParser = P;
}
+ /// Callback to the parser to parse a type expressed as a string.
+ std::function<TypeResult(StringRef, StringRef, SourceLocation)>
+ ParseTypeFromStringCallback;
+
class DelayedDiagnostics;
class DelayedDiagnosticsState {
@@ -3017,6 +3021,9 @@ class Sema final {
ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC,
SourceLocation Loc,
QualType T);
+ QualType AdjustParameterTypeForObjCAutoRefCount(QualType T,
+ SourceLocation NameLoc,
+ TypeSourceInfo *TSInfo);
ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc,
SourceLocation NameLoc, IdentifierInfo *Name,
QualType T, TypeSourceInfo *TSInfo,
@@ -4742,6 +4749,12 @@ class Sema final {
bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A,
bool SkipArgCountCheck = false);
+ /// Map any API notes provided for this declaration to attributes on the
+ /// declaration.
+ ///
+ /// Triggered by declaration-attribute processing.
+ void ProcessAPINotes(Decl *D);
+
/// Determine if type T is a valid subject for a nonnull and similar
/// attributes. By default, we look through references (the behavior used by
/// nonnull), but if the second parameter is true, then we treat a reference
@@ -4802,6 +4815,29 @@ class Sema final {
/// Valid types should not have multiple attributes with different CCs.
const AttributedType *getCallingConvAttributedType(QualType T) const;
+ /// Check whether a nullability type specifier can be added to the given
+ /// type through some means not written in source (e.g. API notes).
+ ///
+ /// \param Type The type to which the nullability specifier will be
+ /// added. On success, this type will be updated appropriately.
+ ///
+ /// \param Nullability The nullability specifier to add.
+ ///
+ /// \param DiagLoc The location to use for diagnostics.
+ ///
+ /// \param AllowArrayTypes Whether to accept nullability specifiers on an
+ /// array type (e.g., because it will decay to a pointer).
+ ///
+ /// \param OverrideExisting Whether to override an existing, locally-specified
+ /// nullability specifier rather than complaining about the conflict.
+ ///
+ /// \returns true if nullability cannot be applied, false otherwise.
+ bool CheckImplicitNullabilityTypeSpecifier(QualType &Type,
+ NullabilityKind Nullability,
+ SourceLocation DiagLoc,
+ bool AllowArrayTypes,
+ bool OverrideExisting);
+
/// Process the attributes before creating an attributed statement. Returns
/// the semantic attributes that have been processed.
void ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributes &InAttrs,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8cb5b09fd3b0fa6..421fec3bed58534 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -8028,6 +8028,71 @@ bool Parser::TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc,
return false;
}
+TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context,
+ SourceLocation IncludeLoc) {
+ // Consume (unexpanded) tokens up to the end-of-directive.
+ SmallVector<Token, 4> Tokens;
+ {
+ // Create a new buffer from which we will parse the type.
+ auto &SourceMgr = PP.getSourceManager();
+ FileID FID = SourceMgr.createFileID(
+ llvm::MemoryBuffer::getMemBufferCopy(TypeStr, Context), SrcMgr::C_User,
+ 0, 0, IncludeLoc);
+
+ // Form a new lexer that references the buffer.
+ Lexer L(FID, SourceMgr.getBufferOrFake(FID), PP);
+ L.setParsingPreprocessorDirective(true);
+
+ // Lex the tokens from that buffer.
+ Token Tok;
+ do {
+ L.Lex(Tok);
+ Tokens.push_back(Tok);
+ } while (Tok.isNot(tok::eod));
+ }
+
+ // Replace the "eod" token with an "eof" token identifying the end of
+ // the provided string.
+ Token &EndToken = Tokens.back();
+ EndToken.startToken();
+ EndToken.setKind(tok::eof);
+ EndToken.setLocation(Tok.getLocation());
+ EndToken.setEofData(TypeStr.data());
+
+ // Add the current token back.
+ Tokens.push_back(Tok);
+
+ // Enter the tokens into the token stream.
+ PP.EnterTokenStream(Tokens, /*DisableMacroExpansion=*/false,
+ /*IsReinject=*/false);
+
+ // Consume the current token so that we'll start parsing the tokens we
+ // added to the stream.
+ ConsumeAnyToken();
+
+ // Enter a new scope.
+ ParseScope LocalScope(this, 0);
+
+ // Parse the type.
+ TypeResult Result = ParseTypeName(nullptr);
+
+ // Check if we parsed the whole thing.
+ if (Result.isUsable() &&
+ (Tok.isNot(tok::eof) || Tok.getEofData() != TypeStr.data())) {
+ Diag(Tok.getLocation(), diag::err_type_unparsed);
+ }
+
+ // There could be leftover tokens (e.g. because of an error).
+ // Skip through until we reach the 'end of directive' token.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ // Consume the end token.
+ if (Tok.is(tok::eof) && Tok.getEofData() == TypeStr.data())
+ ConsumeAnyToken();
+ return Result;
+}
+
void Parser::DiagnoseBitIntUse(const Token &Tok) {
// If the token is for _ExtInt, diagnose it as being deprecated. Otherwise,
// the token is about _BitInt and gets (potentially) diagnosed as use of an
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 1baeb2aeb021faa..97bcbb605dcbd75 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -70,6 +70,11 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
PP.addCommentHandler(CommentSemaHandler.get());
PP.setCodeCompletionHandler(*this);
+
+ Actions.ParseTypeFromStringCallback =
+ [this](StringRef TypeStr, StringRef Context, SourceLocation IncludeLoc) {
+ return this->ParseTypeFromString(TypeStr, Context, IncludeLoc);
+ };
}
DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) {
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 1856a88e9a3271a..4f72bce98fbbec3 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -27,6 +27,7 @@ add_clang_library(clangSema
Sema.cpp
SemaAccess.cpp
SemaAttr.cpp
+ SemaAPINotes.cpp
SemaAvailability.cpp
SemaCXXScopeSpec.cpp
SemaCast.cpp
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
new file mode 100644
index 000000000000000..4f2ac1acfc01a9d
--- /dev/null
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -0,0 +1,1014 @@
+//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the mapping from API notes to declaration attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/APINotes/APINotesReader.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Sema/SemaInternal.h"
+
+using namespace clang;
+
+namespace {
+enum IsActive_t : bool { IsNotActive, IsActive };
+enum IsReplacement_t : bool { IsNotReplacement, IsReplacement };
+
+struct VersionedInfoMetadata {
+ /// An empty version refers to unversioned metadata.
+ VersionTuple Version;
+ unsigned IsActive : 1;
+ unsigned IsReplacement : 1;
+
+ VersionedInfoMetadata(VersionTuple Version, IsActive_t Active,
+ IsReplacement_t Replacement)
+ : Version(Version), IsActive(Active == IsActive_t::IsActive),
+ IsReplacement(Replacement == IsReplacement_t::IsReplacement) {}
+};
+} // end anonymous namespace
+
+/// Determine whether this is a multi-level pointer type.
+static bool isMultiLevelPointerType(QualType Type) {
+ QualType Pointee = Type->getPointeeType();
+ if (Pointee.isNull())
+ return false;
+
+ return Pointee->isAnyPointerType() || Pointee->isObjCObjectPointerType() ||
+ Pointee->isMemberPointerType();
+}
+
+/// Apply nullability to the given declaration.
+static void applyNullability(Sema &S, Decl *D, NullabilityKind Nullability,
+ VersionedInfoMetadata Metadata) {
+ if (!Metadata.IsActive)
+ return;
+
+ QualType Type;
+
+ // Nullability for a function/method appertains to the retain type.
+ if (auto Function = dyn_cast<FunctionDecl>(D))
+ Type = Function->getReturnType();
+ else if (auto Method = dyn_cast<ObjCMethodDecl>(D))
+ Type = Method->getReturnType();
+ else if (auto Value = dyn_cast<ValueDecl>(D))
+ Type = Value->getType();
+ else if (auto Property = dyn_cast<ObjCPropertyDecl>(D))
+ Type = Property->getType();
+ else
+ return;
+
+ // Check the nullability specifier on this type.
+ QualType OrigType = Type;
+ S.CheckImplicitNullabilityTypeSpecifier(Type, Nullability, D->getLocation(),
+ isa<ParmVarDecl>(D),
+ /*overrideExisting=*/true);
+ if (Type.getTypePtr() == OrigType.getTypePtr())
+ return;
+
+ if (auto Function = dyn_cast<FunctionDecl>(D)) {
+ const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
+ if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FnType))
+ Function->setType(S.Context.getFunctionType(Type, Proto->getParamTypes(),
+ Proto->getExtProtoInfo()));
+ else
+ Function->setType(
+ S.Context.getFunctionNoProtoType(Type, FnType->getExtInfo()));
+ } else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
+ Method->setReturnType(Type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (!isMultiLevelPointerType(Type))
+ Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
+ Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
+
+ } else if (auto Value = dyn_cast<ValueDecl>(D)) {
+ Value->setType(Type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (auto Parm = dyn_cast<ParmVarDecl>(D)) {
+ if (Parm->isObjCMethodParameter() && !isMultiLevelPointerType(Type))
+ Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
+ Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
+ }
+ } else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
+ Property->setType(Type, Property->getTypeSourceInfo());
+
+ // Make it a property attribute if we can.
+ if (!isMultiLevelPointerType(Type))
+ Property->setPropertyAttributes(
+ ObjCPropertyAttribute::kind_null_resettable);
+
+ } else
+ llvm_unreachable("cannot handle nullability here");
+}
+
+/// Copy a string into ASTContext-allocated memory.
+static StringRef CopyString(ASTContext &Ctx, StringRef String) {
+ void *mem = Ctx.Allocate(String.size(), alignof(char));
+ memcpy(mem, String.data(), String.size());
+ return StringRef(static_cast<char *>(mem), String.size());
+}
+
+static AttributeCommonInfo getDummyAttrInfo() {
+ return AttributeCommonInfo(SourceRange(),
+ AttributeCommonInfo::UnknownAttribute,
+ {AttributeCommonInfo::AS_GNU,
+ /*Spelling*/ 0, /*IsAlignas*/ false,
+ /*IsRegularKeywordAttribute*/ false});
+}
+
+namespace {
+template <typename A> struct AttrKindFor {};
+
+#define ATTR(X) \
+ template <> struct AttrKindFor<X##Attr> { \
+ static const attr::Kind value = attr::X; \
+ };
+#include "clang/Basic/AttrList.inc"
+
+/// Handle an attribute introduced by API notes.
+///
+/// \param ShouldAddAttribute Whether we should add a new attribute
+/// (otherwise, we might remove an existing attribute).
+/// \param CreateAttr Create the new attribute to be added.
+template <typename A>
+void handleAPINotedAttribute(
+ Sema &S, Decl *D, bool ShouldAddAttribute, VersionedInfoMetadata Metadata,
+ llvm::function_ref<A *()> CreateAttr,
+ llvm::function_ref<Decl::attr_iterator(const Decl *)> GetExistingAttr) {
+ if (Metadata.IsActive) {
+ auto Existing = GetExistingAttr(D);
+ if (Existing != D->attr_end()) {
+ // Remove the existing attribute, and treat it as a superseded
+ // non-versioned attribute.
+ auto *Versioned = SwiftVersionedAttr::CreateImplicit(
+ S.Context, Metadata.Version, *Existing, /*IsReplacedByActive*/ true);
+
+ D->getAttrs().erase(Existing);
+ D->addAttr(Versioned);
+ }
+
+ // If we're supposed to add a new attribute, do so.
+ if (ShouldAddAttribute) {
+ if (auto Attr = CreateAttr())
+ D->addAttr(Attr);
+ }
+
+ } else {
+ if (ShouldAddAttribute) {
+ if (auto Attr = CreateAttr()) {
+ auto *Versioned = SwiftVersionedAttr::CreateImplicit(
+ S.Context, Metadata.Version, Attr,
+ /*IsReplacedByActive*/ Metadata.IsReplacement);
+ D->addAttr(Versioned);
+ }
+ } else {
+ // FIXME: This isn't preserving enough information for things like
+ // availability, where we're trying to remove a /specific/ kind of
+ // attribute.
+ auto *Versioned = SwiftVersionedRemovalAttr::CreateImplicit(
+ S.Context, Metadata.Version, AttrKindFor<A>::value,
+ /*IsReplacedByActive*/ Metadata.IsReplacement);
+ D->addAttr(Versioned);
+ }
+ }
+}
+
+template <typename A>
+void handleAPINotedAttribute(Sema &S, Decl *D, bool ShouldAddAttribute,
+ VersionedInfoMeta...
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
474f4f4
to
dfc82b0
Compare
@compnerd ping :) |
@compnerd ping ;) |
@compnerd pinging again :) |
clang/lib/Sema/SemaDecl.cpp
Outdated
// Special cases for arrays: | ||
// - if it's const, use __unsafe_unretained | ||
// - otherwise, it's an error | ||
if (T->isArrayType()) { |
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.
I wonder if this is valuable to sink into QualType
.
dfc82b0
to
646265c
Compare
646265c
to
2e6f1e8
Compare
2e6f1e8
to
a884c27
Compare
Do we want to submit the first three in the series and then take on the topmost commit? |
Sounds good, let me remove the 4th commit from this PR |
a884c27
to
19634d4
Compare
This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes This was extracted from a larger PR: #73017
This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes
This is the largest chunk of the API Notes functionality in the upstreaming process. I will soon submit a follow-up patch to actually enable usage of this functionality by having a Clang driver flag that enables API Notes, along with tests.
(it might be easier to review commit-by-commit)