[clang] 8e9145e - [clang][ExtractAPI] Add --emit-symbol-graph option
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 3 05:07:45 PDT 2023
Author: Ankur
Date: 2023-07-03T17:32:30+05:30
New Revision: 8e9145e4314202b960dd883e6a7b21707ed5c176
URL: https://github.com/llvm/llvm-project/commit/8e9145e4314202b960dd883e6a7b21707ed5c176
DIFF: https://github.com/llvm/llvm-project/commit/8e9145e4314202b960dd883e6a7b21707ed5c176.diff
LOG: [clang][ExtractAPI] Add --emit-symbol-graph option
Add new --emit-symbol-graph=<DIR> option which generates ExtractAPI symbol
graph information of .c/.m files on regular compilation job and put them in
the provided "DIR" directory.
Reviewed By: dang
Differential Revision: https://reviews.llvm.org/D152356
Added:
clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
clang/test/ExtractAPI/emit-symbol-graph/multi_file.c
clang/test/ExtractAPI/emit-symbol-graph/single_file.c
Modified:
clang/include/clang/Driver/Options.td
clang/include/clang/ExtractAPI/FrontendActions.h
clang/include/clang/Frontend/FrontendOptions.h
clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c3242ac2a804b8..f74a589d1baaa6 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1198,6 +1198,9 @@ def extract_api : Flag<["-"], "extract-api">, Flags<[CC1Option]>, Group<Action_G
HelpText<"Extract API information">;
def product_name_EQ: Joined<["--"], "product-name=">, Flags<[CC1Option]>,
MarshallingInfoString<FrontendOpts<"ProductName">>;
+def emit_symbol_graph_EQ: JoinedOrSeparate<["--"], "emit-symbol-graph=">, Flags<[CC1Option]>,
+ HelpText<"Generate Extract API information as a side effect of compilation.">,
+ MarshallingInfoString<FrontendOpts<"SymbolGraphOutputDir">>;
def extract_api_ignores_EQ: CommaJoined<["--"], "extract-api-ignores=">, Flags<[CC1Option]>,
HelpText<"Comma separated list of files containing a new line separated list of API symbols to ignore when extracting API information.">,
MarshallingInfoStringVector<FrontendOpts<"ExtractAPIIgnoresFileList">>;
diff --git a/clang/include/clang/ExtractAPI/ExtractAPIActionBase.h b/clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
new file mode 100644
index 00000000000000..ac4f391db5f14a
--- /dev/null
+++ b/clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
@@ -0,0 +1,54 @@
+//===- ExtractAPI/ExtractAPIActionBase.h -----------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines the ExtractAPIActionBase class.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_EXTRACTAPI_ACTION_BASE_H
+#define LLVM_CLANG_EXTRACTAPI_ACTION_BASE_H
+
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/APIIgnoresList.h"
+
+namespace clang {
+
+/// Base class to be used by front end actions to generate ExtarctAPI info
+///
+/// Deriving from this class equips an action with all the necessary tools to
+/// generate ExractAPI information in form of symbol-graphs
+class ExtractAPIActionBase {
+protected:
+ /// A representation of the APIs this action extracts.
+ std::unique_ptr<extractapi::APISet> API;
+
+ /// A stream to the output file of this action.
+ std::unique_ptr<raw_pwrite_stream> OS;
+
+ /// The product this action is extracting API information for.
+ std::string ProductName;
+
+ /// The synthesized input buffer that contains all the provided input header
+ /// files.
+ std::unique_ptr<llvm::MemoryBuffer> Buffer;
+
+ /// The list of symbols to ignore during serialization
+ extractapi::APIIgnoresList IgnoresList;
+
+ /// Implements EndSourceFileAction for Symbol-Graph generation
+ ///
+ /// Use the serializer to generate output symbol graph files from
+ /// the information gathered during the execution of Action.
+ void ImplEndSourceFileAction();
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_EXTRACTAPI_ACTION_BASE_H
diff --git a/clang/include/clang/ExtractAPI/FrontendActions.h b/clang/include/clang/ExtractAPI/FrontendActions.h
index e946b33abbd984..c67864aac9af9c 100644
--- a/clang/include/clang/ExtractAPI/FrontendActions.h
+++ b/clang/include/clang/ExtractAPI/FrontendActions.h
@@ -7,41 +7,27 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// This file defines the ExtractAPIAction frontend action.
+/// This file defines the ExtractAPIAction and WrappingExtractAPIAction frontend
+/// actions.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H
#define LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H
-#include "clang/ExtractAPI/API.h"
-#include "clang/ExtractAPI/APIIgnoresList.h"
+#include "clang/ExtractAPI/ExtractAPIActionBase.h"
#include "clang/Frontend/FrontendAction.h"
namespace clang {
/// ExtractAPIAction sets up the output file and creates the ExtractAPIVisitor.
-class ExtractAPIAction : public ASTFrontendAction {
+class ExtractAPIAction : public ASTFrontendAction,
+ private ExtractAPIActionBase {
protected:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override;
private:
- /// A representation of the APIs this action extracts.
- std::unique_ptr<extractapi::APISet> API;
-
- /// A stream to the output file of this action.
- std::unique_ptr<raw_pwrite_stream> OS;
-
- /// The product this action is extracting API information for.
- std::string ProductName;
-
- /// The synthesized input buffer that contains all the provided input header
- /// files.
- std::unique_ptr<llvm::MemoryBuffer> Buffer;
-
- /// The list of symbols to ignore during serialization
- extractapi::APIIgnoresList IgnoresList;
/// The input file originally provided on the command line.
///
@@ -62,10 +48,46 @@ class ExtractAPIAction : public ASTFrontendAction {
/// emit them in this callback.
void EndSourceFileAction() override;
+ static StringRef getInputBufferName() { return "<extract-api-includes>"; }
+
static std::unique_ptr<llvm::raw_pwrite_stream>
CreateOutputFile(CompilerInstance &CI, StringRef InFile);
+};
- static StringRef getInputBufferName() { return "<extract-api-includes>"; }
+/// Wrap ExtractAPIAction on top of a pre-existing action
+///
+/// Used when the ExtractAPI action needs to be executed as a side effect of a
+/// regular compilation job. Unlike ExtarctAPIAction, this is meant to be used
+/// on regular source files ( .m , .c files) instead of header files
+class WrappingExtractAPIAction : public WrapperFrontendAction,
+ private ExtractAPIActionBase {
+public:
+ WrappingExtractAPIAction(std::unique_ptr<FrontendAction> WrappedAction)
+ : WrapperFrontendAction(std::move(WrappedAction)) {}
+
+protected:
+ /// Create ExtractAPI consumer multiplexed on another consumer.
+ ///
+ /// This allows us to execute ExtractAPI action while on top of
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override;
+
+private:
+ /// Flag to check if the wrapper front end action's consumer is
+ /// craeted or not
+ bool CreatedASTConsumer = false;
+
+ void EndSourceFile() override { FrontendAction::EndSourceFile(); }
+
+ /// Called after executing the action on the synthesized input buffer.
+ ///
+ /// Executes both Wrapper and ExtractAPIBase end source file
+ /// actions. This is the place where all the gathered symbol graph
+ /// information is emited.
+ void EndSourceFileAction() override;
+
+ static std::unique_ptr<llvm::raw_pwrite_stream>
+ CreateOutputFile(CompilerInstance &CI, StringRef InFile);
};
} // namespace clang
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 40aa144a0e3698..3132c11705d3d8 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -456,6 +456,12 @@ class FrontendOptions {
// ignore when extracting documentation.
std::vector<std::string> ExtractAPIIgnoresFileList;
+ // Currently this is only used as part of the `-emit-symbol-graph`
+ // action.
+ // Location of output directory where symbol graph information would
+ // be dumped
+ std::string SymbolGraphOutputDir;
+
/// Args to pass to the plugins
std::map<std::string, std::vector<std::string>> PluginArgs;
diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index c27b2d025374a3..eb533a934367f4 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -28,12 +28,14 @@
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
@@ -245,6 +247,20 @@ struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
LocationFileChecker &LCF;
};
+class WrappingExtractAPIConsumer : public ASTConsumer {
+public:
+ WrappingExtractAPIConsumer(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());
+ }
+
+private:
+ ExtractAPIVisitor<> Visitor;
+};
+
class ExtractAPIConsumer : public ASTConsumer {
public:
ExtractAPIConsumer(ASTContext &Context,
@@ -263,9 +279,8 @@ class ExtractAPIConsumer : public ASTConsumer {
class MacroCallback : public PPCallbacks {
public:
- MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
- Preprocessor &PP)
- : SM(SM), LCF(LCF), API(API), PP(PP) {}
+ MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP)
+ : SM(SM), API(API), PP(PP) {}
void MacroDefined(const Token &MacroNameToken,
const MacroDirective *MD) override {
@@ -305,7 +320,7 @@ class MacroCallback : public PPCallbacks {
if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
continue;
- if (!LCF(PM.MacroNameToken.getLocation()))
+ if (!shouldMacroBeIncluded(PM))
continue;
StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
@@ -323,7 +338,7 @@ class MacroCallback : public PPCallbacks {
PendingMacros.clear();
}
-private:
+protected:
struct PendingMacro {
Token MacroNameToken;
const MacroDirective *MD;
@@ -332,18 +347,58 @@ class MacroCallback : public PPCallbacks {
: MacroNameToken(MacroNameToken), MD(MD) {}
};
+ virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; }
+
const SourceManager &SM;
- LocationFileChecker &LCF;
APISet &API;
Preprocessor &PP;
llvm::SmallVector<PendingMacro> PendingMacros;
};
+class APIMacroCallback : public MacroCallback {
+public:
+ APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP,
+ LocationFileChecker &LCF)
+ : MacroCallback(SM, API, PP), LCF(LCF) {}
+
+ bool shouldMacroBeIncluded(const PendingMacro &PM) override {
+ // Do not include macros from external files
+ return LCF(PM.MacroNameToken.getLocation());
+ }
+
+private:
+ LocationFileChecker &LCF;
+};
+
} // namespace
+void ExtractAPIActionBase::ImplEndSourceFileAction() {
+ 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, IgnoresList);
+ SGSerializer.serialize(*OS);
+ OS.reset();
+}
+
+std::unique_ptr<raw_pwrite_stream>
+ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
+ std::unique_ptr<raw_pwrite_stream> OS;
+ OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile,
+ /*Extension=*/"json",
+ /*RemoveFileOnSignal=*/false);
+ if (!OS)
+ return nullptr;
+ return OS;
+}
+
std::unique_ptr<ASTConsumer>
ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
OS = CreateOutputFile(CI, InFile);
+
if (!OS)
return nullptr;
@@ -357,8 +412,8 @@ ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
- CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
- CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
+ CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>(
+ CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF));
// Do not include location in anonymous decls.
PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
@@ -438,23 +493,88 @@ bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
return true;
}
-void ExtractAPIAction::EndSourceFileAction() {
+void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); }
+
+std::unique_ptr<ASTConsumer>
+WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) {
+ auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+ if (!OtherConsumer)
+ return nullptr;
+
+ CreatedASTConsumer = true;
+
+ OS = CreateOutputFile(CI, InFile);
if (!OS)
- return;
+ return nullptr;
- // Setup a SymbolGraphSerializer to write out collected API information in
- // the Symbol Graph format.
- // FIXME: Make the kind of APISerializer configurable.
- SymbolGraphSerializer SGSerializer(*API, IgnoresList);
- SGSerializer.serialize(*OS);
- OS.reset();
+ auto 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<APISet>(
+ CI.getTarget().getTriple(),
+ CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
+
+ CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
+ CI.getSourceManager(), *API, CI.getPreprocessor()));
+
+ // Do not include location in anonymous decls.
+ PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
+ Policy.AnonymousTagLocations = false;
+ CI.getASTContext().setPrintingPolicy(Policy);
+
+ if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
+ llvm::handleAllErrors(
+ APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
+ CI.getFileManager())
+ .moveInto(IgnoresList),
+ [&CI](const IgnoresFileNotFound &Err) {
+ CI.getDiagnostics().Report(
+ diag::err_extract_api_ignores_file_not_found)
+ << Err.Path;
+ });
+ }
+
+ auto WrappingConsumer =
+ std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API);
+ std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+ Consumers.push_back(std::move(OtherConsumer));
+ Consumers.push_back(std::move(WrappingConsumer));
+
+ return std::make_unique<MultiplexConsumer>(std::move(Consumers));
+}
+
+void WrappingExtractAPIAction::EndSourceFileAction() {
+ // Invoke wrapped action's method.
+ WrapperFrontendAction::EndSourceFileAction();
+
+ if (CreatedASTConsumer) {
+ ImplEndSourceFileAction();
+ }
}
std::unique_ptr<raw_pwrite_stream>
-ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
- std::unique_ptr<raw_pwrite_stream> OS =
- CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
- /*RemoveFileOnSignal=*/false);
+WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI,
+ StringRef InFile) {
+ std::unique_ptr<raw_pwrite_stream> OS;
+ std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir;
+
+ // The symbol graphs need to be generated as a side effect of regular
+ // compilation so the output should be dumped in the directory provided with
+ // the command line option.
+ llvm::SmallString<128> OutFilePath(OutputDir);
+ auto Seperator = llvm::sys::path::get_separator();
+ auto Infilename = llvm::sys::path::filename(InFile);
+ OutFilePath.append({Seperator, Infilename});
+ llvm::sys::path::replace_extension(OutFilePath, "json");
+ // StringRef outputFilePathref = *OutFilePath;
+
+ // don't use the default output file
+ OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false,
+ /*RemoveFileOnSignal=*/true,
+ /*UseTemporary=*/true,
+ /*CreateMissingDirectories=*/true);
if (!OS)
return nullptr;
return OS;
diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 47157ca5092b75..310f67774a6605 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -178,6 +178,14 @@ CreateFrontendAction(CompilerInstance &CI) {
}
#endif
+ // Wrap the base FE action in an extract api action to generate
+ // symbol graph as a biproduct of comilation ( enabled with
+ // --emit-symbol-graph option )
+ if (!FEOpts.SymbolGraphOutputDir.empty()) {
+ CI.getCodeGenOpts().ClearASTBeforeBackend = false;
+ Act = std::make_unique<WrappingExtractAPIAction>(std::move(Act));
+ }
+
// If there are any AST files to merge, create a frontend action
// adaptor to perform the merge.
if (!FEOpts.ASTMergeFiles.empty())
diff --git a/clang/test/ExtractAPI/emit-symbol-graph/multi_file.c b/clang/test/ExtractAPI/emit-symbol-graph/multi_file.c
new file mode 100644
index 00000000000000..1b44cfbdeb75a9
--- /dev/null
+++ b/clang/test/ExtractAPI/emit-symbol-graph/multi_file.c
@@ -0,0 +1,763 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.main.json.in >> %t/reference.main.json
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.test.json.in >> %t/reference.test.json
+// RUN: %clang_cc1 %t/test.c %t/main.c --emit-symbol-graph=%t/SymbolGraphs --product-name=multifile_test -triple=x86_64-apple-macosx12.0.0
+
+// Test main.json
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/SymbolGraphs/main.json > %t/output-normalized.json
+// RUN:
diff %t/reference.main.json %t/output-normalized.json
+
+// Test test.json
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/SymbolGraphs/test.json > %t/output-normalized.json
+// RUN:
diff %t/reference.test.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- test.h
+#ifndef TEST_H
+#define TEST_H
+
+#define testmarcro1 32
+#define testmacro2 42
+
+int testfunc (int param1, int param2);
+void testfunc2 ();
+#endif /* TEST_H */
+
+//--- test.c
+#include "test.h"
+
+int testfunc(int param1, int param2) { return param1 + param2; }
+
+void testfunc2() {}
+
+//--- main.c
+#include "test.h"
+
+int main ()
+{
+ testfunc2();
+ return 0;
+}
+
+//--- reference.main.json.in
+{
+ "metadata": {
+ "formatVersion": {
+ "major": 0,
+ "minor": 5,
+ "patch": 3
+ },
+ "generator": "?"
+ },
+ "module": {
+ "name": "multifile_test",
+ "platform": {
+ "architecture": "x86_64",
+ "operatingSystem": {
+ "name": "macosx"
+ },
+ "vendor": "apple"
+ }
+ },
+ "relationships": [],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testfunc"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param1"
+ },
+ {
+ "kind": "text",
+ "spelling": ", "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param2"
+ },
+ {
+ "kind": "text",
+ "spelling": ");"
+ }
+ ],
+ "functionSignature": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param1"
+ }
+ ],
+ "name": "param1"
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param2"
+ }
+ ],
+ "name": "param2"
+ }
+ ],
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:@F at testfunc"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "c.func"
+ },
+ "location": {
+ "position": {
+ "character": 5,
+ "line": 7
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc"
+ }
+ ],
+ "title": "testfunc"
+ },
+ "pathComponents": [
+ "testfunc"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testfunc2"
+ },
+ {
+ "kind": "text",
+ "spelling": "();"
+ }
+ ],
+ "functionSignature": {
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:@F at testfunc2"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "c.func"
+ },
+ "location": {
+ "position": {
+ "character": 6,
+ "line": 8
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc2"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc2"
+ }
+ ],
+ "title": "testfunc2"
+ },
+ "pathComponents": [
+ "testfunc2"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "main"
+ },
+ {
+ "kind": "text",
+ "spelling": "();"
+ }
+ ],
+ "functionSignature": {
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:@F at main"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "c.func"
+ },
+ "location": {
+ "position": {
+ "character": 5,
+ "line": 3
+ },
+ "uri": "file://INPUT_DIR/main.c"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "main"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "main"
+ }
+ ],
+ "title": "main"
+ },
+ "pathComponents": [
+ "main"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testmarcro1"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:test.h at 39@macro at testmarcro1"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "c.macro"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 4
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testmarcro1"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testmarcro1"
+ }
+ ],
+ "title": "testmarcro1"
+ },
+ "pathComponents": [
+ "testmarcro1"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testmacro2"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:test.h at 62@macro at testmacro2"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "c.macro"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 5
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testmacro2"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testmacro2"
+ }
+ ],
+ "title": "testmacro2"
+ },
+ "pathComponents": [
+ "testmacro2"
+ ]
+ }
+ ]
+}
+//--- reference.test.json.in
+{
+ "metadata": {
+ "formatVersion": {
+ "major": 0,
+ "minor": 5,
+ "patch": 3
+ },
+ "generator": "?"
+ },
+ "module": {
+ "name": "multifile_test",
+ "platform": {
+ "architecture": "x86_64",
+ "operatingSystem": {
+ "name": "macosx"
+ },
+ "vendor": "apple"
+ }
+ },
+ "relationships": [],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testfunc"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param1"
+ },
+ {
+ "kind": "text",
+ "spelling": ", "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param2"
+ },
+ {
+ "kind": "text",
+ "spelling": ");"
+ }
+ ],
+ "functionSignature": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param1"
+ }
+ ],
+ "name": "param1"
+ },
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "param2"
+ }
+ ],
+ "name": "param2"
+ }
+ ],
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:@F at testfunc"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "c.func"
+ },
+ "location": {
+ "position": {
+ "character": 5,
+ "line": 7
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc"
+ }
+ ],
+ "title": "testfunc"
+ },
+ "pathComponents": [
+ "testfunc"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testfunc2"
+ },
+ {
+ "kind": "text",
+ "spelling": "();"
+ }
+ ],
+ "functionSignature": {
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:@F at testfunc2"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "c.func"
+ },
+ "location": {
+ "position": {
+ "character": 6,
+ "line": 8
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc2"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testfunc2"
+ }
+ ],
+ "title": "testfunc2"
+ },
+ "pathComponents": [
+ "testfunc2"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testmarcro1"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:test.h at 39@macro at testmarcro1"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "c.macro"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 4
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testmarcro1"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testmarcro1"
+ }
+ ],
+ "title": "testmarcro1"
+ },
+ "pathComponents": [
+ "testmarcro1"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "testmacro2"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:test.h at 62@macro at testmacro2"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "c.macro"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 5
+ },
+ "uri": "file://INPUT_DIR/test.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "testmacro2"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "testmacro2"
+ }
+ ],
+ "title": "testmacro2"
+ },
+ "pathComponents": [
+ "testmacro2"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/emit-symbol-graph/single_file.c b/clang/test/ExtractAPI/emit-symbol-graph/single_file.c
new file mode 100644
index 00000000000000..aa2a5353ae980b
--- /dev/null
+++ b/clang/test/ExtractAPI/emit-symbol-graph/single_file.c
@@ -0,0 +1,213 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 %t/main.c --emit-symbol-graph=%t/SymbolGraphs --product-name=basicfile -triple=x86_64-apple-macosx12.0.0
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/SymbolGraphs/main.json >> %t/output-normalized.json
+// RUN:
diff %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- main.c
+#define TESTMACRO1 2
+#define TESTMARCRO2 5
+
+int main ()
+{
+ return 0;
+}
+
+
+//--- reference.output.json.in
+{
+ "metadata": {
+ "formatVersion": {
+ "major": 0,
+ "minor": 5,
+ "patch": 3
+ },
+ "generator": "?"
+ },
+ "module": {
+ "name": "basicfile",
+ "platform": {
+ "architecture": "x86_64",
+ "operatingSystem": {
+ "name": "macosx"
+ },
+ "vendor": "apple"
+ }
+ },
+ "relationships": [],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "main"
+ },
+ {
+ "kind": "text",
+ "spelling": "();"
+ }
+ ],
+ "functionSignature": {
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:@F at main"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "c.func"
+ },
+ "location": {
+ "position": {
+ "character": 5,
+ "line": 4
+ },
+ "uri": "file://INPUT_DIR/main.c"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "main"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "main"
+ }
+ ],
+ "title": "main"
+ },
+ "pathComponents": [
+ "main"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "TESTMACRO1"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:main.c at 8@macro at TESTMACRO1"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "c.macro"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/main.c"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "TESTMACRO1"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "TESTMACRO1"
+ }
+ ],
+ "title": "TESTMACRO1"
+ },
+ "pathComponents": [
+ "TESTMACRO1"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "#define"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "TESTMARCRO2"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c",
+ "precise": "c:main.c at 29@macro at TESTMARCRO2"
+ },
+ "kind": {
+ "displayName": "Macro",
+ "identifier": "c.macro"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 2
+ },
+ "uri": "file://INPUT_DIR/main.c"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "TESTMARCRO2"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "TESTMARCRO2"
+ }
+ ],
+ "title": "TESTMARCRO2"
+ },
+ "pathComponents": [
+ "TESTMARCRO2"
+ ]
+ }
+ ]
+}
More information about the cfe-commits
mailing list