diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -77,6 +77,7 @@ RK_Enum, RK_StructField, RK_Struct, + RK_MacroDefinition, }; private: @@ -113,7 +114,7 @@ /// 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, GVKind Kind, StringRef USR, PresumedLoc Loc, const AvailabilityInfo &Availability, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, FunctionSignature Signature) @@ -189,6 +190,17 @@ } }; +struct MacroDefinitionRecord : APIRecord { + MacroDefinitionRecord(StringRef Name, StringRef USR, PresumedLoc Loc, + DeclarationFragments DeclarationFragments) + : APIRecord(RK_MacroDefinition, Name, USR, Loc, AvailabilityInfo(), + LinkageInfo(), {}, DeclarationFragments, {}) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_MacroDefinition; + } +}; + /// APISet holds the set of API records collected from given inputs. class APISet { public: @@ -268,6 +280,17 @@ DeclarationFragments Declaration, DeclarationFragments SubHeading); + /// 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 DeclarationFragments); + /// Create and add a struct record into the API set. /// /// Note: the caller is responsible for keeping the StringRef \p Name and @@ -294,6 +317,11 @@ using StructRecordMap = llvm::MapVector>; + /// A map to store the set of MacroDefinitionRecord%s with the macro name as + /// the key. + using MacroDefinitionRecordMap = + llvm::MapVector>; + /// Get the target triple for the ExtractAPI invocation. const llvm::Triple &getTarget() const { return Target; } @@ -303,6 +331,7 @@ const GlobalRecordMap &getGlobals() const { return Globals; } const EnumRecordMap &getEnums() const { return Enums; } const StructRecordMap &getStructs() const { return Structs; } + const MacroDefinitionRecordMap &getMacros() const { return Macros; } /// Generate and store the USR of declaration \p D. /// @@ -311,6 +340,14 @@ /// \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. @@ -331,6 +368,7 @@ GlobalRecordMap Globals; EnumRecordMap Enums; StructRecordMap Structs; + MacroDefinitionRecordMap Macros; }; } // namespace extractapi diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -21,6 +21,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/Lex/MacroInfo.h" #include "llvm/ADT/StringRef.h" #include @@ -202,6 +203,13 @@ /// Build DeclarationFragments for a struct record declaration RecordDecl. static DeclarationFragments getFragmentsForStruct(const RecordDecl *); + /// 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 *); diff --git a/clang/include/clang/ExtractAPI/FrontendActions.h b/clang/include/clang/ExtractAPI/FrontendActions.h --- a/clang/include/clang/ExtractAPI/FrontendActions.h +++ b/clang/include/clang/ExtractAPI/FrontendActions.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H #define LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H +#include "clang/ExtractAPI/API.h" #include "clang/Frontend/FrontendAction.h" namespace clang { @@ -25,11 +26,15 @@ StringRef InFile) override; private: - /// The synthesized input buffer that contains all the provided input header - /// files. - std::unique_ptr Buffer; + /// A representation of the APIs this action extracts. + std::unique_ptr API; + + /// A stream to the output file of this action. + std::unique_ptr OS; + + /// The product this action is extracting API information for. + std::string ProductName; -public: /// Prepare to execute the action on the given CompilerInstance. /// /// This is called before executing the action on any inputs. This generates a @@ -37,8 +42,15 @@ /// list with it before actually executing the action. bool PrepareToExecuteAction(CompilerInstance &CI) override; + /// Called after executing the action on the synthesized input buffer. + /// + /// Note: Now that we have gathered all the API definitions to surface we can + /// emit them in this callback. + void EndSourceFileAction() override; + static std::unique_ptr CreateOutputFile(CompilerInstance &CI, StringRef InFile); + static StringRef getInputBufferName() { return ""; } }; diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -19,6 +19,7 @@ #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/Serialization/SerializerBase.h" +#include "clang/Lex/PreprocessingRecord.h" #include "llvm/Support/JSON.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/raw_ostream.h" @@ -113,6 +114,9 @@ /// Serialize a struct record. void serializeStructRecord(const StructRecord &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 --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -17,11 +17,32 @@ #include "clang/AST/CommentLexer.h" #include "clang/AST/RawCommentList.h" #include "clang/Index/USRGeneration.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/ADT/StringRef.h" #include using namespace clang::extractapi; using namespace llvm; +namespace { + +template +RecordTy * +addTopLevelRecord(MapVector> &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(Name, std::forward(CtorArgs)...); + + return Result.first->second.get(); +} + +} // namespace + GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR, PresumedLoc Loc, const AvailabilityInfo &Availability, @@ -29,15 +50,8 @@ 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( - 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, Kind, USR, Loc, Availability, Linkage, + Comment, Fragments, SubHeading, Signature); } GlobalRecord * @@ -73,14 +87,8 @@ 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( - 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 +107,15 @@ 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( - 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); +} + +MacroDefinitionRecord * +APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc, + DeclarationFragments DeclarationFragments) { + auto *MD = addTopLevelRecord(Macros, Name, USR, Loc, DeclarationFragments); + return MD; } StringRef APISet::recordUSR(const Decl *D) { @@ -115,6 +124,13 @@ 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 {}; diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -450,7 +450,37 @@ 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; } diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -28,6 +28,11 @@ #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/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" @@ -41,9 +46,8 @@ /// information. class ExtractAPIVisitor : public RecursiveASTVisitor { public: - explicit ExtractAPIVisitor(ASTContext &Context) - : Context(Context), - API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {} + ExtractAPIVisitor(ASTContext &Context, APISet &API) + : Context(Context), API(API) {} const APISet &getAPI() const { return API; } @@ -304,42 +308,106 @@ } ASTContext &Context; - APISet API; + APISet &API; }; class ExtractAPIConsumer : public ASTConsumer { public: - ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, - std::unique_ptr OS) - : Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {} + ExtractAPIConsumer(ASTContext &Context, APISet &API) + : Visitor(Context, API) {} void HandleTranslationUnit(ASTContext &Context) override { // Use ExtractAPIVisitor to traverse symbol declarations in the context. Visitor.TraverseDecl(Context.getTranslationUnitDecl()); - - // Setup a SymbolGraphSerializer to write out collected API information in - // the Symbol Graph format. - // FIXME: Make the kind of APISerializer configurable. - SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName); - SGSerializer.serialize(*OS); } private: ExtractAPIVisitor Visitor; - std::string ProductName; - std::unique_ptr OS; +}; + +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( + API.copyString(Name), USR, Loc, + DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD)); + } + + 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 PendingMacros; }; } // namespace std::unique_ptr ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - std::unique_ptr OS = CreateOutputFile(CI, InFile); + OS = CreateOutputFile(CI, InFile); if (!OS) return nullptr; - return std::make_unique( - CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName, - std::move(OS)); + + ProductName = CI.getFrontendOpts().ProductName; + + // Now that we have enough information about the language options and the + // target triple, let's create the APISet before anyone uses it. + API = std::make_unique(CI.getTarget().getTriple(), CI.getLangOpts()); + + // Register preprocessor callbacks that will add macro definitions to API. + CI.getPreprocessor().addPPCallbacks( + std::make_unique(CI.getSourceManager(), *API)); + + return std::make_unique(CI.getASTContext(), *API); } bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { @@ -361,16 +429,32 @@ 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; } +void ExtractAPIAction::EndSourceFileAction() { + if (!OS) + return; + + // Setup a SymbolGraphSerializer to write out collected API information in + // the Symbol Graph format. + // FIXME: Make the kind of APISerializer configurable. + SymbolGraphSerializer SGSerializer(*API, ProductName); + SGSerializer.serialize(*OS); + OS->flush(); +} + std::unique_ptr ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { std::unique_ptr OS = diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -14,6 +14,7 @@ #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" #include "clang/Basic/Version.h" #include "clang/ExtractAPI/API.h" +#include "clang/Lex/PreprocessingRecord.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" #include "llvm/Support/VersionTuple.h" @@ -375,6 +376,9 @@ Kind["identifier"] = AddLangPrefix("struct"); Kind["displayName"] = "Structure"; break; + case APIRecord::RK_MacroDefinition: + Kind["identifier"] = AddLangPrefix("macro"); + Kind["displayName"] = "Macro"; } return Kind; @@ -499,6 +503,15 @@ } } +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()); @@ -516,6 +529,9 @@ for (const auto &Struct : API.getStructs()) serializeStructRecord(*Struct.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 --- /dev/null +++ b/clang/test/ExtractAPI/macro_undefined.c @@ -0,0 +1,275 @@ +// 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 *, size_t); +#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": "c", + "precise": "c:@F@foo" + }, + "kind": { + "displayName": "Function", + "identifier": "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": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "size_t" + }, + { + "kind": "text", + "spelling": ")" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@F@bar" + }, + "kind": { + "displayName": "Function", + "identifier": "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": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "size_t" + } + ], + "name": "size_t" + } + ], + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + } + }, + { + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "#define" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "HELLO" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:input.h@8@macro@HELLO" + }, + "kind": { + "displayName": "Macro", + "identifier": "c.macro" + }, + "location": { + "character": 9, + "line": 1, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "title": "HELLO" + } + } + ] +} diff --git a/clang/test/ExtractAPI/macros.c b/clang/test/ExtractAPI/macros.c new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/macros.c @@ -0,0 +1,308 @@ +// 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": "c", + "precise": "c:input.h@8@macro@HELLO" + }, + "kind": { + "displayName": "Macro", + "identifier": "c.macro" + }, + "location": { + "character": 9, + "line": 1, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "title": "HELLO" + } + }, + { + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "#define" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "WORLD" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:input.h@24@macro@WORLD" + }, + "kind": { + "displayName": "Macro", + "identifier": "c.macro" + }, + "location": { + "character": 9, + "line": 2, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "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": "c", + "precise": "c:input.h@40@macro@MACRO_FUN" + }, + "kind": { + "displayName": "Macro", + "identifier": "c.macro" + }, + "location": { + "character": 9, + "line": 3, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "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": "c", + "precise": "c:input.h@65@macro@FUN" + }, + "kind": { + "displayName": "Macro", + "identifier": "c.macro" + }, + "location": { + "character": 9, + "line": 4, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "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": "c", + "precise": "c:input.h@96@macro@FUNC99" + }, + "kind": { + "displayName": "Macro", + "identifier": "c.macro" + }, + "location": { + "character": 9, + "line": 5, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "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": "c", + "precise": "c:input.h@119@macro@FUNGNU" + }, + "kind": { + "displayName": "Macro", + "identifier": "c.macro" + }, + "location": { + "character": 9, + "line": 6, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "title": "FUNGNU" + } + } + ] +}