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 @@ -30,6 +30,7 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include +#include namespace clang { namespace extractapi { @@ -49,6 +50,9 @@ /// \endcode using DocComment = std::vector; +// 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 @@ RK_ObjCMethod, RK_ObjCInterface, RK_ObjCProtocol, + RK_MacroDefinition, }; private: @@ -119,10 +124,11 @@ /// 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 @@ 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 @@ DeclarationFragments Declaration, DeclarationFragments SubHeading); - /// A map to store the set of GlobalRecord%s with the declaration name as the - /// key. - using GlobalRecordMap = - llvm::MapVector>; - - /// A map to store the set of EnumRecord%s with the declaration name as the - /// key. - using EnumRecordMap = llvm::MapVector>; - - /// A map to store the set of StructRecord%s with the declaration name as the - /// key. - using StructRecordMap = - llvm::MapVector>; - - /// A map to store the set of ObjCInterfaceRecord%s with the declaration name - /// as the key. - using ObjCInterfaceRecordMap = - llvm::MapVector>; - - /// A map to store the set of ObjCProtocolRecord%s with the declaration name - /// as the key. - using ObjCProtocolRecordMap = - llvm::MapVector>; + /// 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 ::value>> + using RecordMap = llvm::MapVector>; /// Get the target triple for the ExtractAPI invocation. const llvm::Triple &getTarget() const { return Target; } @@ -560,15 +576,16 @@ /// 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 &getGlobals() const { return Globals; } + const RecordMap &getEnums() const { return Enums; } + const RecordMap &getStructs() const { return Structs; } + const RecordMap &getObjCInterfaces() const { return ObjCInterfaces; } - const ObjCProtocolRecordMap &getObjCProtocols() const { + const RecordMap &getObjCProtocols() const { return ObjCProtocols; } + const RecordMap &getMacros() const { return Macros; } /// Generate and store the USR of declaration \p D. /// @@ -577,6 +594,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. @@ -594,11 +619,12 @@ const llvm::Triple Target; const Language Lang; - GlobalRecordMap Globals; - EnumRecordMap Enums; - StructRecordMap Structs; - ObjCInterfaceRecordMap ObjCInterfaces; - ObjCProtocolRecordMap ObjCProtocols; + RecordMap Globals; + RecordMap Enums; + RecordMap Structs; + RecordMap ObjCInterfaces; + RecordMap ObjCProtocols; + RecordMap 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 @@ -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 @@ -222,12 +223,22 @@ 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 --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -123,6 +123,9 @@ /// 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 --- 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 using namespace clang::extractapi; using namespace llvm; +namespace { + +template +RecordTy *addTopLevelRecord(APISet::RecordMap &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 +48,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, USR, Loc, Availability, Linkage, + Comment, Fragments, SubHeading, Kind, Signature); } GlobalRecord * @@ -73,14 +85,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 +105,8 @@ 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); } ObjCInterfaceRecord *APISet::addObjCInterface( @@ -114,15 +114,9 @@ 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( - 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 @@ 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( - 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 @@ 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 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 --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -466,7 +466,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; } @@ -699,3 +729,11 @@ 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 --- 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 @@ 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 PendingMacros; +}; + } // namespace std::unique_ptr @@ -522,6 +591,10 @@ 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(CI.getSourceManager(), *API)); + return std::make_unique(CI.getASTContext(), *API); } @@ -544,13 +617,17 @@ 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 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -397,6 +397,9 @@ 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 @@ 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 @@ 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 --- /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@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" + } + } + ] +} 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,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" + } + } + ] +}