dang updated this revision to Diff 419197.
dang marked 2 inline comments as done.
dang added a comment.

Rebase on top of latest changes


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D122648/new/

https://reviews.llvm.org/D122648

Files:
  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
  clang/test/ExtractAPI/macro_undefined.c
  clang/test/ExtractAPI/macros.c

Index: clang/test/ExtractAPI/macros.c
===================================================================
--- /dev/null
+++ 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"
+      }
+    }
+  ]
+}
Index: clang/test/ExtractAPI/macro_undefined.c
===================================================================
--- /dev/null
+++ 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"
+      }
+    }
+  ]
+}
Index: clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
===================================================================
--- clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ 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);
 
Index: clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
===================================================================
--- clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ 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<PendingMacro> PendingMacros;
+};
+
 } // namespace
 
 std::unique_ptr<ASTConsumer>
@@ -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<MacroCallback>(CI.getSourceManager(), *API));
+
   return std::make_unique<ExtractAPIConsumer>(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;
 }
 
Index: clang/lib/ExtractAPI/DeclarationFragments.cpp
===================================================================
--- clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ 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;
+}
Index: clang/lib/ExtractAPI/API.cpp
===================================================================
--- clang/lib/ExtractAPI/API.cpp
+++ 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 @@
                                 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 @@
                             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 @@
                                 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 @@
     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 @@
     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 @@
   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() {}
Index: clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
===================================================================
--- clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ 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 = {})
Index: clang/include/clang/ExtractAPI/DeclarationFragments.h
===================================================================
--- clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ 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 @@
   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.
   ///
Index: clang/include/clang/ExtractAPI/API.h
===================================================================
--- clang/include/clang/ExtractAPI/API.h
+++ 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 @@
 /// \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 @@
     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<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 @@
   /// 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 @@
   /// \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<GlobalRecord> Globals;
+  RecordMap<EnumRecord> Enums;
+  RecordMap<StructRecord> Structs;
+  RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
+  RecordMap<ObjCProtocolRecord> ObjCProtocols;
+  RecordMap<MacroDefinitionRecord> Macros;
 };
 
 } // namespace extractapi
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to