diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1080,6 +1080,8 @@ HelpText<"Start emitting warnings for unused driver arguments">; def interface_stub_version_EQ : JoinedOrSeparate<["-"], "interface-stub-version=">, Flags<[CC1Option]>; def exported__symbols__list : Separate<["-"], "exported_symbols_list">; +def extract_api : Flag<["-"], "extract-api">, Flags<[CC1Option]>, Group, + HelpText<"Extract API information">; def e : JoinedOrSeparate<["-"], "e">, Flags<[LinkerInput]>, Group; def fmax_tokens_EQ : Joined<["-"], "fmax-tokens=">, Group, Flags<[CC1Option]>, HelpText<"Max total number of preprocessed tokens for -Wmax-tokens.">, diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -100,4 +100,5 @@ TYPE("dependencies", Dependencies, INVALID, "d", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("cuda-fatbin", CUDA_FATBIN, INVALID, "fatbin", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("api-information", API_INFO, INVALID, "json", phases::Compile) TYPE("none", Nothing, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h --- a/clang/include/clang/Frontend/FrontendActions.h +++ b/clang/include/clang/Frontend/FrontendActions.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_FRONTEND_FRONTENDACTIONS_H #include "clang/Frontend/FrontendAction.h" +#include #include #include @@ -270,6 +271,12 @@ bool usesPreprocessorOnly() const override { return true; } }; +class ExtractAPIAction : public ASTFrontendAction { +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; +}; + //===----------------------------------------------------------------------===// // Preprocessor Actions //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -75,6 +75,9 @@ /// Emit a .o file. EmitObj, + // Extract API information + ExtractAPI, + /// Parse and apply any fixits to the source. FixIt, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -62,6 +62,7 @@ #include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/Tool.h" #include "clang/Driver/ToolChain.h" +#include "clang/Driver/Types.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" @@ -326,7 +327,8 @@ (PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) || (PhaseArg = DAL.getLastArg(options::OPT__migrate)) || (PhaseArg = DAL.getLastArg(options::OPT__analyze)) || - (PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) { + (PhaseArg = DAL.getLastArg(options::OPT_emit_ast)) || + (PhaseArg = DAL.getLastArg(options::OPT_extract_api))) { FinalPhase = phases::Compile; // -S only runs up to the backend. @@ -4069,7 +4071,8 @@ OutputTy = types::TY_ModuleFile; } - if (Args.hasArg(options::OPT_fsyntax_only)) { + if (Args.hasArg(options::OPT_fsyntax_only) || + Args.hasArg(options::OPT_extract_api)) { // Syntax checks should not emit a PCH file OutputTy = types::TY_Nothing; } @@ -4097,6 +4100,8 @@ return C.MakeAction(Input, types::TY_ModuleFile); if (Args.hasArg(options::OPT_verify_pch)) return C.MakeAction(Input, types::TY_Nothing); + if (Args.hasArg(options::OPT_extract_api)) + return C.MakeAction(Input, types::TY_API_INFO); return C.MakeAction(Input, types::TY_LLVM_BC); } case phases::Backend: { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -33,6 +33,7 @@ #include "clang/Driver/InputInfo.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" +#include "clang/Driver/Types.h" #include "clang/Driver/XRayArgs.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/llvm-config.h" @@ -4597,6 +4598,8 @@ } else if (JA.getType() == types::TY_RewrittenLegacyObjC) { CmdArgs.push_back("-rewrite-objc"); rewriteKind = RK_Fragile; + } else if (JA.getType() == types::TY_API_INFO) { + CmdArgs.push_back("-extract-api"); } else { assert(JA.getType() == types::TY_PP_Asm && "Unexpected output type!"); } diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp --- a/clang/lib/Driver/Types.cpp +++ b/clang/lib/Driver/Types.cpp @@ -143,6 +143,7 @@ case TY_CXXModule: case TY_PP_CXXModule: case TY_AST: case TY_ModuleFile: case TY_PCH: case TY_LLVM_IR: case TY_LLVM_BC: + case TY_API_INFO: return true; } } diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -20,6 +20,7 @@ DependencyFile.cpp DependencyGraph.cpp DiagnosticRenderer.cpp + ExtractAPIConsumer.cpp FrontendAction.cpp FrontendActions.cpp FrontendOptions.cpp diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2408,6 +2408,7 @@ {frontend::EmitCodeGenOnly, OPT_emit_codegen_only}, {frontend::EmitCodeGenOnly, OPT_emit_codegen_only}, {frontend::EmitObj, OPT_emit_obj}, + {frontend::ExtractAPI, OPT_extract_api}, {frontend::FixIt, OPT_fixit_EQ}, {frontend::FixIt, OPT_fixit}, @@ -4147,6 +4148,7 @@ case frontend::EmitLLVMOnly: case frontend::EmitCodeGenOnly: case frontend::EmitObj: + case frontend::ExtractAPI: case frontend::FixIt: case frontend::GenerateModule: case frontend::GenerateModuleInterface: diff --git a/clang/lib/Frontend/ExtractAPIConsumer.cpp b/clang/lib/Frontend/ExtractAPIConsumer.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Frontend/ExtractAPIConsumer.cpp @@ -0,0 +1,39 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" + +using namespace clang; + +namespace { +class ExtractAPIVisitor : public RecursiveASTVisitor { +public: + explicit ExtractAPIVisitor(ASTContext *Context) : Context(Context) {} + + bool VisitNamedDecl(NamedDecl *Decl) { + llvm::outs() << Decl->getName() << "\n"; + return true; + } + +private: + ASTContext *Context; +}; + +class ExtractAPIConsumer : public ASTConsumer { +public: + explicit ExtractAPIConsumer(ASTContext *Context) : Visitor(Context) {} + + void HandleTranslationUnit(ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + ExtractAPIVisitor Visitor; +}; +} // namespace + +std::unique_ptr +ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return std::make_unique(&CI.getASTContext()); +} diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -57,6 +57,8 @@ case EmitLLVMOnly: return std::make_unique(); case EmitCodeGenOnly: return std::make_unique(); case EmitObj: return std::make_unique(); + case ExtractAPI: + return std::make_unique(); case FixIt: return std::make_unique(); case GenerateModule: return std::make_unique(); diff --git a/clang/test/Driver/extract-api.c b/clang/test/Driver/extract-api.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/extract-api.c @@ -0,0 +1,16 @@ +// RUN: %clang -target x86_64-unknown-unknown -ccc-print-phases -extract-api %s 2> %t +// RUN: echo 'END' >> %t +// RUN: FileCheck -check-prefix EXTRACT-API-PHASES -input-file %t %s + +// EXTRACT-API-PHASES: 0: input, +// EXTRACT-API-PHASES: , c +// EXTRACT-API-PHASES: 1: preprocessor, {0}, cpp-output +// EXTRACT-API-PHASES: 2: compiler, {1}, api-information +// EXTRACT-API-PHASES-NOT: 3: +// EXTRACT-API-PHASES: END + +// FIXME: Check for the dummy output now to verify that the custom action was executed. +// RUN: %clang -extract-api %s | FileCheck -check-prefix DUMMY-OUTPUT %s + +void dummy_function(); +// DUMMY-OUTPUT: dummy_function