[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.
This commit is contained in:
Daniel Grumberg 2022-03-29 17:48:11 +01:00
parent a9909d23e9
commit 529a0570f7
9 changed files with 876 additions and 78 deletions

View File

@ -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 @@ private:
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 @@ public:
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>>;
/// 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 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>>;
/// 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 @@ public:
/// 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 @@ public:
/// \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 @@ private:
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

View File

@ -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 @@ public:
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.
///

View File

@ -123,6 +123,9 @@ private:
/// 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 = {})

View File

@ -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,
return addTopLevelRecord(ObjCInterfaces, Name, USR, Loc, Availability,
Linkage, Comment, Declaration, SubHeading,
SuperClass);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
}
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() {}

View File

@ -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;
}

View File

@ -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 @@ private:
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,
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;
}

View File

@ -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);

View File

@ -0,0 +1,281 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%/t@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@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@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@8@macro@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"
}
}
]
}

View File

@ -0,0 +1,344 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%/t@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@8@macro@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@24@macro@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@40@macro@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@65@macro@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@96@macro@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@119@macro@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"
}
}
]
}