[clang] 529a057 - [clang][extract-api] Add support for macros
Daniel Grumberg via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 30 10:33:30 PDT 2022
Author: Daniel Grumberg
Date: 2022-03-30T18:33:10+01:00
New Revision: 529a0570f7e8c5144bd3ad057e43f00e3af58d1b
URL: https://github.com/llvm/llvm-project/commit/529a0570f7e8c5144bd3ad057e43f00e3af58d1b
DIFF: https://github.com/llvm/llvm-project/commit/529a0570f7e8c5144bd3ad057e43f00e3af58d1b.diff
LOG: [clang][extract-api] Add support for macros
To achieve this we hook into the preprocessor during the
ExtractAPIAction and record definitions for macros that don't get
undefined during preprocessing.
Added:
clang/test/ExtractAPI/macro_undefined.c
clang/test/ExtractAPI/macros.c
Modified:
clang/include/clang/ExtractAPI/API.h
clang/include/clang/ExtractAPI/DeclarationFragments.h
clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
clang/lib/ExtractAPI/API.cpp
clang/lib/ExtractAPI/DeclarationFragments.cpp
clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 57397e7c256ea..142dd7a45de47 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -30,6 +30,7 @@
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include <memory>
+#include <type_traits>
namespace clang {
namespace extractapi {
@@ -49,6 +50,9 @@ namespace extractapi {
/// \endcode
using DocComment = std::vector<RawComment::CommentLine>;
+// Classes deriving from APIRecord need to have Name be the first constructor
+// argument. This is so that they are compatible with `addTopLevelRecord`
+// defined in API.cpp
/// The base representation of an API record. Holds common symbol information.
struct APIRecord {
StringRef Name;
@@ -83,6 +87,7 @@ struct APIRecord {
RK_ObjCMethod,
RK_ObjCInterface,
RK_ObjCProtocol,
+ RK_MacroDefinition,
};
private:
@@ -119,10 +124,11 @@ struct GlobalRecord : APIRecord {
/// The function signature of the record if it is a function.
FunctionSignature Signature;
- GlobalRecord(GVKind Kind, StringRef Name, StringRef USR, PresumedLoc Loc,
+ GlobalRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading, FunctionSignature Signature)
+ DeclarationFragments SubHeading, GVKind Kind,
+ FunctionSignature Signature)
: APIRecord(RK_Global, Name, USR, Loc, Availability, Linkage, Comment,
Declaration, SubHeading),
GlobalKind(Kind), Signature(Signature) {}
@@ -374,6 +380,21 @@ struct ObjCProtocolRecord : ObjCContainerRecord {
virtual void anchor();
};
+struct MacroDefinitionRecord : APIRecord {
+ MacroDefinitionRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading)
+ : APIRecord(RK_MacroDefinition, Name, USR, Loc, AvailabilityInfo(),
+ LinkageInfo(), {}, Declaration, SubHeading) {}
+
+ static bool classof(const APIRecord *Record) {
+ return Record->getKind() == RK_MacroDefinition;
+ }
+
+private:
+ virtual void anchor();
+};
+
/// APISet holds the set of API records collected from given inputs.
class APISet {
public:
@@ -530,29 +551,24 @@ class APISet {
DeclarationFragments Declaration,
DeclarationFragments SubHeading);
- /// A map to store the set of GlobalRecord%s with the declaration name as the
- /// key.
- using GlobalRecordMap =
- llvm::MapVector<StringRef, std::unique_ptr<GlobalRecord>>;
-
- /// A map to store the set of EnumRecord%s with the declaration name as the
- /// key.
- using EnumRecordMap = llvm::MapVector<StringRef, std::unique_ptr<EnumRecord>>;
-
- /// A map to store the set of StructRecord%s with the declaration name as the
- /// key.
- using StructRecordMap =
- llvm::MapVector<StringRef, std::unique_ptr<StructRecord>>;
-
- /// A map to store the set of ObjCInterfaceRecord%s with the declaration name
- /// as the key.
- using ObjCInterfaceRecordMap =
- llvm::MapVector<StringRef, std::unique_ptr<ObjCInterfaceRecord>>;
-
- /// A map to store the set of ObjCProtocolRecord%s with the declaration name
- /// as the key.
- using ObjCProtocolRecordMap =
- llvm::MapVector<StringRef, std::unique_ptr<ObjCProtocolRecord>>;
+ /// Create a macro definition record into the API set.
+ ///
+ /// Note: the caller is responsible for keeping the StringRef \p Name and
+ /// \p USR alive. APISet::copyString provides a way to copy strings into
+ /// APISet itself, and APISet::recordUSRForMacro(StringRef Name,
+ /// SourceLocation SL, const SourceManager &SM) is a helper method to generate
+ /// the USR for the macro and keep it alive in APISet.
+ MacroDefinitionRecord *addMacroDefinition(StringRef Name, StringRef USR,
+ PresumedLoc Loc,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading);
+
+ /// A mapping type to store a set of APIRecord%s with the declaration name as
+ /// the key.
+ template <typename RecordTy,
+ typename =
+ std::enable_if_t<std::is_base_of<APIRecord, RecordTy>::value>>
+ using RecordMap = llvm::MapVector<StringRef, std::unique_ptr<RecordTy>>;
/// Get the target triple for the ExtractAPI invocation.
const llvm::Triple &getTarget() const { return Target; }
@@ -560,15 +576,16 @@ class APISet {
/// Get the language used by the APIs.
Language getLanguage() const { return Lang; }
- const GlobalRecordMap &getGlobals() const { return Globals; }
- const EnumRecordMap &getEnums() const { return Enums; }
- const StructRecordMap &getStructs() const { return Structs; }
- const ObjCInterfaceRecordMap &getObjCInterfaces() const {
+ const RecordMap<GlobalRecord> &getGlobals() const { return Globals; }
+ const RecordMap<EnumRecord> &getEnums() const { return Enums; }
+ const RecordMap<StructRecord> &getStructs() const { return Structs; }
+ const RecordMap<ObjCInterfaceRecord> &getObjCInterfaces() const {
return ObjCInterfaces;
}
- const ObjCProtocolRecordMap &getObjCProtocols() const {
+ const RecordMap<ObjCProtocolRecord> &getObjCProtocols() const {
return ObjCProtocols;
}
+ const RecordMap<MacroDefinitionRecord> &getMacros() const { return Macros; }
/// Generate and store the USR of declaration \p D.
///
@@ -577,6 +594,14 @@ class APISet {
/// \returns a StringRef of the generated USR string.
StringRef recordUSR(const Decl *D);
+ /// Generate and store the USR for a macro \p Name.
+ ///
+ /// Note: The USR string is stored in and owned by Allocator.
+ ///
+ /// \returns a StringRef to the generate USR string.
+ StringRef recordUSRForMacro(StringRef Name, SourceLocation SL,
+ const SourceManager &SM);
+
/// Copy \p String into the Allocator in this APISet.
///
/// \returns a StringRef of the copied string in APISet::Allocator.
@@ -594,11 +619,12 @@ class APISet {
const llvm::Triple Target;
const Language Lang;
- GlobalRecordMap Globals;
- EnumRecordMap Enums;
- StructRecordMap Structs;
- ObjCInterfaceRecordMap ObjCInterfaces;
- ObjCProtocolRecordMap ObjCProtocols;
+ RecordMap<GlobalRecord> Globals;
+ RecordMap<EnumRecord> Enums;
+ RecordMap<StructRecord> Structs;
+ RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
+ RecordMap<ObjCProtocolRecord> ObjCProtocols;
+ RecordMap<MacroDefinitionRecord> Macros;
};
} // namespace extractapi
diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index ca5b014a1f5a5..7f6e3311125aa 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -22,6 +22,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/Lex/MacroInfo.h"
#include "llvm/ADT/StringRef.h"
#include <vector>
@@ -222,12 +223,22 @@ class DeclarationFragmentsBuilder {
static DeclarationFragments
getFragmentsForObjCProtocol(const ObjCProtocolDecl *);
+ /// Build DeclarationFragments for a macro.
+ ///
+ /// \param Name name of the macro.
+ /// \param MD the associated MacroDirective.
+ static DeclarationFragments getFragmentsForMacro(StringRef Name,
+ const MacroDirective *MD);
+
/// Build sub-heading fragments for a NamedDecl.
static DeclarationFragments getSubHeading(const NamedDecl *);
/// Build sub-heading fragments for an Objective-C method.
static DeclarationFragments getSubHeading(const ObjCMethodDecl *);
+ /// Build a sub-heading for macro \p Name.
+ static DeclarationFragments getSubHeadingForMacro(StringRef Name);
+
/// Build FunctionSignature for a function-like declaration \c FunctionT like
/// FunctionDecl or ObjCMethodDecl.
///
diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index b5002cabfbbe1..02ea9c2e92c83 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -123,6 +123,9 @@ class SymbolGraphSerializer : public APISerializer {
/// Serialize an Objective-C container record.
void serializeObjCContainerRecord(const ObjCContainerRecord &Record);
+ /// Serialize a macro defintion record.
+ void serializeMacroDefinitionRecord(const MacroDefinitionRecord &Record);
+
public:
SymbolGraphSerializer(const APISet &API, StringRef ProductName,
APISerializerOption Options = {})
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 1c9314c0be10d..66ebbb5f0e999 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -17,11 +17,30 @@
#include "clang/AST/CommentLexer.h"
#include "clang/AST/RawCommentList.h"
#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/StringRef.h"
#include <memory>
using namespace clang::extractapi;
using namespace llvm;
+namespace {
+
+template <typename RecordTy, typename... CtorArgsTy>
+RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
+ StringRef Name, CtorArgsTy &&...CtorArgs) {
+ auto Result = RecordMap.insert({Name, nullptr});
+
+ // Create the record if it does not already exist
+ if (Result.second)
+ Result.first->second =
+ std::make_unique<RecordTy>(Name, std::forward<CtorArgsTy>(CtorArgs)...);
+
+ return Result.first->second.get();
+}
+
+} // namespace
+
GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR,
PresumedLoc Loc,
const AvailabilityInfo &Availability,
@@ -29,15 +48,8 @@ GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR,
DeclarationFragments Fragments,
DeclarationFragments SubHeading,
FunctionSignature Signature) {
- auto Result = Globals.insert({Name, nullptr});
- if (Result.second) {
- // Create the record if it does not already exist.
- auto Record = std::make_unique<GlobalRecord>(
- Kind, Name, USR, Loc, Availability, Linkage, Comment, Fragments,
- SubHeading, Signature);
- Result.first->second = std::move(Record);
- }
- return Result.first->second.get();
+ return addTopLevelRecord(Globals, Name, USR, Loc, Availability, Linkage,
+ Comment, Fragments, SubHeading, Kind, Signature);
}
GlobalRecord *
@@ -73,14 +85,8 @@ EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
- auto Result = Enums.insert({Name, nullptr});
- if (Result.second) {
- // Create the record if it does not already exist.
- auto Record = std::make_unique<EnumRecord>(
- Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
- Result.first->second = std::move(Record);
- }
- return Result.first->second.get();
+ return addTopLevelRecord(Enums, Name, USR, Loc, Availability, Comment,
+ Declaration, SubHeading);
}
StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
@@ -99,14 +105,8 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
- auto Result = Structs.insert({Name, nullptr});
- if (Result.second) {
- // Create the record if it does not already exist.
- auto Record = std::make_unique<StructRecord>(
- Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
- Result.first->second = std::move(Record);
- }
- return Result.first->second.get();
+ return addTopLevelRecord(Structs, Name, USR, Loc, Availability, Comment,
+ Declaration, SubHeading);
}
ObjCInterfaceRecord *APISet::addObjCInterface(
@@ -114,15 +114,9 @@ ObjCInterfaceRecord *APISet::addObjCInterface(
const AvailabilityInfo &Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, SymbolReference SuperClass) {
- auto Result = ObjCInterfaces.insert({Name, nullptr});
- if (Result.second) {
- // Create the record if it does not already exist.
- auto Record = std::make_unique<ObjCInterfaceRecord>(
- Name, USR, Loc, Availability, Linkage, Comment, Declaration, SubHeading,
- SuperClass);
- Result.first->second = std::move(Record);
- }
- return Result.first->second.get();
+ return addTopLevelRecord(ObjCInterfaces, Name, USR, Loc, Availability,
+ Linkage, Comment, Declaration, SubHeading,
+ SuperClass);
}
ObjCMethodRecord *APISet::addObjCMethod(
@@ -165,14 +159,15 @@ ObjCProtocolRecord *APISet::addObjCProtocol(
StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading) {
- auto Result = ObjCProtocols.insert({Name, nullptr});
- if (Result.second) {
- // Create the record if it does not already exist.
- auto Record = std::make_unique<ObjCProtocolRecord>(
- Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
- Result.first->second = std::move(Record);
- }
- return Result.first->second.get();
+ return addTopLevelRecord(ObjCProtocols, Name, USR, Loc, Availability, Comment,
+ Declaration, SubHeading);
+}
+
+MacroDefinitionRecord *
+APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading) {
+ return addTopLevelRecord(Macros, Name, USR, Loc, Declaration, SubHeading);
}
StringRef APISet::recordUSR(const Decl *D) {
@@ -181,6 +176,13 @@ StringRef APISet::recordUSR(const Decl *D) {
return copyString(USR);
}
+StringRef APISet::recordUSRForMacro(StringRef Name, SourceLocation SL,
+ const SourceManager &SM) {
+ SmallString<128> USR;
+ index::generateUSRForMacro(Name, SL, SM, USR);
+ return copyString(USR);
+}
+
StringRef APISet::copyString(StringRef String) {
if (String.empty())
return {};
@@ -208,3 +210,4 @@ void ObjCInstanceVariableRecord::anchor() {}
void ObjCMethodRecord::anchor() {}
void ObjCInterfaceRecord::anchor() {}
void ObjCProtocolRecord::anchor() {}
+void MacroDefinitionRecord::anchor() {}
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 7c6c9dcde9eea..5997a8faef14c 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -466,7 +466,37 @@ DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
if (!Record->getName().empty())
Fragments.appendSpace().append(
Record->getName(), DeclarationFragments::FragmentKind::Identifier);
+ return Fragments;
+}
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
+ const MacroDirective *MD) {
+ DeclarationFragments Fragments;
+ Fragments.append("#define", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+ Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier);
+
+ auto *MI = MD->getMacroInfo();
+
+ if (MI->isFunctionLike()) {
+ Fragments.append("(", DeclarationFragments::FragmentKind::Text);
+ unsigned numParameters = MI->getNumParams();
+ if (MI->isC99Varargs())
+ --numParameters;
+ for (unsigned i = 0; i < numParameters; ++i) {
+ if (i)
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ Fragments.append(MI->params()[i]->getName(),
+ DeclarationFragments::FragmentKind::InternalParam);
+ }
+ if (MI->isVariadic()) {
+ if (numParameters && MI->isC99Varargs())
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ Fragments.append("...", DeclarationFragments::FragmentKind::Text);
+ }
+ Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+ }
return Fragments;
}
@@ -699,3 +729,11 @@ DeclarationFragmentsBuilder::getSubHeading(const ObjCMethodDecl *Method) {
return Fragments.append(Method->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier);
}
+
+// Subheading of a symbol defaults to its name.
+DeclarationFragments
+DeclarationFragmentsBuilder::getSubHeadingForMacro(StringRef Name) {
+ DeclarationFragments Fragments;
+ Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier);
+ return Fragments;
+}
diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index 10b28873611e9..266acff9a72ef 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -28,6 +28,10 @@
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
@@ -506,6 +510,71 @@ class ExtractAPIConsumer : public ASTConsumer {
ExtractAPIVisitor Visitor;
};
+class MacroCallback : public PPCallbacks {
+public:
+ MacroCallback(const SourceManager &SM, APISet &API) : SM(SM), API(API) {}
+
+ void MacroDefined(const Token &MacroNameToken,
+ const MacroDirective *MD) override {
+ auto *MacroInfo = MD->getMacroInfo();
+
+ if (MacroInfo->isBuiltinMacro())
+ return;
+
+ auto SourceLoc = MacroNameToken.getLocation();
+ if (SM.isWrittenInBuiltinFile(SourceLoc) ||
+ SM.isWrittenInCommandLineFile(SourceLoc))
+ return;
+
+ PendingMacros.emplace_back(MacroNameToken, MD);
+ }
+
+ // If a macro gets undefined at some point during preprocessing of the inputs
+ // it means that it isn't an exposed API and we should therefore not add a
+ // macro definition for it.
+ void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
+ const MacroDirective *Undef) override {
+ llvm::erase_if(PendingMacros, [&MD](const PendingMacro &PM) {
+ return MD.getMacroInfo()->getDefinitionLoc() ==
+ PM.MD->getMacroInfo()->getDefinitionLoc();
+ });
+ }
+
+ void EndOfMainFile() override {
+ for (auto &PM : PendingMacros) {
+ // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
+ // file so check for it here.
+ if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
+ continue;
+
+ StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
+ PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
+ StringRef USR =
+ API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
+
+ API.addMacroDefinition(
+ Name, USR, Loc,
+ DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
+ DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
+ }
+
+ PendingMacros.clear();
+ }
+
+private:
+ struct PendingMacro {
+ Token MacroNameToken;
+ const MacroDirective *MD;
+
+ PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
+ : MacroNameToken(MacroNameToken), MD(MD) {}
+ };
+
+ const SourceManager &SM;
+ APISet &API;
+ llvm::SmallVector<PendingMacro> PendingMacros;
+};
+
} // namespace
std::unique_ptr<ASTConsumer>
@@ -522,6 +591,10 @@ ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
CI.getTarget().getTriple(),
CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
+ // Register preprocessor callbacks that will add macro definitions to API.
+ CI.getPreprocessor().addPPCallbacks(
+ std::make_unique<MacroCallback>(CI.getSourceManager(), *API));
+
return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), *API);
}
@@ -544,13 +617,17 @@ bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
HeaderContents += "\"\n";
}
- Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
- getInputBufferName());
+ auto Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
+ getInputBufferName());
// Set that buffer up as our "real" input in the CompilerInstance.
Inputs.clear();
Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
+ // Tell the processor about the input file.
+ CI.getPreprocessorOpts().addRemappedFile(Buffer->getBufferIdentifier(),
+ Buffer.release());
+
return true;
}
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 1acb67aac67de..bfd2c207c0918 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -397,6 +397,9 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
Kind["identifier"] = AddLangPrefix("protocol");
Kind["displayName"] = "Protocol";
break;
+ case APIRecord::RK_MacroDefinition:
+ Kind["identifier"] = AddLangPrefix("macro");
+ Kind["displayName"] = "Macro";
}
return Kind;
@@ -576,6 +579,15 @@ void SymbolGraphSerializer::serializeObjCContainerRecord(
ObjCInterface->SuperClass);
}
+void SymbolGraphSerializer::serializeMacroDefinitionRecord(
+ const MacroDefinitionRecord &Record) {
+ auto Macro = serializeAPIRecord(Record);
+ if (!Macro)
+ return;
+
+ Symbols.emplace_back(std::move(*Macro));
+}
+
Object SymbolGraphSerializer::serialize() {
Object Root;
serializeObject(Root, "metadata", serializeMetadata());
@@ -601,6 +613,9 @@ Object SymbolGraphSerializer::serialize() {
for (const auto &ObjCProtocol : API.getObjCProtocols())
serializeObjCContainerRecord(*ObjCProtocol.second);
+ for (const auto &Macro : API.getMacros())
+ serializeMacroDefinitionRecord(*Macro.second);
+
Root["symbols"] = std::move(Symbols);
Root["relationhips"] = std::move(Relationships);
diff --git a/clang/test/ExtractAPI/macro_undefined.c b/clang/test/ExtractAPI/macro_undefined.c
new file mode 100644
index 0000000000000..e04712500ef93
--- /dev/null
+++ b/clang/test/ExtractAPI/macro_undefined.c
@@ -0,0 +1,281 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%/t at g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api --product-name=Macros -target arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN:
diff %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+#define HELLO 1
+#define FUNC_GEN(NAME, ...) void NAME(__VA_ARGS__);
+FUNC_GEN(foo)
+FUNC_GEN(bar, const int *, unsigned);
+#undef FUNC_GEN
+
+//--- reference.output.json.in
+{
+ "metadata": {
+ "formatVersion": {
+ "major": 0,
+ "minor": 5,
+ "patch": 3
+ },
+ "generator": "?"
+ },
+ "module": {
+ "name": "Macros",
+ "platform": {
+ "architecture": "arm64",
+ "operatingSystem": {
+ "minimumVersion": {
+ "major": 11,
+ "minor": 0,
+ "patch": 0
+ },
+ "name": "macosx"
+ },
+ "vendor": "apple"
+ }
+ },
+ "relationhips": [],
+ "symbols": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "foo"
+ },
+ {
+ "kind": "text",
+ "spelling": "()"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:@F at foo"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "objective-c.func"
+ },
+ "location": {
+ "character": 1,
+ "line": 3,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "foo"
+ }
+ ],
+ "title": "foo"
+ },
+ "parameters": {
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ }
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "bar"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "keyword",
+ "spelling": "const"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " *"
+ },
+ {
+ "kind": "internalParam",
+ "spelling": ""
+ },
+ {
+ "kind": "text",
+ "spelling": ", "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:i",
+ "spelling": "unsigned int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": ""
+ },
+ {
+ "kind": "text",
+ "spelling": ")"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:@F at bar"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "objective-c.func"
+ },
+ "location": {
+ "character": 1,
+ "line": 4,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "bar"
+ }
+ ],
+ "title": "bar"
+ },
+ "parameters": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "const"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " *"
+ },
+ {
+ "kind": "internalParam",
+ "spelling": ""
+ }
+ ],
+ "name": ""
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:i",
+ "spelling": "unsigned int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": ""
+ }
+ ],
+ "name": ""
+ }
+ ],
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ }
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "HELLO"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:input.h at 8@macro at HELLO"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "objective-c.macro"
+ },
+ "location": {
+ "character": 9,
+ "line": 1,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "HELLO"
+ }
+ ],
+ "title": "HELLO"
+ }
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/macros.c b/clang/test/ExtractAPI/macros.c
new file mode 100644
index 0000000000000..1e1e5e60906a6
--- /dev/null
+++ b/clang/test/ExtractAPI/macros.c
@@ -0,0 +1,344 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%/t at g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api --product-name=Macros -target arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN:
diff %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+#define HELLO 1
+#define WORLD 2
+#define MACRO_FUN(x) x x
+#define FUN(x, y, z) x + y + z
+#define FUNC99(x, ...)
+#define FUNGNU(x...)
+
+//--- reference.output.json.in
+{
+ "metadata": {
+ "formatVersion": {
+ "major": 0,
+ "minor": 5,
+ "patch": 3
+ },
+ "generator": "?"
+ },
+ "module": {
+ "name": "Macros",
+ "platform": {
+ "architecture": "arm64",
+ "operatingSystem": {
+ "minimumVersion": {
+ "major": 11,
+ "minor": 0,
+ "patch": 0
+ },
+ "name": "macosx"
+ },
+ "vendor": "apple"
+ }
+ },
+ "relationhips": [],
+ "symbols": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "HELLO"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:input.h at 8@macro at HELLO"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "objective-c.macro"
+ },
+ "location": {
+ "character": 9,
+ "line": 1,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "HELLO"
+ }
+ ],
+ "title": "HELLO"
+ }
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "WORLD"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:input.h at 24@macro at WORLD"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "objective-c.macro"
+ },
+ "location": {
+ "character": 9,
+ "line": 2,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "WORLD"
+ }
+ ],
+ "title": "WORLD"
+ }
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "MACRO_FUN"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "x"
+ },
+ {
+ "kind": "text",
+ "spelling": ")"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:input.h at 40@macro at MACRO_FUN"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "objective-c.macro"
+ },
+ "location": {
+ "character": 9,
+ "line": 3,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "MACRO_FUN"
+ }
+ ],
+ "title": "MACRO_FUN"
+ }
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "FUN"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "x"
+ },
+ {
+ "kind": "text",
+ "spelling": ", "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "y"
+ },
+ {
+ "kind": "text",
+ "spelling": ", "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "z"
+ },
+ {
+ "kind": "text",
+ "spelling": ")"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:input.h at 65@macro at FUN"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "objective-c.macro"
+ },
+ "location": {
+ "character": 9,
+ "line": 4,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "FUN"
+ }
+ ],
+ "title": "FUN"
+ }
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "FUNC99"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "x"
+ },
+ {
+ "kind": "text",
+ "spelling": ", ...)"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:input.h at 96@macro at FUNC99"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "objective-c.macro"
+ },
+ "location": {
+ "character": 9,
+ "line": 5,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "FUNC99"
+ }
+ ],
+ "title": "FUNC99"
+ }
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "FUNGNU"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "x"
+ },
+ {
+ "kind": "text",
+ "spelling": "...)"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:input.h at 119@macro at FUNGNU"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "objective-c.macro"
+ },
+ "location": {
+ "character": 9,
+ "line": 6,
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "FUNGNU"
+ }
+ ],
+ "title": "FUNGNU"
+ }
+ }
+ ]
+}
More information about the cfe-commits
mailing list