Index: clang-tools-extra/clangd/ClangdUnit.cpp =================================================================== --- clang-tools-extra/clangd/ClangdUnit.cpp +++ clang-tools-extra/clangd/ClangdUnit.cpp @@ -27,7 +27,6 @@ #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/Utils.h" #include "clang/Index/IndexDataConsumer.h" -#include "clang/Index/IndexingAction.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" Index: clang-tools-extra/clangd/FindSymbols.cpp =================================================================== --- clang-tools-extra/clangd/FindSymbols.cpp +++ clang-tools-extra/clangd/FindSymbols.cpp @@ -17,7 +17,6 @@ #include "clang/AST/DeclTemplate.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/IndexSymbol.h" -#include "clang/Index/IndexingAction.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" Index: clang-tools-extra/clangd/XRefs.cpp =================================================================== --- clang-tools-extra/clangd/XRefs.cpp +++ clang-tools-extra/clangd/XRefs.cpp @@ -29,9 +29,10 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Index/GenerateIndexAction.h" #include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexOptions.h" #include "clang/Index/IndexSymbol.h" -#include "clang/Index/IndexingAction.h" #include "clang/Index/USRGeneration.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" Index: clang-tools-extra/clangd/index/FileIndex.cpp =================================================================== --- clang-tools-extra/clangd/index/FileIndex.cpp +++ clang-tools-extra/clangd/index/FileIndex.cpp @@ -16,7 +16,8 @@ #include "index/Merge.h" #include "index/SymbolOrigin.h" #include "index/dex/Dex.h" -#include "clang/Index/IndexingAction.h" +#include "clang/Index/GenerateIndexAction.h" +#include "clang/Index/IndexOptions.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/DenseMap.h" Index: clang-tools-extra/clangd/index/IndexAction.cpp =================================================================== --- clang-tools-extra/clangd/index/IndexAction.cpp +++ clang-tools-extra/clangd/index/IndexAction.cpp @@ -12,7 +12,8 @@ #include "index/Relation.h" #include "index/SymbolOrigin.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Index/IndexingAction.h" +#include "clang/Index/GenerateIndexAction.h" +#include "clang/Index/IndexOptions.h" #include "clang/Tooling/Tooling.h" #include <utility> @@ -123,7 +124,8 @@ std::function<void(RefSlab)> RefsCallback, std::function<void(RelationSlab)> RelationsCallback, std::function<void(IncludeGraph)> IncludeGraphCallback) - : WrapperFrontendAction(index::createIndexingAction(C, Opts, nullptr)), + : WrapperFrontendAction( + llvm::make_unique<index::GenerateIndexAction>(C, Opts)), SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback), RelationsCallback(RelationsCallback), IncludeGraphCallback(IncludeGraphCallback), Collector(C), Index: clang-tools-extra/clangd/index/SymbolCollector.cpp =================================================================== --- clang-tools-extra/clangd/index/SymbolCollector.cpp +++ clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -24,7 +24,6 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Index/IndexSymbol.h" -#include "clang/Index/IndexingAction.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Preprocessor.h" #include "llvm/Support/Casting.h" Index: clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp +++ clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp @@ -13,7 +13,8 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Index/IndexingAction.h" +#include "clang/Index/GenerateIndexAction.h" +#include "clang/Index/IndexOptions.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/STLExtras.h" @@ -208,7 +209,7 @@ const index::IndexingOptions &Opts, CommentHandler *PragmaHandler) : WrapperFrontendAction( - index::createIndexingAction(C, Opts, nullptr)), + llvm::make_unique<index::GenerateIndexAction>(C, Opts)), PragmaHandler(PragmaHandler) {} std::unique_ptr<ASTConsumer> Index: clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp +++ clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp @@ -15,7 +15,6 @@ #include "XRefs.h" #include "index/FileIndex.h" #include "index/SymbolCollector.h" -#include "clang/Index/IndexingAction.h" #include "llvm/Support/Path.h" #include "gmock/gmock.h" #include "gtest/gtest.h" Index: clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp +++ clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp @@ -17,7 +17,6 @@ #include "index/SymbolCollector.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" -#include "clang/Index/IndexingAction.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock.h" Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -18,7 +18,6 @@ #include "index/FileIndex.h" #include "index/MemIndex.h" #include "index/SymbolCollector.h" -#include "clang/Index/IndexingAction.h" #include "llvm/ADT/None.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -220,6 +220,10 @@ def err_module_header_file_invalid : Error<"unexpected module header file input '%0'">, DefaultFatal; +def remark_index_producing_module_file_data : Remark<"producing index data for " + "module file '%0'">, + InGroup<IndexStore>; + def err_interface_stubs : Error<"clang-ifs (-emit-iterface-stubs): %0">; def err_test_module_file_extension_version : Error< Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -358,6 +358,7 @@ def ModuleImport : DiagGroup<"module-import">; def ModuleConflict : DiagGroup<"module-conflict">; def ModuleFileExtension : DiagGroup<"module-file-extension">; +def IndexStore : DiagGroup<"index-store">; def NewlineEOF : DiagGroup<"newline-eof">; def Nullability : DiagGroup<"nullability">; def NullabilityDeclSpec : DiagGroup<"nullability-declspec">; Index: clang/include/clang/Driver/Job.h =================================================================== --- clang/include/clang/Driver/Job.h +++ clang/include/clang/Driver/Job.h @@ -31,9 +31,11 @@ struct CrashReportInfo { StringRef Filename; StringRef VFSPath; + StringRef IndexStorePath; - CrashReportInfo(StringRef Filename, StringRef VFSPath) - : Filename(Filename), VFSPath(VFSPath) {} + CrashReportInfo(StringRef Filename, StringRef VFSPath, + StringRef IndexStorePath) + : Filename(Filename), VFSPath(VFSPath), IndexStorePath(IndexStorePath) {} }; /// Command - An executable path/name and argument vector to Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -323,6 +323,13 @@ def : Joined<["-"], "objcmt-white-list-dir-path=">, Flags<[CC1Option]>, Alias<objcmt_whitelist_dir_path>; +def index_store_path : Separate<["-"], "index-store-path">, Flags<[CC1Option]>, + HelpText<"Enable indexing with the specified data store path">; +def index_ignore_system_symbols : Flag<["-"], "index-ignore-system-symbols">, Flags<[CC1Option]>, + HelpText<"Ignore symbols from system headers">; +def index_record_codegen_name : Flag<["-"], "index-record-codegen-name">, Flags<[CC1Option]>, + HelpText<"Record the codegen name for symbols">; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<["-"], "ccc-">, Group<internal_Group>, Flags<[Unsupported]>; Index: clang/include/clang/Frontend/CompilerInstance.h =================================================================== --- clang/include/clang/Frontend/CompilerInstance.h +++ clang/include/clang/Frontend/CompilerInstance.h @@ -177,6 +177,12 @@ /// The list of active output files. std::list<OutputFile> OutputFiles; + /// \brief An optional callback function used to wrap all FrontendActions + /// produced to generate imported modules before they are executed. + std::function<std::unique_ptr<FrontendAction>( + const FrontendOptions &opts, std::unique_ptr<FrontendAction> action)> + GenModuleActionWrapper; + /// Force an output buffer. std::unique_ptr<llvm::raw_pwrite_stream> OutputStream; @@ -790,6 +796,19 @@ bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override; + void setGenModuleActionWrapper( + std::function<std::unique_ptr<FrontendAction>( + const FrontendOptions &Opts, std::unique_ptr<FrontendAction> Action)> + Wrapper) { + GenModuleActionWrapper = Wrapper; + } + + std::function<std::unique_ptr<FrontendAction>( + const FrontendOptions &Opts, std::unique_ptr<FrontendAction> Action)> + getGenModuleActionWrapper() const { + return GenModuleActionWrapper; + } + void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) { DependencyCollectors.push_back(std::move(Listener)); } Index: clang/include/clang/Frontend/FrontendOptions.h =================================================================== --- clang/include/clang/Frontend/FrontendOptions.h +++ clang/include/clang/Frontend/FrontendOptions.h @@ -387,6 +387,10 @@ std::string MTMigrateDir; std::string ARCMTMigrateReportOut; + std::string IndexStorePath; + unsigned IndexIgnoreSystemSymbols : 1; + unsigned IndexRecordCodegenName : 1; + /// The input files and their types. std::vector<FrontendInputFile> Inputs; @@ -461,7 +465,8 @@ UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), ModulesEmbedAllFiles(false), - IncludeTimestamps(true) {} + IncludeTimestamps(true), IndexIgnoreSystemSymbols(false), + IndexRecordCodegenName(false) {} /// getInputKindForExtension - Return the appropriate input kind for a file /// extension. For example, "c" would return InputKind::C. Index: clang/include/clang/Index/CodegenNameGenerator.h =================================================================== --- clang/include/clang/Index/CodegenNameGenerator.h +++ clang/include/clang/Index/CodegenNameGenerator.h @@ -30,23 +30,23 @@ explicit CodegenNameGenerator(ASTContext &Ctx); ~CodegenNameGenerator(); - /// \returns true on failure to produce a name for the given decl, false on - /// success. + /// Writes name for \p D to \p OS. + /// \returns true on failure, false on success. bool writeName(const Decl *D, raw_ostream &OS); - /// Version of \c writeName function that returns a string. + /// \returns name for \p D std::string getName(const Decl *D); - /// This can return multiple mangled names when applicable, e.g. for C++ - /// constructors/destructors. + /// \returns all applicable mangled names. + /// For example C++ constructors/destructors can have multiple. std::vector<std::string> getAllManglings(const Decl *D); private: struct Implementation; - std::unique_ptr<ASTNameGenerator> Impl; + Implementation *Impl; }; } // namespace index } // namespace clang -#endif // LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H +#endif Index: clang/include/clang/Index/DeclOccurrence.h =================================================================== --- clang/include/clang/Index/DeclOccurrence.h +++ /dev/null @@ -1,41 +0,0 @@ -//===- DeclOccurrence.h - An occurrence of a decl within a file -*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_INDEX_DECLOCCURRENCE_H -#define LLVM_CLANG_INDEX_DECLOCCURRENCE_H - -#include "clang/Basic/LLVM.h" -#include "clang/Index/IndexSymbol.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallVector.h" - -namespace clang { -class Decl; - -namespace index { - -struct DeclOccurrence { - SymbolRoleSet Roles; - unsigned Offset; - const Decl *Dcl; - SmallVector<SymbolRelation, 3> Relations; - - DeclOccurrence(SymbolRoleSet R, unsigned Offset, const Decl *D, - ArrayRef<SymbolRelation> Relations) - : Roles(R), Offset(Offset), Dcl(D), - Relations(Relations.begin(), Relations.end()) {} - - friend bool operator<(const DeclOccurrence &LHS, const DeclOccurrence &RHS) { - return LHS.Offset < RHS.Offset; - } -}; - -} // namespace index -} // namespace clang - -#endif // LLVM_CLANG_INDEX_DECLOCCURRENCE_H Index: clang/include/clang/Index/GenerateIndexAction.h =================================================================== --- /dev/null +++ clang/include/clang/Index/GenerateIndexAction.h @@ -0,0 +1,74 @@ +//===--- GenerateIndexAction.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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXINGACTION_H +#define LLVM_CLANG_INDEX_INDEXINGACTION_H + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Index/IndexAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexingContext.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace clang { +class ASTContext; +class ASTReader; +class ASTUnit; +class CompilerInstance; +class Decl; + +namespace serialization { +class ModuleFile; +} + +namespace index { +class IndexDataConsumer; + +/// Recursively indexes all decls in the \p Unit and feeds them to \p +/// DataConsumer. +void indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, + const IndexingOptions &Opts); + +/// Recursively indexes \p Decls and feeds them to \p DataConsumer. +void indexTopLevelDecls(ASTContext &Ctx, Preprocessor &PP, + ArrayRef<const Decl *> Decls, + IndexDataConsumer &DataConsumer, + const IndexingOptions &Opts); + +/// Creates a PPCallbacks that indexes macros and feeds them to \p Consumer. +/// The caller is responsible for calling `Consumer.setPreprocessor()`. +std::unique_ptr<PPCallbacks> indexMacrosCallback(IndexDataConsumer &Consumer, + const IndexingOptions &Opts); + +/// Recursively indexes all top-level decls in the \p Mod and feeds them to \p +/// DataConsumer. +void indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, + IndexDataConsumer &DataConsumer, + const IndexingOptions &Opts); + +/// Generates index data and uses IndexDataConsumer to consume it. +/// Handles both index units and index records. +class GenerateIndexAction : public IndexAction { + std::shared_ptr<IndexDataConsumer> DataConsumer; + std::shared_ptr<IndexingContext> IndexCtx; + +public: + GenerateIndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts); + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + + void EndSourceFileAction() override; +}; + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexAction.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexAction.h @@ -0,0 +1,52 @@ +//===--- IndexAction.h - Interface for index actions ------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXACTION_H +#define LLVM_CLANG_INDEX_INDEXACTION_H + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace clang { +namespace index { + +class IndexActionWrapper; + +/// This is primarily a hack - poking hole through FrontendAction encapsulation +/// in order to access protected methods from IndexActionWrapper. FIXME? +/// Auxiliary purpose is type distinction for two different actions in +/// IndexActionWrapper in order to quarantee their methods are called in correct +/// order. +class IndexAction : public ASTFrontendAction { + friend class IndexActionWrapper; +}; + +/// Combines implementation of WrappedActionParam and IndexingActionParam. +class IndexActionWrapper : public WrapperFrontendAction { + std::unique_ptr<IndexAction> IndexingAction; + +public: + IndexActionWrapper(std::unique_ptr<IndexAction> IndexingActionParam, + std::unique_ptr<FrontendAction> WrappedActionParam); + + /// \returns a consumer that first invokes consumer created by + /// WrappedActionParam and then consumer created by IndexingActionParam. + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + + /// Invokes implementation of EndSourceFileAction in WrappedActionParam first, + /// then implementation in IndexingActionParam. + void EndSourceFileAction() override; +}; + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexDataConsumer.h =================================================================== --- clang/include/clang/Index/IndexDataConsumer.h +++ clang/include/clang/Index/IndexDataConsumer.h @@ -32,7 +32,7 @@ const DeclContext *ContainerDC; }; - virtual ~IndexDataConsumer() {} + virtual ~IndexDataConsumer() = default; virtual void initialize(ASTContext &Ctx) {} @@ -41,12 +41,16 @@ /// \returns true to continue indexing, or false to abort. virtual bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations, - SourceLocation Loc, ASTNodeInfo ASTNode); + SourceLocation Loc, ASTNodeInfo ASTNode) { + return true; + } /// \returns true to continue indexing, or false to abort. virtual bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *MI, SymbolRoleSet Roles, - SourceLocation Loc); + SourceLocation Loc) { + return true; + } /// \returns true to continue indexing, or false to abort. /// @@ -54,8 +58,10 @@ /// For "@import MyMod.SubMod", there will be a call for 'MyMod' with the /// 'reference' role, and a call for 'SubMod' with the 'declaration' role. virtual bool handleModuleOccurence(const ImportDecl *ImportD, - const Module *Mod, - SymbolRoleSet Roles, SourceLocation Loc); + const Module *Mod, SymbolRoleSet Roles, + SourceLocation Loc) { + return true; + } virtual void finish() {} }; Index: clang/include/clang/Index/IndexOptions.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexOptions.h @@ -0,0 +1,44 @@ +//===--- IndexOptions.h - Options for indexing ------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXOPTIONS_H +#define LLVM_CLANG_INDEX_INDEXOPTIONS_H + +#include "clang/Frontend/FrontendOptions.h" +#include <memory> +#include <string> + +namespace clang { +namespace index { + +struct IndexingOptions { + enum class SystemSymbolFilterKind { + None, + DeclarationsOnly, + All, + }; + + SystemSymbolFilterKind SystemSymbolFilter = + SystemSymbolFilterKind::DeclarationsOnly; + bool IndexFunctionLocals = false; + bool IndexImplicitInstantiation = false; + // Whether to index macro definitions in the Preprocesor when preprocessor + // callback is not available (e.g. after parsing has finished). Note that + // macro references are not available in Proprocessor. + bool IndexMacrosInPreprocessor = false; + // Has no effect if IndexFunctionLocals are false. + bool IndexParametersInDeclarations = false; + bool IndexTemplateParameters = false; +}; + +IndexingOptions parseIndexGenerationOptions(const FrontendOptions &FEOpts); + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexPPCallbacks.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexPPCallbacks.h @@ -0,0 +1,60 @@ +//===--- IndexPPCallbacks.h - Gathering index record data -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXPPCALLBACKS_H +#define LLVM_CLANG_INDEX_INDEXPPCALLBACKS_H + +#include "clang/Index/IndexingContext.h" +#include "clang/Lex/PPCallbacks.h" + +#include <memory> + +namespace llvm { +class BitstreamWriter; +} + +namespace clang { +class FileEntry; +class FileManager; + +namespace index { + +class IndexPPCallbacks : public PPCallbacks { + std::shared_ptr<IndexingContext> IndexCtx; + +public: + IndexPPCallbacks(std::shared_ptr<IndexingContext> IndexCtx) + : IndexCtx(IndexCtx) {} + + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override { + IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), + Range.getBegin(), *MD.getMacroInfo()); + } + + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override { + IndexCtx->handleMacroDefined(*MacroNameTok.getIdentifierInfo(), + MacroNameTok.getLocation(), + *MD->getMacroInfo()); + } + + void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, + const MacroDirective *Undef) override { + if (!MD.getMacroInfo()) // Ignore noop #undef. + return; + IndexCtx->handleMacroUndefined(*MacroNameTok.getIdentifierInfo(), + MacroNameTok.getLocation(), + *MD.getMacroInfo()); + } +}; + +} // end namespace index +} // end namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/DeclOccurrenceCollector.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/DeclOccurrenceCollector.h @@ -0,0 +1,45 @@ +//===--- DeclOccurrenceCollector.h - Collecting data for index records --*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_DECLOCCURENCECOLLECTOR_H +#define LLVM_CLANG_INDEX_DECLOCCURENCECOLLECTOR_H + +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexWhileBuilding/IndexRecordWriter.h" +#include "clang/Index/IndexingContext.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace clang { +namespace index { + +/// Collects occurences of declarations. +class DeclOccurrenceCollector : public IndexDataConsumer { + SourceManager &SM; + // This exists because there's cyclic dependency between IndexingContext and + // DeclOccurrenceCollector. + IndexingContext *IndexCtx = nullptr; + + llvm::DenseMap<FileID, IndexRecordWriter> RecordByFile; + +public: + explicit DeclOccurrenceCollector(SourceManager &SM) : SM(SM) {} + void setIndexingContext(IndexingContext &IC) { IndexCtx = &IC; } + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) override; + const llvm::DenseMap<FileID, IndexRecordWriter> &getRecordByFile() const { + return RecordByFile; + } +}; + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/EmitIndexAction.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/EmitIndexAction.h @@ -0,0 +1,100 @@ +//===--- EmitIndexAction.h - Index emitting action --------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_EMITINDEXACTION_H +#define LLVM_CLANG_INDEX_EMITINDEXACTION_H + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Index/IndexAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexWhileBuilding/DeclOccurrenceCollector.h" +#include "clang/Index/IndexWhileBuilding/IndexRecordWriter.h" +#include "clang/Index/IndexWhileBuilding/IndexUnitWriter.h" +#include "clang/Index/IndexingContext.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace clang { +namespace index { + +class SourceFilesDependencyCollector : public DependencyCollector { + const CompilerInstance &CI; + IndexingContext &IndexCtx; + EmitIndexOptions RecordOpts; + +public: + IndexUnitWriter UnitWriter; + + SourceFilesDependencyCollector(const CompilerInstance &CI, + IndexingContext &indexCtx, + EmitIndexOptions recordOpts); + + virtual void attachToPreprocessor(Preprocessor &PP) override; + +private: + bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, + bool IsModuleFile, bool IsMissing) override; + bool isInSysroot(StringRef Filename); +}; + +// FIXME: +// This isn't nice - just a glue. +// The reasons for this to exist are: +// - We need access to CompilerInstance to create collectors which we get only +// in CreateASTConsumer method, not in FrontendAction implementation's +// constructor. +// - createIndexRecordingASTConsumer returns unique_ptr but we need to preserve +// some data until EndSourceFileAction. +struct ActionContext { + const CompilerInstance &CI; + DeclOccurrenceCollector DeclsDataCollector; + IndexingContext Ctx; + SourceFilesDependencyCollector DepCollector; + EmitIndexOptions RecordOpts; + IndexingOptions IndexOpts; + + ActionContext(const IndexingOptions &IndexOpts, const CompilerInstance &CI, + const EmitIndexOptions &RecordOpts); + + void emitCollectedData(); +}; + +/// Generates index data and serializes them to disk. +/// Handles both index units and index records. +class EmitIndexAction : public IndexAction { + EmitIndexOptions RecordOpts; + IndexingOptions IndexOpts; + std::shared_ptr<ActionContext> Context = nullptr; + +public: + EmitIndexAction(const IndexingOptions &IndexOpts, + const EmitIndexOptions &RecordOpts); + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef) override; + + void EndSourceFileAction() override; +}; + +/// Checks if the unit file exists for \p Mod and emits index data in case it +/// doesn't. Handles both index units and index records. \returns true if the +/// index data were generated, false otherwise. +bool emitIndexDataForModule(const Module *Mod, const CompilerInstance &CI); + +/// Emits index data for \p Mod. +/// Handles both index units and index records. +void emitIndexDataForModuleFile(serialization::ModuleFile &Mod, + const CompilerInstance &CI, + const IndexingOptions &IndexOpts, + const EmitIndexOptions &RecordOpts); + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/EmitIndexOptions.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/EmitIndexOptions.h @@ -0,0 +1,48 @@ +//===--- IndexOptions.h - Options for indexing ------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_EMITINDEXOPTIONS_H +#define LLVM_CLANG_INDEX_EMITINDEXOPTIONS_H + +#include "clang/Frontend/FrontendOptions.h" +#include <memory> +#include <string> + +namespace clang { +namespace index { + +struct EmitIndexOptions { + enum class IncludesRecordingKind { + None, + UserOnly, // only record includes inside non-system files. + All, + }; + + const std::string RootDir; + // Caching the full paths. + const std::string IndexDir; + const std::string UnitDir; + const std::string RecordDir; + + const bool RecordSymbolCodeGenName; + const bool RecordSystemDependencies; + const IncludesRecordingKind RecordIncludes; + + EmitIndexOptions(const StringRef RootDir, + const bool RecordSymbolCodeGenName = false, + const bool RecordSystemDependencies = true, + const IncludesRecordingKind RecordIncludes = + IncludesRecordingKind::UserOnly); +}; + +EmitIndexOptions parseEmitIndexOptions(const FrontendOptions &FEOpts); + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/IndexDataFormat.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/IndexDataFormat.h @@ -0,0 +1,128 @@ +//===--- IndexDataFormat.h - Layout of index on a filesystem --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXDATAFORMAT_H +#define LLVM_CLANG_LIB_INDEX_INDEXDATAFORMAT_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitCodes.h" + +namespace clang { +namespace index { +namespace store { + +/// Index is organized like this: +/// (root of index dir) +/// /<IndexDir> +/// /<IndexRecordSubdir> +/// /AA +/// /(records) +/// /AB +/// /(records) +/// /AC +/// /(records) +/// ... +/// /<IndexUnitSubdir> +/// /(units) + +/// \returns version of index format +unsigned getIndexFormatVersion(); + +/// \returns name of directory where index data lives inside the root directory. +const char *getIndexDir(); + +/// \returns name of directory where unit data lives inside the index directory. +const char *getIndexUnitSubdir(); + +/// \returns name of directory where record data lives inside the index +/// directory. +const char *getIndexRecordSubdir(); + +/// \returns name of index record related to \p SourceFilename. +/// Index record filename is the original source filename with hash of the index +/// data as a suffix. +std::string +generateFilenameForIndexRecord(llvm::StringRef SourceFilename, + const llvm::SmallString<512> &IndexRecordBuffer); + +/// Appends name of index unit related to \p OutputFilePath to \p Str. +/// \p OutputFilePath is absolute path to output file. +void getUnitNameForOutputFile(llvm::StringRef OutputFilePath, + llvm::SmallVectorImpl<char> &Str); + +/// Appends index directory and unit subdirectory to \p Buffer without erasing +/// it. +void appendUnitSubDir(llvm::SmallVectorImpl<char> &Buffer); + +/// Appends index directory and record subdirectory to \p Buffer without erasing +/// it. +void appendRecordSubDir(llvm::SmallVectorImpl<char> &Buffer); + +/// This is currently a placeholder - just appends \p UnitName to \p Buffer. +void appendUnitBucketDir(llvm::StringRef UnitName, + llvm::SmallVectorImpl<char> &Buffer); + +/// We're bucketing record files according to the last two characters in their +/// names in order to keep number of files in a single directory reasonable. +/// Appends bucket subdirectory and delimiter. +void appendRecordBucketDir(llvm::StringRef RecordName, + llvm::SmallVectorImpl<char> &Buffer); + +/// Appends \p RecordName to \p Buffer. +void appendRecordFilename(llvm::StringRef RecordName, + llvm::SmallVectorImpl<char> &Buffer); + +enum RecordBitRecord { + REC_VERSION = 0, + REC_DECLINFO = 1, + REC_DECLOFFSETS = 2, + REC_DECLOCCURRENCE = 3, +}; + +enum RecordBitBlock { + REC_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + REC_DECLS_BLOCK_ID, + REC_DECLOFFSETS_BLOCK_ID, + REC_DECLOCCURRENCES_BLOCK_ID, +}; + +enum UnitBitRecord { + UNIT_VERSION = 0, + UNIT_INFO = 1, + UNIT_DEPENDENCY = 2, + UNIT_INCLUDE = 3, + UNIT_PATH = 4, + UNIT_PATH_BUFFER = 5, + UNIT_MODULE = 6, + UNIT_MODULE_BUFFER = 7, +}; + +enum UnitBitBlock { + UNIT_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + UNIT_INFO_BLOCK_ID, + UNIT_DEPENDENCIES_BLOCK_ID, + UNIT_INCLUDES_BLOCK_ID, + UNIT_PATHS_BLOCK_ID, + UNIT_MODULES_BLOCK_ID, +}; + +enum UnitDependencyKind { + UNIT_DEPEND_KIND_FILE = 0, + UNIT_DEPEND_KIND_RECORD = 1, + UNIT_DEPEND_KIND_UNIT = 2, +}; +static const unsigned UnitDependencyKindBitNum = 2; +static const unsigned UnitFilePathPrefixKindBitNum = 2; + +} // end namespace store +} // end namespace index +} // end namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/IndexRecordReader.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/IndexRecordReader.h @@ -0,0 +1,140 @@ +//===--- IndexRecordReader.h - Index record deserialization ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDREADER_H +#define LLVM_CLANG_INDEX_INDEXRECORDREADER_H + +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include <memory> + +namespace llvm { +class MemoryBuffer; +} + +namespace clang { +namespace index { + +struct IndexRecordDecl { + unsigned DeclID; + SymbolInfo SymInfo; + SymbolRoleSet Roles; + SymbolRoleSet RelatedRoles; + StringRef Name; + StringRef USR; + StringRef CodeGenName; +}; + +struct IndexRecordRelation { + SymbolRoleSet Roles; + const IndexRecordDecl *Dcl = nullptr; + + IndexRecordRelation() = default; + IndexRecordRelation(SymbolRoleSet Roles, const IndexRecordDecl *Dcl) + : Roles(Roles), Dcl(Dcl) {} +}; + +struct IndexRecordOccurrence { + const IndexRecordDecl *Dcl; + SmallVector<IndexRecordRelation, 4> Relations; + SymbolRoleSet Roles; + unsigned Line; + unsigned Column; +}; + +/// Interface for reading index record data backed by a memory buffer. +class IndexRecordReader { +public: + static std::unique_ptr<IndexRecordReader> + createWithRecordFilename(StringRef RecordFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr<IndexRecordReader> + createWithFilePath(StringRef FilePath, std::string &Error); + static std::unique_ptr<IndexRecordReader> + createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer, + std::string &Error); + + struct DeclSearchReturn { + bool AcceptDecl; + bool ContinueSearch; + }; + typedef DeclSearchReturn(DeclSearchCheck)(const IndexRecordDecl &); + /// Iterates over declarations and calls first \p Checker and conditionally + /// also \p Receiver for each of them. Receiver is called for those + /// declarations for which \p Checker returns AcceptDecl == true. Iteration is + /// terminated early if \p Checker returns ContinueSearch == false. Resulting + /// decls can be used as filter for \c foreachOccurrence. This allows + /// allocating memory only for the record decls that the caller is interested + /// in. + bool searchDecls(llvm::function_ref<DeclSearchCheck> Checker, + llvm::function_ref<void(const IndexRecordDecl *)> Receiver); + + /// Iterates over declarations and calls \p Receiver for each of them. + /// Avoids caching the declaration data if \p NoCache is true. + bool foreachDecl(bool NoCache, + llvm::function_ref<bool(const IndexRecordDecl *)> Receiver); + + /// Iterates over occurences of declarations and conditionally calls \p + /// Receiver for occurences of declarations. If \p WhitelistDecls is non-empty + /// \p Receiver is called only for occurence of declarations that it contains. + /// If \p WhitelistRelated is non-empty \p Receiver is called only for + /// occurences that are related to any of the declarations it contains. In + /// case \p WhitelistDecls is non-empty and a particular declaration isn't + /// present, it is skipped and isn't further inspected in regards to \p + /// WhitelistRelated. + bool foreachOccurrence( + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver, + ArrayRef<const IndexRecordDecl *> WhitelistDecls = None, + ArrayRef<const IndexRecordDecl *> WhitelistRelated = None); + + /// Iterates over occurences of declarations and conditionally calls \p + /// Receiver for occurences of declarations. \p Receiver will be called only + /// for occurences between \p lineStart and \p lineStart + \p lineCount (both + /// ends are included). + bool foreachOccurrenceInLineRange( + unsigned lineStart, unsigned lineCount, + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver); + + // TODO order +private: + friend class IndexBitstreamVisitor; + llvm::BumpPtrAllocator Allocator; + std::unique_ptr<llvm::MemoryBuffer> Buffer; + llvm::BitstreamCursor DeclCursor; + llvm::BitstreamCursor OccurCursor; + llvm::ArrayRef<uint32_t> DeclOffsets; + const IndexRecordDecl **Decls; + + void setDeclOffsets(ArrayRef<uint32_t> Offs); + + unsigned getNumDecls() const; + + const IndexRecordDecl *getDeclByID(unsigned DeclID); + + const IndexRecordDecl *getDecl(unsigned Index); + + /// Reads occurrence data. + /// \param WhitelistDecls if non-empty indicates the list of decls that we + /// want to get occurrences for. If empty then indicates that we want + /// occurrences for all decls. \param WhitelistRelated Same as \c + /// WhitelistDecls but for related decls. \returns true if the occurrence info + /// was filled out, false if occurrence was ignored. + bool readOccurrence(SmallVectorImpl<uint64_t> &Record, StringRef Blob, + ArrayRef<const IndexRecordDecl *> WhitelistDecls, + ArrayRef<const IndexRecordDecl *> WhitelistRelated, + IndexRecordOccurrence &RecOccur); + + void readDecl(unsigned Index, IndexRecordDecl &RecD); +}; + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/IndexRecordWriter.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/IndexRecordWriter.h @@ -0,0 +1,76 @@ +//===--- IndexRecordWriter.h - Index record serialization-------- ---------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDWRITER_H +#define LLVM_CLANG_INDEX_INDEXRECORDWRITER_H + +#include "clang/AST/DeclBase.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Index/CodegenNameGenerator.h" +#include "clang/Index/IndexSymbol.h" +#include "clang/Index/IndexWhileBuilding/EmitIndexOptions.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include <map> +#include <vector> + +namespace clang { +namespace index { + +struct DeclInfo { + const Decl *D; + SymbolRoleSet Roles; + SymbolRoleSet RelatedRoles; +}; + +/// Collects declaration occurences in a header or source file and serializes +/// them. +class IndexRecordWriter { +private: + const bool IsSystem; + + llvm::DenseMap<const Decl *, unsigned> IndexForDecl; + std::vector<DeclInfo> Decls; + std::multimap<unsigned, SmallVector<uint64_t, 10>> Occurrences; + +public: + IndexRecordWriter(bool IsSystem) : IsSystem(IsSystem) {} + + bool isSystem() const { return IsSystem; } + + /// Adds an occurrence of the canonical declaration \c D at the supplied + /// \c Offset + /// + /// \param Roles the roles the occurrence fulfills in this position. + /// \param Offset the offset in the file of this occurrence. + /// \param D the canonical declaration this is an occurrence of. + /// \param Relations the set of symbols related to this occurrence. + void addDeclOccurence(SymbolRoleSet Roles, FileID FID, unsigned Offset, + const Decl *D, ArrayRef<SymbolRelation> Relations); + + bool isRecordEmpty() const { return Decls.empty(); } + + /// \returns filename of the index record created or None in case of failure. + llvm::Optional<std::string> + writeToDisk(StringRef SourceFileName, const EmitIndexOptions &RecOpts, + const PrintingPolicy &Policy, + std::shared_ptr<CodegenNameGenerator> CGNameGen, + DiagnosticsEngine &Diag) const; + +private: + void getBitcodeRepresentation( + llvm::SmallString<512> &Buffer, const clang::PrintingPolicy &Policy, + std::shared_ptr<CodegenNameGenerator> CGNameGen) const; +}; + +} // end namespace index +} // end namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/IndexUnitReader.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/IndexUnitReader.h @@ -0,0 +1,156 @@ +//===--- IndexUnitReader.h - Index unit deserialization -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITREADER_H +#define LLVM_CLANG_INDEX_INDEXUNITREADER_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "clang/Index/IndexWhileBuilding/PathStorage.h" +#include <memory> +#include <vector> + +namespace clang { +namespace index { + +struct ModuleInfo { + unsigned NameOffset; + unsigned NameSize; +}; + +/// Interface for reading index unit data backed by a memory buffer. +class IndexUnitReader { +public: + enum class DependencyKind { + Unit, + Record, + File, + }; + + IndexUnitReader(std::unique_ptr<llvm::MemoryBuffer> Buf, + llvm::BitstreamCursor &&DependenciesBlockCursor, + llvm::BitstreamCursor &&IncludeCursor, + StringRef ProviderIdentifier, // < reference to the Buf data + StringRef ProviderVersion, // < reference to the Buf data + bool IsSystemUnit, bool IsModuleUnit, bool IsDebugCompilation, + StringRef ModuleName, // < reference to the Buf data + StringRef Target, // < reference to the Buf data + int MainPathIndex, std::vector<FileBitPath> &&Paths, + StringRef PathsBuffer, + // Offset-size pairs to the PathsBuffer. + size_t WorkDirOffset, size_t WorkDirSize, + size_t OutputFileOffset, size_t OutputFileSize, + size_t SysrootOffset, size_t SysrootSize, + std::vector<ModuleInfo> &&Modules, + StringRef ModuleNamesBuffer, // < reference to the Buf data + llvm::sys::TimePoint<> ModTime); + + /// Creates IndexUnitReader from \p Buf. + /// \returns nullptr in case of any failure. + static std::unique_ptr<IndexUnitReader> + create(std::unique_ptr<llvm::MemoryBuffer> Buf, + llvm::sys::TimePoint<> ModTime, std::string &Error); + + /// Creates IndexUnitReader from a file. + /// File location is derived from \p IndexUnitFilename and \p IndexRootDir. + static std::unique_ptr<IndexUnitReader> + createWithUnitFilename(StringRef IndexUnitFilename, StringRef IndexRootDir, + std::string &Error); + + /// Creates IndexUnitReader from a file at \p IndexUnitPath. + static std::unique_ptr<IndexUnitReader> + createWithFilePath(StringRef IndexUnitPath, std::string &Error); + + /// Get Modification time of index unit file by getting it's status on + /// filesystem. File location is derived from \p IndexUnitFilename and \p + /// IndexRootDir. \returns nullptr in case of failure to get status of the + /// file. + static Optional<llvm::sys::TimePoint<>> + getModificationTimeForUnit(StringRef IndexUnitFilename, + StringRef IndexRootDir, std::string &Error); + + StringRef getProviderIdentifier() const; + StringRef getProviderVersion() const; + + llvm::sys::TimePoint<> getModificationTime() const; + StringRef getWorkingDirectory() const; + StringRef getOutputFile() const; + StringRef getSysrootPath() const; + StringRef getMainFilePath() const; + StringRef getModuleName() const; + StringRef getTarget() const; + bool hasMainFile() const; + bool isSystemUnit() const; + bool isModuleUnit() const; + bool isDebugCompilation() const; + + struct DependencyInfo { + DependencyKind Kind; + bool IsSystem; + StringRef UnitOrRecordName; + StringRef FilePath; + StringRef ModuleName; + }; + /// Iterates over dependencies of this index unit and calls \p Receiver for + /// each of them. Order of dependencies is: units, records, files. + bool foreachDependency( + llvm::function_ref<bool(const DependencyInfo &Info)> Receiver); + + struct IncludeInfo { + StringRef SourcePath; + unsigned SourceLine; + StringRef TargetPath; + }; + /// Iterates over files included into this index unit and calls \p Receiver + /// for each of them. + bool + foreachInclude(llvm::function_ref<bool(const IncludeInfo &Info)> Receiver); + +private: + const std::unique_ptr<llvm::MemoryBuffer> Buf; + + llvm::BitstreamCursor DependenciesBlockCursor; + llvm::BitstreamCursor IncludedFilesCursor; + StringRef ProviderIdentifier; + StringRef ProviderVersion; + const bool IsSystemUnit; + const bool IsModuleUnit; + const bool IsDebugCompilation; + StringRef ModuleName; + StringRef Target; + + SmallString<128> MainFilePath; + const std::vector<FileBitPath> Paths; + + StringRef PathsBuffer; + // Offset-size pairs to the PathsBuffer. + const size_t WorkDirOffset; + const size_t WorkDirSize; + const size_t OutputFileOffset; + const size_t OutputFileSize; + const size_t SysrootOffset; + const size_t SysrootSize; + + const std::vector<ModuleInfo> Modules; + StringRef ModuleNamesBuffer; + const llvm::sys::TimePoint<> ModTime; + +private: + /// Appends the full path related to \p PathIndex to \p PathBuf. + void constructFilePath(SmallVectorImpl<char> &PathBuf, int PathIndex) const; +}; + +} // namespace index +} // namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/IndexUnitWriter.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/IndexUnitWriter.h @@ -0,0 +1,175 @@ +//===--- IndexUnitWriter.h - Index unit serialization ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITWRITER_H +#define LLVM_CLANG_INDEX_INDEXUNITWRITER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Index/IndexWhileBuilding/EmitIndexOptions.h" +#include "clang/Index/IndexWhileBuilding/PathStorage.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Allocator.h" +#include <set> +#include <string> +#include <vector> + +namespace llvm { +class BitstreamWriter; +} + +namespace clang { +class CompilerInstance; +class FileEntry; +class FileManager; +class Module; + +namespace index { + +namespace writer { +/// An opaque pointer to a module used by the IndexUnitWriter to associate +/// record and file dependencies with a module, and as a token for getting +/// information about the module from the caller. +typedef const void *OpaqueModule; + +struct ModuleInfo { + /// Full dot-separated module name. + StringRef Name; +}; + +typedef llvm::function_ref<ModuleInfo(OpaqueModule, + SmallVectorImpl<char> &Scratch)> + ModuleInfoWriterCallback; +} // end namespace writer + +struct ModuleIndexData { + const std::string Name; + const bool IsSystem; +}; + +class IndexUnitWriter { +private: + const EmitIndexOptions RecOpts; + + // index unit metadata + const std::string ProviderIdentifier; + const std::string ProviderVersion; + const std::string OutputFile; + const std::string AbsPathToOutputFile; + const FileEntry *MainFile; + + // Absence of value means we're not indexing a module. + const llvm::Optional<ModuleIndexData> ModuleData; + + const bool IsDebugCompilation; + const std::string TargetTriple; + const std::string WorkDir; + // FIXME + // IIUC we're serializing this as a source of truth of what is considered a + // system part of directory tree but we should probably be using values of all + // of -isysroot, -isystem and -F options. + const std::string SysrootPath; + + mutable PathStorage PathStore; + // Module dependencies + SmallString<512> ModuleNamesBuf; + std::vector<std::array<uint64_t, 3>> Modules; + // Maps module to one-based index in the Modules container. + llvm::DenseMap<writer::OpaqueModule, unsigned> ModToIdx; + + // Source file dependencies - mapping from UID to data. + llvm::DenseMap<unsigned, std::array<uint64_t, 7>> SourceFiles; + // Source files that has a corresponding IndexRecord aren't written into + // index. + llvm::DenseSet<unsigned> SourceFileHasARecord; + std::vector<std::array<uint64_t, 4>> IncludedFiles; + + std::vector<std::pair<SmallString<64>, std::array<uint64_t, 7>>> + IndexUnitDependencies; + llvm::DenseSet<unsigned> ASTFilesOfSeenIndexUnits; + + std::vector<std::pair<std::string, std::array<uint64_t, 7>>> + IndexRecordDependencies; + + std::function<writer::ModuleInfo(writer::OpaqueModule, + SmallVectorImpl<char> &Scratch)> + GetInfoForModuleFn; + +public: + /// Creates IndexUnitWriter for index unit represented either by \p MainFile + /// or \p ModuleData. \p MainFile the main file for a compiled source file. + /// This should be null for PCH and module units. + IndexUnitWriter(FileManager &FileMgr, const EmitIndexOptions &RecOpts, + StringRef ProviderIdentifier, StringRef ProviderVersion, + StringRef OutputFile, StringRef AbsPathToOutputFile, + const FileEntry *MainFile, + const llvm::Optional<ModuleIndexData> &ModuleData, + bool IsDebugCompilation, StringRef TargetTriple, + StringRef WorkDir, StringRef SysrootPath, + writer::ModuleInfoWriterCallback GetInfoForModule); + + unsigned addModule(writer::OpaqueModule Mod); + + /// Stores dependency of this index unit on source file \p File. + /// If \p Mod != nullptr it means \p File is compiled as part of module \p + /// Mod. + void addSourceFile(const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod); + + /// Stores dependency of this index unit on index record. + /// Index record \p RecordFile contains index data of \p SourceFile. + /// If \p Mod != nullptr it means \p SourceFile is compiled as part of module + /// \p Mod. + void addIndexRecord(StringRef RecordFile, const FileEntry *SourceFile, + bool IsSystem, writer::OpaqueModule Mod); + + /// Stores dependency of this index unit on another index unit - \p File. + /// If \p Mod != nullptr it means \p File contains index data of module \p + /// Mod. + void addIndexUnit(const FileEntry *File, StringRef AbsolutePath, + bool IsSystem, writer::OpaqueModule Mod, + bool withoutUnitName = false); + + /// Stores dependency of this index unit on \p IncludedFile. + void addIncludedFile(const FileEntry *IncludingFile, unsigned Line, + const FileEntry *IncludedFile); + + bool writeToDisk(std::string &Error) const; + +private: + void writeUnitInfo(llvm::BitstreamWriter &Stream) const; + void writeDependencies(llvm::BitstreamWriter &Stream) const; + void writeIncludes(llvm::BitstreamWriter &Stream) const; + void writePaths(llvm::BitstreamWriter &Stream) const; + void writeModules(llvm::BitstreamWriter &Stream) const; + + void getBitcodeRepresentation(llvm::SmallString<512> &Buffer, + std::string &Error) const; +}; + +IndexUnitWriter makeIndexUnitWriter(const CompilerInstance &CI, + const EmitIndexOptions &RecordOpts, + StringRef OutputFile, + const FileEntry *MainFile, + Module *UnitModule); + +bool initIndexDirectory(const EmitIndexOptions &RecordingOpts, + std::string &Error); + +// \returns true in case index unit for \p ModuleFilePath exists, false in case +// it doesn't and llvm::None in case it couldn't be stat()-ed. +llvm::Optional<bool> +doesIndexUnitForModuleExist(StringRef ModuleFilePath, + const FileManager &FileMgr, + const EmitIndexOptions &RecOpts); + +} // end namespace index +} // end namespace clang + +#endif Index: clang/include/clang/Index/IndexWhileBuilding/PathStorage.h =================================================================== --- /dev/null +++ clang/include/clang/Index/IndexWhileBuilding/PathStorage.h @@ -0,0 +1,73 @@ +//===--- PathStorage.h -- Index of paths ------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_PATHSTORAGE_H +#define LLVM_CLANG_INDEX_PATHSTORAGE_H + +#include "clang/Basic/FileManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" + +#include <string> +#include <vector> + +enum UnitFilePathPrefixKind { + UNIT_PATH_PREFIX_NONE = 0, + UNIT_PATH_PREFIX_WORKDIR = 1, + UNIT_PATH_PREFIX_SYSROOT = 2, +}; + +struct BitPathComponent { + size_t Offset = 0; + size_t Size = 0; + BitPathComponent(size_t Offset, size_t Size) : Offset(Offset), Size(Size) {} + BitPathComponent() = default; +}; + +struct DirBitPath { + UnitFilePathPrefixKind PrefixKind; + BitPathComponent Dir; + + DirBitPath(UnitFilePathPrefixKind Kind, BitPathComponent Dir); + DirBitPath(); +}; + +struct FileBitPath : DirBitPath { + BitPathComponent Filename; + FileBitPath(UnitFilePathPrefixKind Kind, BitPathComponent Dir, + BitPathComponent Filename) + : DirBitPath(Kind, Dir), Filename(Filename) {} + FileBitPath() = default; +}; + +class PathStorage { + std::string WorkDir; + std::string SysrootPath; + llvm::SmallString<512> PathsBuf; + llvm::StringMap<DirBitPath, llvm::BumpPtrAllocator> Dirs; + std::vector<FileBitPath> FileBitPaths; + llvm::DenseMap<const clang::FileEntry *, size_t> FileToIndex; + +public: + PathStorage(llvm::StringRef workDir, llvm::StringRef sysrootPath); + + llvm::StringRef getPathsBuffer() const { return PathsBuf.str(); } + + llvm::ArrayRef<FileBitPath> getBitPaths() const { return FileBitPaths; } + + int getPathIndex(const clang::FileEntry *FE); + + size_t getPathOffset(llvm::StringRef Path); + +private: + DirBitPath getDirBitPath(llvm::StringRef dirStr); +}; + +#endif \ No newline at end of file Index: clang/include/clang/Index/IndexingAction.h =================================================================== --- clang/include/clang/Index/IndexingAction.h +++ /dev/null @@ -1,80 +0,0 @@ -//===--- IndexingAction.h - Frontend index action ---------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_INDEX_INDEXINGACTION_H -#define LLVM_CLANG_INDEX_INDEXINGACTION_H - -#include "clang/Basic/LLVM.h" -#include "clang/Lex/PPCallbacks.h" -#include "clang/Lex/Preprocessor.h" -#include "llvm/ADT/ArrayRef.h" -#include <memory> - -namespace clang { - class ASTContext; - class ASTReader; - class ASTUnit; - class Decl; - class FrontendAction; - -namespace serialization { - class ModuleFile; -} - -namespace index { - class IndexDataConsumer; - -struct IndexingOptions { - enum class SystemSymbolFilterKind { - None, - DeclarationsOnly, - All, - }; - - SystemSymbolFilterKind SystemSymbolFilter - = SystemSymbolFilterKind::DeclarationsOnly; - bool IndexFunctionLocals = false; - bool IndexImplicitInstantiation = false; - // Whether to index macro definitions in the Preprocesor when preprocessor - // callback is not available (e.g. after parsing has finished). Note that - // macro references are not available in Proprocessor. - bool IndexMacrosInPreprocessor = false; - // Has no effect if IndexFunctionLocals are false. - bool IndexParametersInDeclarations = false; - bool IndexTemplateParameters = false; -}; - -/// Creates a frontend action that indexes all symbols (macros and AST decls). -/// \param WrappedAction another frontend action to wrap over or null. -std::unique_ptr<FrontendAction> -createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts, - std::unique_ptr<FrontendAction> WrappedAction); - -/// Recursively indexes all decls in the AST. -void indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, - IndexingOptions Opts); - -/// Recursively indexes \p Decls. -void indexTopLevelDecls(ASTContext &Ctx, Preprocessor &PP, - ArrayRef<const Decl *> Decls, - IndexDataConsumer &DataConsumer, IndexingOptions Opts); - -/// Creates a PPCallbacks that indexes macros and feeds macros to \p Consumer. -/// The caller is responsible for calling `Consumer.setPreprocessor()`. -std::unique_ptr<PPCallbacks> indexMacrosCallback(IndexDataConsumer &Consumer, - IndexingOptions Opts); - -/// Recursively indexes all top-level decls in the module. -void indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, - IndexDataConsumer &DataConsumer, IndexingOptions Opts); - -} // namespace index -} // namespace clang - -#endif Index: clang/include/clang/Index/IndexingContext.h =================================================================== --- clang/include/clang/Index/IndexingContext.h +++ clang/include/clang/Index/IndexingContext.h @@ -11,10 +11,12 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Index/IndexOptions.h" #include "clang/Index/IndexSymbol.h" -#include "clang/Index/IndexingAction.h" #include "clang/Lex/MacroInfo.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" namespace clang { class ASTContext; @@ -30,15 +32,22 @@ class Stmt; class Expr; class TypeLoc; - class SourceLocation; + class DirectoryEntry; -namespace index { + namespace index { class IndexDataConsumer; class IndexingContext { IndexingOptions IndexOpts; IndexDataConsumer &DataConsumer; ASTContext *Ctx = nullptr; + std::string SysrootPath; + + // Records whether a directory entry is system or not. + llvm::DenseMap<const DirectoryEntry *, bool> DirEntries; + // Keeps track of the last check for whether a FileID is system or + // not. This is used to speed up isSystemFile() call. + std::pair<FileID, bool> LastFileCheck; public: IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer) @@ -48,6 +57,10 @@ IndexDataConsumer &getDataConsumer() { return DataConsumer; } void setASTContext(ASTContext &ctx) { Ctx = &ctx; } + void setSysrootPath(StringRef path); + StringRef getSysrootPath() const { return SysrootPath; } + + bool isSystemFile(FileID FID); bool shouldIndex(const Decl *D); Index: clang/include/clang/Index/USRGeneration.h =================================================================== --- clang/include/clang/Index/USRGeneration.h +++ clang/include/clang/Index/USRGeneration.h @@ -97,5 +97,4 @@ } // namespace index } // namespace clang -#endif // LLVM_CLANG_INDEX_USRGENERATION_H - +#endif Index: clang/include/clang/IndexDataStore/IndexDataStore.h =================================================================== --- /dev/null +++ clang/include/clang/IndexDataStore/IndexDataStore.h @@ -0,0 +1,73 @@ +//===--- IndexDataStore.h - Index data store info -------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATASTORE_H +#define LLVM_CLANG_INDEX_INDEXDATASTORE_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Chrono.h" +#include <functional> +#include <memory> +#include <string> + +namespace clang { +namespace index { + +class IndexDataStore { +public: + ~IndexDataStore(); + + static std::unique_ptr<IndexDataStore> create(StringRef IndexStorePath, + std::string &Error); + + StringRef getFilePath() const; + bool foreachUnitName(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver); + + static unsigned getFormatVersion(); + + enum class UnitEventKind { + Removed, + Modified, + /// The directory got deleted. No more events will follow. + DirectoryDeleted, + Failure + }; + struct UnitEvent { + UnitEventKind Kind; + StringRef UnitName; + }; + struct UnitEventNotification { + bool IsInitial; + ArrayRef<UnitEvent> Events; + }; + typedef std::function<void(UnitEventNotification)> UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler Handler); + /// \returns true if an error occurred. + bool startEventListening(bool waitInitialSync, std::string &Error); + void stopEventListening(); + + void discardUnit(StringRef UnitName); + void discardRecord(StringRef RecordName); + + void purgeStaleData(); + +private: + IndexDataStore(void *Impl) : Impl(Impl) {} + + void *Impl; // An IndexDataStoreImpl. +}; + +} // namespace index +} // namespace clang + +#endif Index: clang/include/indexstore/Conversions.h =================================================================== --- /dev/null +++ clang/include/indexstore/Conversions.h @@ -0,0 +1,387 @@ +//===--- IndexDataStoreSymbolUtils.h - Utilities for indexstore symbols ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEXSTORE_CONVERSIONS_H +#define LLVM_CLANG_INDEXSTORE_CONVERSIONS_H + +#include "indexstore/indexstore.h" +#include "clang/Index/IndexSymbol.h" + +namespace clang { +namespace index { + +inline SymbolKind getSymbolKind(indexstore_symbol_kind_t K) { + switch ((uint64_t)K) { + default: + case INDEXSTORE_SYMBOL_KIND_UNKNOWN: + return SymbolKind::Unknown; + case INDEXSTORE_SYMBOL_KIND_MODULE: + return SymbolKind::Module; + case INDEXSTORE_SYMBOL_KIND_NAMESPACE: + return SymbolKind::Namespace; + case INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS: + return SymbolKind::NamespaceAlias; + case INDEXSTORE_SYMBOL_KIND_MACRO: + return SymbolKind::Macro; + case INDEXSTORE_SYMBOL_KIND_ENUM: + return SymbolKind::Enum; + case INDEXSTORE_SYMBOL_KIND_STRUCT: + return SymbolKind::Struct; + case INDEXSTORE_SYMBOL_KIND_CLASS: + return SymbolKind::Class; + case INDEXSTORE_SYMBOL_KIND_PROTOCOL: + return SymbolKind::Protocol; + case INDEXSTORE_SYMBOL_KIND_EXTENSION: + return SymbolKind::Extension; + case INDEXSTORE_SYMBOL_KIND_UNION: + return SymbolKind::Union; + case INDEXSTORE_SYMBOL_KIND_TYPEALIAS: + return SymbolKind::TypeAlias; + case INDEXSTORE_SYMBOL_KIND_FUNCTION: + return SymbolKind::Function; + case INDEXSTORE_SYMBOL_KIND_VARIABLE: + return SymbolKind::Variable; + case INDEXSTORE_SYMBOL_KIND_FIELD: + return SymbolKind::Field; + case INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT: + return SymbolKind::EnumConstant; + case INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD: + return SymbolKind::InstanceMethod; + case INDEXSTORE_SYMBOL_KIND_CLASSMETHOD: + return SymbolKind::ClassMethod; + case INDEXSTORE_SYMBOL_KIND_STATICMETHOD: + return SymbolKind::StaticMethod; + case INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY: + return SymbolKind::InstanceProperty; + case INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY: + return SymbolKind::ClassProperty; + case INDEXSTORE_SYMBOL_KIND_STATICPROPERTY: + return SymbolKind::StaticProperty; + case INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR: + return SymbolKind::Constructor; + case INDEXSTORE_SYMBOL_KIND_DESTRUCTOR: + return SymbolKind::Destructor; + case INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION: + return SymbolKind::ConversionFunction; + case INDEXSTORE_SYMBOL_KIND_PARAMETER: + return SymbolKind::Parameter; + case INDEXSTORE_SYMBOL_KIND_USING: + return SymbolKind::Using; + } +} + +inline SymbolSubKind getSymbolSubKind(indexstore_symbol_subkind_t K) { + switch ((uint64_t)K) { + default: + case INDEXSTORE_SYMBOL_SUBKIND_NONE: + return SymbolSubKind::None; + case INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR: + return SymbolSubKind::CXXCopyConstructor; + case INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR: + return SymbolSubKind::CXXMoveConstructor; + case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER: + return SymbolSubKind::AccessorGetter; + case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER: + return SymbolSubKind::AccessorSetter; + case INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME: + return SymbolSubKind::UsingTypename; + case INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE: + return SymbolSubKind::UsingValue; + } +} + +inline SymbolLanguage getSymbolLanguage(indexstore_symbol_language_t L) { + switch ((uint64_t)L) { + default: // FIXME: add an unknown language? + case INDEXSTORE_SYMBOL_LANG_C: + return SymbolLanguage::C; + case INDEXSTORE_SYMBOL_LANG_OBJC: + return SymbolLanguage::ObjC; + case INDEXSTORE_SYMBOL_LANG_CXX: + return SymbolLanguage::CXX; + case INDEXSTORE_SYMBOL_LANG_SWIFT: + return SymbolLanguage::Swift; + } +} + +inline SymbolPropertySet getSymbolProperties(uint64_t Props) { + SymbolPropertySet SymbolProperties = 0; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_GENERIC) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::Generic; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION) + SymbolProperties |= + (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION) + SymbolProperties |= + (SymbolPropertySet)SymbolProperty::TemplateSpecialization; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_UNITTEST) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::UnitTest; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::IBAnnotated; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::IBOutletCollection; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::GKInspectable; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_LOCAL) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::Local; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface; + + return SymbolProperties; +} + +inline SymbolRoleSet getSymbolRoles(uint64_t Roles) { + SymbolRoleSet SymbolRoles = 0; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DECLARATION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Declaration; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DEFINITION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Definition; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REFERENCE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Reference; + if (Roles & INDEXSTORE_SYMBOL_ROLE_READ) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Read; + if (Roles & INDEXSTORE_SYMBOL_ROLE_WRITE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Write; + if (Roles & INDEXSTORE_SYMBOL_ROLE_CALL) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Call; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DYNAMIC) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Dynamic; + if (Roles & INDEXSTORE_SYMBOL_ROLE_ADDRESSOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::AddressOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_IMPLICIT) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Implicit; + if (Roles & INDEXSTORE_SYMBOL_ROLE_UNDEFINITION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Undefinition; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationChildOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_BASEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationBaseOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationOverrideOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationReceivedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationCalledBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationExtendedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationAccessorOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationContainedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationIBTypeOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationSpecializationOf; + + return SymbolRoles; +} + +inline indexstore_symbol_kind_t getIndexStoreKind(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: + return INDEXSTORE_SYMBOL_KIND_UNKNOWN; + case SymbolKind::Module: + return INDEXSTORE_SYMBOL_KIND_MODULE; + case SymbolKind::Namespace: + return INDEXSTORE_SYMBOL_KIND_NAMESPACE; + case SymbolKind::NamespaceAlias: + return INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS; + case SymbolKind::Macro: + return INDEXSTORE_SYMBOL_KIND_MACRO; + case SymbolKind::Enum: + return INDEXSTORE_SYMBOL_KIND_ENUM; + case SymbolKind::Struct: + return INDEXSTORE_SYMBOL_KIND_STRUCT; + case SymbolKind::Class: + return INDEXSTORE_SYMBOL_KIND_CLASS; + case SymbolKind::Protocol: + return INDEXSTORE_SYMBOL_KIND_PROTOCOL; + case SymbolKind::Extension: + return INDEXSTORE_SYMBOL_KIND_EXTENSION; + case SymbolKind::Union: + return INDEXSTORE_SYMBOL_KIND_UNION; + case SymbolKind::TypeAlias: + return INDEXSTORE_SYMBOL_KIND_TYPEALIAS; + case SymbolKind::Function: + return INDEXSTORE_SYMBOL_KIND_FUNCTION; + case SymbolKind::Variable: + return INDEXSTORE_SYMBOL_KIND_VARIABLE; + case SymbolKind::Field: + return INDEXSTORE_SYMBOL_KIND_FIELD; + case SymbolKind::EnumConstant: + return INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT; + case SymbolKind::InstanceMethod: + return INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD; + case SymbolKind::ClassMethod: + return INDEXSTORE_SYMBOL_KIND_CLASSMETHOD; + case SymbolKind::StaticMethod: + return INDEXSTORE_SYMBOL_KIND_STATICMETHOD; + case SymbolKind::InstanceProperty: + return INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY; + case SymbolKind::ClassProperty: + return INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY; + case SymbolKind::StaticProperty: + return INDEXSTORE_SYMBOL_KIND_STATICPROPERTY; + case SymbolKind::Constructor: + return INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR; + case SymbolKind::Destructor: + return INDEXSTORE_SYMBOL_KIND_DESTRUCTOR; + case SymbolKind::ConversionFunction: + return INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION; + case SymbolKind::Parameter: + return INDEXSTORE_SYMBOL_KIND_PARAMETER; + case SymbolKind::Using: + return INDEXSTORE_SYMBOL_KIND_USING; + } + llvm_unreachable("unexpected symbol kind"); +} + +inline indexstore_symbol_subkind_t getIndexStoreSubKind(SymbolSubKind K) { + switch (K) { + case SymbolSubKind::None: + return INDEXSTORE_SYMBOL_SUBKIND_NONE; + case SymbolSubKind::CXXCopyConstructor: + return INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR; + case SymbolSubKind::CXXMoveConstructor: + return INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR; + case SymbolSubKind::AccessorGetter: + return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER; + case SymbolSubKind::AccessorSetter: + return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER; + case SymbolSubKind::UsingTypename: + return INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME; + case SymbolSubKind::UsingValue: + return INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE; + } + llvm_unreachable("unexpected symbol subkind"); +} + +inline indexstore_symbol_language_t getIndexStoreLang(SymbolLanguage L) { + switch (L) { + case SymbolLanguage::C: + return INDEXSTORE_SYMBOL_LANG_C; + case SymbolLanguage::ObjC: + return INDEXSTORE_SYMBOL_LANG_OBJC; + case SymbolLanguage::CXX: + return INDEXSTORE_SYMBOL_LANG_CXX; + case SymbolLanguage::Swift: + return INDEXSTORE_SYMBOL_LANG_SWIFT; + } + llvm_unreachable("unexpected symbol language"); +} + +inline uint64_t getIndexStoreProperties(SymbolPropertySet Props) { + uint64_t storeProp = 0; + applyForEachSymbolProperty(Props, [&](SymbolProperty prop) { + switch (prop) { + case SymbolProperty::Generic: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GENERIC; + break; + case SymbolProperty::TemplatePartialSpecialization: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION; + break; + case SymbolProperty::TemplateSpecialization: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION; + break; + case SymbolProperty::UnitTest: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_UNITTEST; + break; + case SymbolProperty::IBAnnotated: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED; + break; + case SymbolProperty::IBOutletCollection: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION; + break; + case SymbolProperty::GKInspectable: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE; + break; + case SymbolProperty::Local: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_LOCAL; + break; + case SymbolProperty::ProtocolInterface: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE; + break; + } + }); + return storeProp; +} + +inline uint64_t getIndexStoreRoles(SymbolRoleSet Roles) { + uint64_t storeRoles = 0; + applyForEachSymbolRole(Roles, [&](SymbolRole role) { + switch (role) { + case SymbolRole::Declaration: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DECLARATION; + break; + case SymbolRole::Definition: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DEFINITION; + break; + case SymbolRole::Reference: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REFERENCE; + break; + case SymbolRole::Read: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_READ; + break; + case SymbolRole::Write: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_WRITE; + break; + case SymbolRole::Call: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_CALL; + break; + case SymbolRole::Dynamic: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DYNAMIC; + break; + case SymbolRole::AddressOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_ADDRESSOF; + break; + case SymbolRole::Implicit: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_IMPLICIT; + break; + case SymbolRole::Undefinition: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_UNDEFINITION; + break; + case SymbolRole::RelationChildOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF; + break; + case SymbolRole::RelationBaseOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_BASEOF; + break; + case SymbolRole::RelationOverrideOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF; + break; + case SymbolRole::RelationReceivedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY; + break; + case SymbolRole::RelationCalledBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY; + break; + case SymbolRole::RelationExtendedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY; + break; + case SymbolRole::RelationAccessorOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF; + break; + case SymbolRole::RelationContainedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY; + break; + case SymbolRole::RelationIBTypeOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF; + break; + case SymbolRole::RelationSpecializationOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF; + break; + } + }); + return storeRoles; +} + +} // end namespace index +} // namespace clang + +#endif Index: clang/include/indexstore/IndexStoreCXX.h =================================================================== --- /dev/null +++ clang/include/indexstore/IndexStoreCXX.h @@ -0,0 +1,577 @@ +//===--- IndexStoreCXX.h - C++ wrapper for the Index Store C API. ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Header-only C++ wrapper for the Index Store C API. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H +#define LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H + +#include "indexstore/indexstore.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" + +namespace indexstore { +using llvm::ArrayRef; +using llvm::Optional; +using llvm::StringRef; + +static inline StringRef +stringFromIndexStoreStringRef(indexstore_string_ref_t str) { + return StringRef(str.data, str.length); +} + +template <typename Ret, typename... Params> +static inline Ret functionPtrFromFunctionRef(void *ctx, Params... params) { + auto fn = (llvm::function_ref<Ret(Params...)> *)ctx; + return (*fn)(std::forward<Params>(params)...); +} + +class IndexRecordSymbol { + indexstore_symbol_t obj; + friend class IndexRecordReader; + +public: + IndexRecordSymbol(indexstore_symbol_t obj) : obj(obj) {} + + indexstore_symbol_language_t getLanguage() { + return indexstore_symbol_get_language(obj); + } + indexstore_symbol_kind_t getKind() { return indexstore_symbol_get_kind(obj); } + indexstore_symbol_subkind_t getSubKind() { + return indexstore_symbol_get_subkind(obj); + } + uint64_t getProperties() { return indexstore_symbol_get_properties(obj); } + uint64_t getRoles() { return indexstore_symbol_get_roles(obj); } + uint64_t getRelatedRoles() { + return indexstore_symbol_get_related_roles(obj); + } + StringRef getName() { + return stringFromIndexStoreStringRef(indexstore_symbol_get_name(obj)); + } + StringRef getUSR() { + return stringFromIndexStoreStringRef(indexstore_symbol_get_usr(obj)); + } + StringRef getCodegenName() { + return stringFromIndexStoreStringRef( + indexstore_symbol_get_codegen_name(obj)); + } +}; + +class IndexSymbolRelation { + indexstore_symbol_relation_t obj; + +public: + IndexSymbolRelation(indexstore_symbol_relation_t obj) : obj(obj) {} + + uint64_t getRoles() { return indexstore_symbol_relation_get_roles(obj); } + IndexRecordSymbol getSymbol() { + return indexstore_symbol_relation_get_symbol(obj); + } +}; + +class IndexRecordOccurrence { + indexstore_occurrence_t obj; + +public: + IndexRecordOccurrence(indexstore_occurrence_t obj) : obj(obj) {} + + IndexRecordSymbol getSymbol() { + return indexstore_occurrence_get_symbol(obj); + } + uint64_t getRoles() { return indexstore_occurrence_get_roles(obj); } + + bool foreachRelation(llvm::function_ref<bool(IndexSymbolRelation)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_occurrence_relations_apply( + obj, ^bool(indexstore_symbol_relation_t sym_rel) { + return receiver(sym_rel); + }); +#else + return indexstore_occurrence_relations_apply_f(obj, &receiver, + functionPtrFromFunctionRef); +#endif + } + + std::pair<unsigned, unsigned> getLineCol() { + unsigned line, col; + indexstore_occurrence_get_line_col(obj, &line, &col); + return std::make_pair(line, col); + } +}; + +class IndexStore; +typedef std::shared_ptr<IndexStore> IndexStoreRef; + +class IndexStore { + indexstore_t obj; + friend class IndexRecordReader; + friend class IndexUnitReader; + +public: + IndexStore(StringRef path, std::string &error) { + llvm::SmallString<64> buf = path; + indexstore_error_t c_err = nullptr; + obj = indexstore_store_create(buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexStore(IndexStore &&other) : obj(other.obj) { other.obj = nullptr; } + + ~IndexStore() { indexstore_store_dispose(obj); } + + static IndexStoreRef create(StringRef path, std::string &error) { + auto storeRef = std::make_shared<IndexStore>(path, error); + if (storeRef->isInvalid()) + return nullptr; + return storeRef; + } + + static unsigned formatVersion() { return indexstore_format_version(); } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + bool foreachUnit(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_store_units_apply( + obj, sorted, ^bool(indexstore_string_ref_t unit_name) { + return receiver(stringFromIndexStoreStringRef(unit_name)); + }); +#else + return indexstore_store_units_apply_f(obj, sorted, &receiver, + functionPtrFromFunctionRef); +#endif + } + + class UnitEvent { + indexstore_unit_event_t obj; + + public: + UnitEvent(indexstore_unit_event_t obj) : obj(obj) {} + + enum class Kind { Removed, Modified, DirectoryDeleted, Failure }; + Kind getKind() const { + indexstore_unit_event_kind_t c_k = indexstore_unit_event_get_kind(obj); + Kind K; + switch (c_k) { + case INDEXSTORE_UNIT_EVENT_REMOVED: + K = Kind::Removed; + break; + case INDEXSTORE_UNIT_EVENT_MODIFIED: + K = Kind::Modified; + break; + case INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED: + K = Kind::DirectoryDeleted; + break; + case INDEXSTORE_UNIT_EVENT_FAILURE: + K = Kind::Failure; + break; + } + return K; + } + + StringRef getUnitName() const { + return stringFromIndexStoreStringRef( + indexstore_unit_event_get_unit_name(obj)); + } + }; + + class UnitEventNotification { + indexstore_unit_event_notification_t obj; + + public: + UnitEventNotification(indexstore_unit_event_notification_t obj) + : obj(obj) {} + + bool isInitial() const { + return indexstore_unit_event_notification_is_initial(obj); + } + size_t getEventsCount() const { + return indexstore_unit_event_notification_get_events_count(obj); + } + UnitEvent getEvent(size_t index) const { + return indexstore_unit_event_notification_get_event(obj, index); + } + }; + + typedef std::function<void(UnitEventNotification)> UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler handler) { +#if INDEXSTORE_HAS_BLOCKS + if (!handler) { + indexstore_store_set_unit_event_handler(obj, nullptr); + return; + } + + indexstore_store_set_unit_event_handler( + obj, ^(indexstore_unit_event_notification_t evt_note) { + handler(UnitEventNotification(evt_note)); + }); +#else + if (!handler) { + indexstore_store_set_unit_event_handler_f(obj, nullptr, nullptr, nullptr); + return; + } + + auto fnPtr = new UnitEventHandler(handler); + indexstore_store_set_unit_event_handler_f(obj, fnPtr, event_handler, + event_handler_finalizer); +#endif + } + +private: + static void event_handler(void *ctx, + indexstore_unit_event_notification_t evt) { + auto fnPtr = (UnitEventHandler *)ctx; + (*fnPtr)(evt); + } + static void event_handler_finalizer(void *ctx) { + auto fnPtr = (UnitEventHandler *)ctx; + delete fnPtr; + } + +public: + bool startEventListening(bool waitInitialSync, std::string &error) { + indexstore_unit_event_listen_options_t opts; + opts.wait_initial_sync = waitInitialSync; + indexstore_error_t c_err = nullptr; + bool ret = indexstore_store_start_unit_event_listening( + obj, &opts, sizeof(opts), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + return ret; + } + + void stopEventListening() { + return indexstore_store_stop_unit_event_listening(obj); + } + + void discardUnit(StringRef UnitName) { + llvm::SmallString<64> buf = UnitName; + indexstore_store_discard_unit(obj, buf.c_str()); + } + + void discardRecord(StringRef RecordName) { + llvm::SmallString<64> buf = RecordName; + indexstore_store_discard_record(obj, buf.c_str()); + } + + void getUnitNameFromOutputPath(StringRef outputPath, + llvm::SmallVectorImpl<char> &nameBuf) { + llvm::SmallString<256> buf = outputPath; + llvm::SmallString<64> unitName; + unitName.resize(64); + size_t nameLen = indexstore_store_get_unit_name_from_output_path( + obj, buf.c_str(), unitName.data(), unitName.size()); + if (nameLen + 1 > unitName.size()) { + unitName.resize(nameLen + 1); + indexstore_store_get_unit_name_from_output_path( + obj, buf.c_str(), unitName.data(), unitName.size()); + } + nameBuf.append(unitName.begin(), unitName.begin() + nameLen); + } + + void purgeStaleData() { indexstore_store_purge_stale_data(obj); } +}; + +class IndexRecordReader { + indexstore_record_reader_t obj; + +public: + IndexRecordReader(IndexStore &store, StringRef recordName, + std::string &error) { + llvm::SmallString<64> buf = recordName; + indexstore_error_t c_err = nullptr; + obj = indexstore_record_reader_create(store.obj, buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexRecordReader(IndexRecordReader &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexRecordReader() { indexstore_record_reader_dispose(obj); } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + /// Goes through and passes record decls, after filtering using a \c Checker + /// function. + /// + /// Resulting decls can be used as filter for \c foreachOccurrence. This + /// allows allocating memory only for the record decls that the caller is + /// interested in. + bool + searchSymbols(llvm::function_ref<bool(IndexRecordSymbol, bool &stop)> filter, + llvm::function_ref<void(IndexRecordSymbol)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_search_symbols( + obj, + ^bool(indexstore_symbol_t symbol, bool *stop) { + return filter(symbol, *stop); + }, + ^(indexstore_symbol_t symbol) { + receiver(symbol); + }); +#else + return indexstore_record_reader_search_symbols_f( + obj, &filter, functionPtrFromFunctionRef, &receiver, + functionPtrFromFunctionRef); +#endif + } + + bool foreachSymbol(bool noCache, + llvm::function_ref<bool(IndexRecordSymbol)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_symbols_apply( + obj, noCache, ^bool(indexstore_symbol_t sym) { + return receiver(sym); + }); +#else + return indexstore_record_reader_symbols_apply_f(obj, noCache, &receiver, + functionPtrFromFunctionRef); +#endif + } + + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. An empty array indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + bool + foreachOccurrence(ArrayRef<IndexRecordSymbol> symbolsFilter, + ArrayRef<IndexRecordSymbol> relatedSymbolsFilter, + llvm::function_ref<bool(IndexRecordOccurrence)> receiver) { + llvm::SmallVector<indexstore_symbol_t, 16> c_symbolsFilter; + c_symbolsFilter.reserve(symbolsFilter.size()); + for (IndexRecordSymbol sym : symbolsFilter) { + c_symbolsFilter.push_back(sym.obj); + } + llvm::SmallVector<indexstore_symbol_t, 16> c_relatedSymbolsFilter; + c_relatedSymbolsFilter.reserve(relatedSymbolsFilter.size()); + for (IndexRecordSymbol sym : relatedSymbolsFilter) { + c_relatedSymbolsFilter.push_back(sym.obj); + } +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_of_symbols_apply( + obj, c_symbolsFilter.data(), c_symbolsFilter.size(), + c_relatedSymbolsFilter.data(), c_relatedSymbolsFilter.size(), + ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return indexstore_record_reader_occurrences_of_symbols_apply_f( + obj, c_symbolsFilter.data(), c_symbolsFilter.size(), + c_relatedSymbolsFilter.data(), c_relatedSymbolsFilter.size(), &receiver, + functionPtrFromFunctionRef); +#endif + } + + bool + foreachOccurrence(llvm::function_ref<bool(IndexRecordOccurrence)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_apply( + obj, ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return indexstore_record_reader_occurrences_apply_f( + obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachOccurrenceInLineRange( + unsigned lineStart, unsigned lineEnd, + llvm::function_ref<bool(IndexRecordOccurrence)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_in_line_range_apply( + obj, lineStart, lineEnd, ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return indexstore_record_reader_occurrences_in_line_range_apply_f( + obj, lineStart, lineEnd, &receiver, functionPtrFromFunctionRef); +#endif + } +}; + +class IndexUnitDependency { + indexstore_unit_dependency_t obj; + friend class IndexUnitReader; + +public: + IndexUnitDependency(indexstore_unit_dependency_t obj) : obj(obj) {} + + enum class DependencyKind { + Unit, + Record, + File, + }; + DependencyKind getKind() { + switch (indexstore_unit_dependency_get_kind(obj)) { + case INDEXSTORE_UNIT_DEPENDENCY_UNIT: + return DependencyKind::Unit; + case INDEXSTORE_UNIT_DEPENDENCY_RECORD: + return DependencyKind::Record; + case INDEXSTORE_UNIT_DEPENDENCY_FILE: + return DependencyKind::File; + } + } + bool isSystem() { return indexstore_unit_dependency_is_system(obj); } + StringRef getName() { + return stringFromIndexStoreStringRef( + indexstore_unit_dependency_get_name(obj)); + } + StringRef getFilePath() { + return stringFromIndexStoreStringRef( + indexstore_unit_dependency_get_filepath(obj)); + } + StringRef getModuleName() { + return stringFromIndexStoreStringRef( + indexstore_unit_dependency_get_modulename(obj)); + } +}; + +class IndexUnitInclude { + indexstore_unit_include_t obj; + friend class IndexUnitReader; + +public: + IndexUnitInclude(indexstore_unit_include_t obj) : obj(obj) {} + + StringRef getSourcePath() { + return stringFromIndexStoreStringRef( + indexstore_unit_include_get_source_path(obj)); + } + StringRef getTargetPath() { + return stringFromIndexStoreStringRef( + indexstore_unit_include_get_target_path(obj)); + } + unsigned getSourceLine() { + return indexstore_unit_include_get_source_line(obj); + } +}; + +class IndexUnitReader { + indexstore_unit_reader_t obj; + +public: + IndexUnitReader(IndexStore &store, StringRef unitName, std::string &error) { + llvm::SmallString<64> buf = unitName; + indexstore_error_t c_err = nullptr; + obj = indexstore_unit_reader_create(store.obj, buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexUnitReader(IndexUnitReader &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexUnitReader() { indexstore_unit_reader_dispose(obj); } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + StringRef getProviderIdentifier() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_provider_identifier(obj)); + } + StringRef getProviderVersion() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_provider_version(obj)); + } + + timespec getModificationTime() { + int64_t seconds, nanoseconds; + indexstore_unit_reader_get_modification_time(obj, &seconds, &nanoseconds); + timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; + return ts; + } + + bool isSystemUnit() { return indexstore_unit_reader_is_system_unit(obj); } + bool isModuleUnit() { return indexstore_unit_reader_is_module_unit(obj); } + bool isDebugCompilation() { + return indexstore_unit_reader_is_debug_compilation(obj); + } + bool hasMainFile() { return indexstore_unit_reader_has_main_file(obj); } + + StringRef getMainFilePath() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_main_file(obj)); + } + StringRef getModuleName() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_module_name(obj)); + } + StringRef getWorkingDirectory() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_working_dir(obj)); + } + StringRef getOutputFile() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_output_file(obj)); + } + StringRef getSysrootPath() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_sysroot_path(obj)); + } + StringRef getTarget() { + return stringFromIndexStoreStringRef( + indexstore_unit_reader_get_target(obj)); + } + + bool + foreachDependency(llvm::function_ref<bool(IndexUnitDependency)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_dependencies_apply( + obj, ^bool(indexstore_unit_dependency_t dep) { + return receiver(dep); + }); +#else + return indexstore_unit_reader_dependencies_apply_f( + obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachInclude(llvm::function_ref<bool(IndexUnitInclude)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_includes_apply( + obj, ^bool(indexstore_unit_include_t inc) { + return receiver(inc); + }); +#else + return indexstore_unit_reader_includes_apply_f(obj, &receiver, + functionPtrFromFunctionRef); +#endif + } +}; + +} // namespace indexstore + +#endif Index: clang/include/indexstore/indexstore.h =================================================================== --- /dev/null +++ clang/include/indexstore/indexstore.h @@ -0,0 +1,532 @@ +/*===-- indexstore/indexstore.h - Index Store C API ----------------- C -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a C API for the index store. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H +#define LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <time.h> + +/** + * \brief The version constants for the Index Store C API. + * INDEXSTORE_VERSION_MINOR should increase when there are API additions. + * INDEXSTORE_VERSION_MAJOR is intended for "major" source/ABI breaking changes. + */ +#define INDEXSTORE_VERSION_MAJOR 0 +#define INDEXSTORE_VERSION_MINOR 11 + +#define INDEXSTORE_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1)) + +#define INDEXSTORE_VERSION \ + INDEXSTORE_VERSION_ENCODE(INDEXSTORE_VERSION_MAJOR, INDEXSTORE_VERSION_MINOR) + +#define INDEXSTORE_VERSION_STRINGIZE_(major, minor) #major "." #minor +#define INDEXSTORE_VERSION_STRINGIZE(major, minor) \ + INDEXSTORE_VERSION_STRINGIZE_(major, minor) + +#define INDEXSTORE_VERSION_STRING \ + INDEXSTORE_VERSION_STRINGIZE(INDEXSTORE_VERSION_MAJOR, \ + INDEXSTORE_VERSION_MINOR) + +#ifdef __cplusplus +#define INDEXSTORE_BEGIN_DECLS extern "C" { +#define INDEXSTORE_END_DECLS } +#else +#define INDEXSTORE_BEGIN_DECLS +#define INDEXSTORE_END_DECLS +#endif + +#ifndef INDEXSTORE_PUBLIC +#ifdef _WIN32 +#ifdef IndexStore_EXPORTS +#define INDEXSTORE_PUBLIC __declspec(dllexport) +#else +#define INDEXSTORE_PUBLIC __declspec(dllimport) +#endif +#else +#define INDEXSTORE_PUBLIC +#endif +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#if __has_feature(blocks) +#define INDEXSTORE_HAS_BLOCKS 1 +#else +#define INDEXSTORE_HAS_BLOCKS 0 +#endif + +#if __has_attribute(noescape) +#define INDEXSTORE_NOESCAPE __attribute__((noescape)) +#else +#define INDEXSTORE_NOESCAPE +#endif + +INDEXSTORE_BEGIN_DECLS + +typedef void *indexstore_error_t; + +INDEXSTORE_PUBLIC const char * + indexstore_error_get_description(indexstore_error_t); + +INDEXSTORE_PUBLIC void indexstore_error_dispose(indexstore_error_t); + +typedef struct { + const char *data; + size_t length; +} indexstore_string_ref_t; + +INDEXSTORE_PUBLIC unsigned indexstore_format_version(void); + +typedef void *indexstore_t; + +INDEXSTORE_PUBLIC indexstore_t +indexstore_store_create(const char *store_path, indexstore_error_t *error); + +INDEXSTORE_PUBLIC void indexstore_store_dispose(indexstore_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool indexstore_store_units_apply( + indexstore_t, unsigned sorted, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_string_ref_t unit_name)); +#endif + +INDEXSTORE_PUBLIC bool indexstore_store_units_apply_f( + indexstore_t, unsigned sorted, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_string_ref_t unit_name)); + +typedef void *indexstore_unit_event_notification_t; +typedef void *indexstore_unit_event_t; + +INDEXSTORE_PUBLIC size_t indexstore_unit_event_notification_get_events_count( + indexstore_unit_event_notification_t); + +INDEXSTORE_PUBLIC indexstore_unit_event_t +indexstore_unit_event_notification_get_event( + indexstore_unit_event_notification_t, size_t index); + +INDEXSTORE_PUBLIC bool indexstore_unit_event_notification_is_initial( + indexstore_unit_event_notification_t); + +typedef enum { + INDEXSTORE_UNIT_EVENT_REMOVED = 1, + INDEXSTORE_UNIT_EVENT_MODIFIED = 2, + INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 3, + INDEXSTORE_UNIT_EVENT_FAILURE = 4, +} indexstore_unit_event_kind_t; + +INDEXSTORE_PUBLIC indexstore_unit_event_kind_t + indexstore_unit_event_get_kind(indexstore_unit_event_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_event_get_unit_name(indexstore_unit_event_t); + +#if INDEXSTORE_HAS_BLOCKS +typedef void (^indexstore_unit_event_handler_t)( + indexstore_unit_event_notification_t); + +INDEXSTORE_PUBLIC void indexstore_store_set_unit_event_handler( + indexstore_t, indexstore_unit_event_handler_t handler); +#endif + +INDEXSTORE_PUBLIC void indexstore_store_set_unit_event_handler_f( + indexstore_t, void *context, + void (*handler)(void *context, indexstore_unit_event_notification_t), + void (*finalizer)(void *context)); + +typedef struct { + /// If true, \c indexstore_store_start_unit_event_listening will block until + /// the initial set of units is passed to the unit event handler, otherwise + /// the function will return and the initial set will be passed + /// asynchronously. + bool wait_initial_sync; +} indexstore_unit_event_listen_options_t; + +INDEXSTORE_PUBLIC bool indexstore_store_start_unit_event_listening( + indexstore_t, indexstore_unit_event_listen_options_t *, + size_t listen_options_struct_size, indexstore_error_t *error); + +INDEXSTORE_PUBLIC void indexstore_store_stop_unit_event_listening(indexstore_t); + +INDEXSTORE_PUBLIC void indexstore_store_discard_unit(indexstore_t, + const char *unit_name); + +INDEXSTORE_PUBLIC void indexstore_store_discard_record(indexstore_t, + const char *record_name); + +INDEXSTORE_PUBLIC void indexstore_store_purge_stale_data(indexstore_t); + +/// Determines the unit name from the \c output_path and writes it out in the +/// \c name_buf buffer. It doesn't write more than \c buf_size. +/// \returns the length of the name. If this is larger than \c buf_size, the +/// caller should call the function again with a buffer of the appropriate size. +INDEXSTORE_PUBLIC size_t indexstore_store_get_unit_name_from_output_path( + indexstore_t store, const char *output_path, char *name_buf, + size_t buf_size); + +/// \returns true if an error occurred, false otherwise. +INDEXSTORE_PUBLIC bool indexstore_store_get_unit_modification_time( + indexstore_t store, const char *unit_name, int64_t *seconds, + int64_t *nanoseconds, indexstore_error_t *error); + +typedef void *indexstore_symbol_t; + +typedef enum { + INDEXSTORE_SYMBOL_KIND_UNKNOWN = 0, + INDEXSTORE_SYMBOL_KIND_MODULE = 1, + INDEXSTORE_SYMBOL_KIND_NAMESPACE = 2, + INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS = 3, + INDEXSTORE_SYMBOL_KIND_MACRO = 4, + INDEXSTORE_SYMBOL_KIND_ENUM = 5, + INDEXSTORE_SYMBOL_KIND_STRUCT = 6, + INDEXSTORE_SYMBOL_KIND_CLASS = 7, + INDEXSTORE_SYMBOL_KIND_PROTOCOL = 8, + INDEXSTORE_SYMBOL_KIND_EXTENSION = 9, + INDEXSTORE_SYMBOL_KIND_UNION = 10, + INDEXSTORE_SYMBOL_KIND_TYPEALIAS = 11, + INDEXSTORE_SYMBOL_KIND_FUNCTION = 12, + INDEXSTORE_SYMBOL_KIND_VARIABLE = 13, + INDEXSTORE_SYMBOL_KIND_FIELD = 14, + INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT = 15, + INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD = 16, + INDEXSTORE_SYMBOL_KIND_CLASSMETHOD = 17, + INDEXSTORE_SYMBOL_KIND_STATICMETHOD = 18, + INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY = 19, + INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY = 20, + INDEXSTORE_SYMBOL_KIND_STATICPROPERTY = 21, + INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR = 22, + INDEXSTORE_SYMBOL_KIND_DESTRUCTOR = 23, + INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION = 24, + INDEXSTORE_SYMBOL_KIND_PARAMETER = 25, + INDEXSTORE_SYMBOL_KIND_USING = 26, + + INDEXSTORE_SYMBOL_KIND_COMMENTTAG = 1000, +} indexstore_symbol_kind_t; + +typedef enum { + INDEXSTORE_SYMBOL_SUBKIND_NONE = 0, + INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR = 1, + INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR = 2, + INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER = 3, + INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER = 4, + INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME = 5, + INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE = 6, + + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET = 1000, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET = 1001, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR = 1002, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR = 1003, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT = 1004, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS = 1005, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM = 1006, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL = 1007, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR = 1008, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR = 1009, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR = 1010, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT = 1011, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE = 1012, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM = 1013, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORREAD = 1014, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY = 1015, +} indexstore_symbol_subkind_t; + +typedef enum { + INDEXSTORE_SYMBOL_PROPERTY_GENERIC = 1 << 0, + INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION = 1 << 1, + INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION = 1 << 2, + INDEXSTORE_SYMBOL_PROPERTY_UNITTEST = 1 << 3, + INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED = 1 << 4, + INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION = 1 << 5, + INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE = 1 << 6, + INDEXSTORE_SYMBOL_PROPERTY_LOCAL = 1 << 7, + INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE = 1 << 8, +} indexstore_symbol_property_t; + +typedef enum { + INDEXSTORE_SYMBOL_LANG_C = 0, + INDEXSTORE_SYMBOL_LANG_OBJC = 1, + INDEXSTORE_SYMBOL_LANG_CXX = 2, + + INDEXSTORE_SYMBOL_LANG_SWIFT = 100, +} indexstore_symbol_language_t; + +typedef enum { + INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, + INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, + INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, + INDEXSTORE_SYMBOL_ROLE_READ = 1 << 3, + INDEXSTORE_SYMBOL_ROLE_WRITE = 1 << 4, + INDEXSTORE_SYMBOL_ROLE_CALL = 1 << 5, + INDEXSTORE_SYMBOL_ROLE_DYNAMIC = 1 << 6, + INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7, + INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8, + INDEXSTORE_SYMBOL_ROLE_UNDEFINITION = 1 << 19, + + // Relation roles. + INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 9, + INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 10, + INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 11, + INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 12, + INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 13, + INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 14, + INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 15, + INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 16, + INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 17, + INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 18, +} indexstore_symbol_role_t; + +INDEXSTORE_PUBLIC indexstore_symbol_language_t + indexstore_symbol_get_language(indexstore_symbol_t); + +INDEXSTORE_PUBLIC + indexstore_symbol_kind_t indexstore_symbol_get_kind(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_subkind_t + indexstore_symbol_get_subkind(indexstore_symbol_t); + +INDEXSTORE_PUBLIC + uint64_t indexstore_symbol_get_properties(indexstore_symbol_t); + +INDEXSTORE_PUBLIC uint64_t indexstore_symbol_get_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC + uint64_t indexstore_symbol_get_related_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC + indexstore_string_ref_t indexstore_symbol_get_name(indexstore_symbol_t); + +INDEXSTORE_PUBLIC + indexstore_string_ref_t indexstore_symbol_get_usr(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_symbol_get_codegen_name(indexstore_symbol_t); + +typedef void *indexstore_symbol_relation_t; + +INDEXSTORE_PUBLIC + uint64_t indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t); + +INDEXSTORE_PUBLIC indexstore_symbol_t + indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t); + +typedef void *indexstore_occurrence_t; + +INDEXSTORE_PUBLIC indexstore_symbol_t + indexstore_occurrence_get_symbol(indexstore_occurrence_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool indexstore_occurrence_relations_apply( + indexstore_occurrence_t, INDEXSTORE_NOESCAPE bool (^applier)( + indexstore_symbol_relation_t symbol_rel)); +#endif + +INDEXSTORE_PUBLIC bool indexstore_occurrence_relations_apply_f( + indexstore_occurrence_t, void *context, + INDEXSTORE_NOESCAPE bool (*applier)( + void *context, indexstore_symbol_relation_t symbol_rel)); + +INDEXSTORE_PUBLIC + uint64_t indexstore_occurrence_get_roles(indexstore_occurrence_t); + +INDEXSTORE_PUBLIC void +indexstore_occurrence_get_line_col(indexstore_occurrence_t, unsigned *line, + unsigned *column); + +typedef void *indexstore_record_reader_t; + +INDEXSTORE_PUBLIC indexstore_record_reader_t indexstore_record_reader_create( + indexstore_t store, const char *record_name, indexstore_error_t *error); + +INDEXSTORE_PUBLIC void + indexstore_record_reader_dispose(indexstore_record_reader_t); + +#if INDEXSTORE_HAS_BLOCKS +/// Goes through the symbol data and passes symbols to \c receiver, for the +/// symbol data that \c filter returns true on. +/// +/// This allows allocating memory only for the record symbols that the caller is +/// interested in. +INDEXSTORE_PUBLIC bool indexstore_record_reader_search_symbols( + indexstore_record_reader_t, + INDEXSTORE_NOESCAPE bool (^filter)(indexstore_symbol_t symbol, bool *stop), + INDEXSTORE_NOESCAPE void (^receiver)(indexstore_symbol_t symbol)); + +/// \param nocache if true, avoids allocating memory for the symbols. +/// Useful when the caller does not intend to keep \c indexstore_record_reader_t +/// for more queries. +INDEXSTORE_PUBLIC bool indexstore_record_reader_symbols_apply( + indexstore_record_reader_t, bool nocache, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_apply( + indexstore_record_reader_t, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_in_line_range_apply( + indexstore_record_reader_t, unsigned line_start, unsigned line_count, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_occurrence_t occur)); + +/// \param symbols if non-zero \c symbols_count, indicates the list of symbols +/// that we want to get occurrences for. An empty array indicates that we want +/// occurrences for all symbols. +/// \param related_symbols Same as \c symbols but for related symbols. +INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_of_symbols_apply( + indexstore_record_reader_t, indexstore_symbol_t *symbols, + size_t symbols_count, indexstore_symbol_t *related_symbols, + size_t related_symbols_count, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_occurrence_t occur)); +#endif + +INDEXSTORE_PUBLIC bool indexstore_record_reader_search_symbols_f( + indexstore_record_reader_t, void *filter_ctx, + INDEXSTORE_NOESCAPE bool (*filter)(void *filter_ctx, + indexstore_symbol_t symbol, bool *stop), + void *receiver_ctx, + INDEXSTORE_NOESCAPE void (*receiver)(void *receiver_ctx, + indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool indexstore_record_reader_symbols_apply_f( + indexstore_record_reader_t, bool nocache, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_apply_f( + indexstore_record_reader_t, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_in_line_range_apply_f( + indexstore_record_reader_t, unsigned line_start, unsigned line_count, + void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_of_symbols_apply_f( + indexstore_record_reader_t, indexstore_symbol_t *symbols, + size_t symbols_count, indexstore_symbol_t *related_symbols, + size_t related_symbols_count, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_occurrence_t occur)); + +typedef void *indexstore_unit_reader_t; + +INDEXSTORE_PUBLIC indexstore_unit_reader_t indexstore_unit_reader_create( + indexstore_t store, const char *unit_name, indexstore_error_t *error); + +INDEXSTORE_PUBLIC void indexstore_unit_reader_dispose(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC void indexstore_unit_reader_get_modification_time( + indexstore_unit_reader_t, int64_t *seconds, int64_t *nanoseconds); + +INDEXSTORE_PUBLIC bool + indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool + indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool + indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool + indexstore_unit_reader_has_main_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_main_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_module_name(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_output_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_reader_get_target(indexstore_unit_reader_t); + +typedef void *indexstore_unit_dependency_t; +typedef void *indexstore_unit_include_t; + +typedef enum { + INDEXSTORE_UNIT_DEPENDENCY_UNIT = 1, + INDEXSTORE_UNIT_DEPENDENCY_RECORD = 2, + INDEXSTORE_UNIT_DEPENDENCY_FILE = 3, +} indexstore_unit_dependency_kind_t; + +INDEXSTORE_PUBLIC indexstore_unit_dependency_kind_t + indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC bool + indexstore_unit_dependency_is_system(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_dependency_get_name(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_include_get_source_path(indexstore_unit_include_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t + indexstore_unit_include_get_target_path(indexstore_unit_include_t); + +INDEXSTORE_PUBLIC unsigned + indexstore_unit_include_get_source_line(indexstore_unit_include_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool indexstore_unit_reader_dependencies_apply( + indexstore_unit_reader_t, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_unit_dependency_t)); + +INDEXSTORE_PUBLIC bool indexstore_unit_reader_includes_apply( + indexstore_unit_reader_t, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_unit_include_t)); +#endif + +INDEXSTORE_PUBLIC bool indexstore_unit_reader_dependencies_apply_f( + indexstore_unit_reader_t, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_unit_dependency_t)); + +INDEXSTORE_PUBLIC bool indexstore_unit_reader_includes_apply_f( + indexstore_unit_reader_t, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_unit_include_t)); + +INDEXSTORE_END_DECLS + +#endif Index: clang/lib/CMakeLists.txt =================================================================== --- clang/lib/CMakeLists.txt +++ clang/lib/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(Tooling) add_subdirectory(DirectoryWatcher) add_subdirectory(Index) +add_subdirectory(IndexDataStore) if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) endif() Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -1378,7 +1378,9 @@ } // Assume associated files are based off of the first temporary file. - CrashReportInfo CrashInfo(TempFiles[0], VFS); + CrashReportInfo CrashInfo( + TempFiles[0], VFS, + C.getArgs().getLastArgValue(options::OPT_index_store_path)); llvm::SmallString<128> Script(CrashInfo.Filename); llvm::sys::path::replace_extension(Script, "sh"); Index: clang/lib/Driver/Job.cpp =================================================================== --- clang/lib/Driver/Job.cpp +++ clang/lib/Driver/Job.cpp @@ -72,6 +72,8 @@ .Default(false); if (IsInclude) return !HaveCrashVFS; + if (StringRef(Flag).startswith("-index-store-path")) + return true; // The remaining flags are treated as a single argument. @@ -225,6 +227,7 @@ } bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty(); + bool HaveIndexStorePath = CrashInfo && !CrashInfo->IndexStorePath.empty(); for (size_t i = 0, e = Args.size(); i < e; ++i) { const char *const Arg = Args[i]; @@ -288,6 +291,23 @@ printArg(OS, ModCachePath, Quote); } + if (CrashInfo && HaveIndexStorePath) { + SmallString<128> IndexStoreDir; + + if (HaveCrashVFS) { + IndexStoreDir = llvm::sys::path::parent_path( + llvm::sys::path::parent_path(CrashInfo->VFSPath)); + llvm::sys::path::append(IndexStoreDir, "index-store"); + } else { + IndexStoreDir = "index-store"; + } + + OS << ' '; + printArg(OS, "-index-store-path", Quote); + OS << ' '; + printArg(OS, IndexStoreDir.c_str(), Quote); + } + if (ResponseFile != nullptr) { OS << "\n Arguments passed via response file:\n"; writeResponseFile(OS); Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -4227,6 +4227,25 @@ RenderARCMigrateToolOptions(D, Args, CmdArgs); + if (Args.hasArg(options::OPT_index_store_path)) { + Args.AddLastArg(CmdArgs, options::OPT_index_store_path); + Args.AddLastArg(CmdArgs, options::OPT_index_ignore_system_symbols); + Args.AddLastArg(CmdArgs, options::OPT_index_record_codegen_name); + + // If '-o' is passed along with '-fsyntax-only' pass it along the cc1 + // invocation so that the index action knows what the out file is. + if (isa<CompileJobAction>(JA) && JA.getType() == types::TY_Nothing) { + Args.AddLastArg(CmdArgs, options::OPT_o); + } + } + + if (const char *IdxStorePath = ::getenv("CLANG_PROJECT_INDEX_PATH")) { + CmdArgs.push_back("-index-store-path"); + CmdArgs.push_back(IdxStorePath); + CmdArgs.push_back("-index-ignore-system-symbols"); + CmdArgs.push_back("-index-record-codegen-name"); + } + // Add preprocessing options like -I, -D, etc. if we are using the // preprocessor. // Index: clang/lib/Frontend/CompilerInstance.cpp =================================================================== --- clang/lib/Frontend/CompilerInstance.cpp +++ clang/lib/Frontend/CompilerInstance.cpp @@ -1107,6 +1107,10 @@ SourceMgr.pushModuleBuildStack(ModuleName, FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager())); + // Pass along the GenModuleActionWrapper callback + auto wrapGenModuleAction = ImportingInstance.getGenModuleActionWrapper(); + Instance.setGenModuleActionWrapper(wrapGenModuleAction); + // If we're collecting module dependencies, we need to share a collector // between all of the module CompilerInstances. Other than that, we don't // want to produce any dependency output from the module build. @@ -1124,8 +1128,14 @@ llvm::CrashRecoveryContext CRC; CRC.RunSafelyOnThread( [&]() { - GenerateModuleFromModuleMapAction Action; - Instance.ExecuteAction(Action); + // FIXME: I have no idea what the best way to do this is, but it's + // probably not this. Interfaces changed upstream. + std::unique_ptr<FrontendAction> Action( + new GenerateModuleFromModuleMapAction); + if (wrapGenModuleAction) { + Action = wrapGenModuleAction(FrontendOpts, std::move(Action)); + } + Instance.ExecuteAction(*Action); }, DesiredStackSize); Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -1877,6 +1877,10 @@ << "ARC migration" << "ObjC migration"; } + Opts.IndexStorePath = Args.getLastArgValue(OPT_index_store_path); + Opts.IndexIgnoreSystemSymbols = Args.hasArg(OPT_index_ignore_system_symbols); + Opts.IndexRecordCodegenName = Args.hasArg(OPT_index_record_codegen_name); + InputKind DashX(InputKind::Unknown); if (const Arg *A = Args.getLastArg(OPT_x)) { StringRef XValue = A->getValue(); Index: clang/lib/FrontendTool/CMakeLists.txt =================================================================== --- clang/lib/FrontendTool/CMakeLists.txt +++ clang/lib/FrontendTool/CMakeLists.txt @@ -8,6 +8,8 @@ clangCodeGen clangDriver clangFrontend + clangIndex + clangIndexWhileBuilding clangRewriteFrontend ) Index: clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp =================================================================== --- clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -22,6 +22,8 @@ #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/Utils.h" #include "clang/FrontendTool/Utils.h" +#include "clang/Index/GenerateIndexAction.h" +#include "clang/Index/IndexWhileBuilding/EmitIndexAction.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "llvm/Option/OptTable.h" @@ -175,6 +177,21 @@ } #endif + if (!FEOpts.IndexStorePath.empty()) { + auto ActionFactory = [](const FrontendOptions &Opts, + std::unique_ptr<FrontendAction> Action) { + return llvm::make_unique<index::IndexActionWrapper>( + llvm::make_unique<index::EmitIndexAction>( + index::parseIndexGenerationOptions(Opts), + index::parseEmitIndexOptions(Opts)), + std::move(Action)); + }; + + Act = ActionFactory(FEOpts, std::move(Act)); + + CI.setGenModuleActionWrapper(ActionFactory); + } + // If there are any AST files to merge, create a frontend action // adaptor to perform the merge. if (!FEOpts.ASTMergeFiles.empty()) Index: clang/lib/Index/CMakeLists.txt =================================================================== --- clang/lib/Index/CMakeLists.txt +++ clang/lib/Index/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + BitReader Core Support ) @@ -6,17 +7,17 @@ add_clang_library(clangIndex CodegenNameGenerator.cpp CommentToXML.cpp - FileIndexRecord.cpp + GenerateIndexAction.cpp + IndexAction.cpp IndexBody.cpp IndexDecl.cpp - IndexingAction.cpp IndexingContext.cpp + IndexOptions.cpp IndexSymbol.cpp IndexTypeSourceInfo.cpp USRGeneration.cpp ADDITIONAL_HEADERS - IndexingContext.h SimpleFormatContext.h LINK_LIBS @@ -29,3 +30,5 @@ clangSerialization clangToolingCore ) + +add_subdirectory(IndexWhileBuilding) \ No newline at end of file Index: clang/lib/Index/CodegenNameGenerator.cpp =================================================================== --- clang/lib/Index/CodegenNameGenerator.cpp +++ clang/lib/Index/CodegenNameGenerator.cpp @@ -12,16 +12,203 @@ #include "clang/Index/CodegenNameGenerator.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/VTableBuilder.h" +#include "llvm/IR/Mangler.h" using namespace clang; using namespace clang::index; +struct CodegenNameGenerator::Implementation { + std::unique_ptr<MangleContext> MC; + llvm::DataLayout DL; + + Implementation(ASTContext &Ctx) + : MC(Ctx.createMangleContext()), DL(Ctx.getTargetInfo().getDataLayout()) { + } + + bool writeName(const Decl *D, raw_ostream &OS) { + // First apply frontend mangling. + SmallString<128> FrontendBuf; + llvm::raw_svector_ostream FrontendBufOS(FrontendBuf); + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isDependentContext()) + return true; + if (writeFuncOrVarName(FD, FrontendBufOS)) + return true; + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + if (writeFuncOrVarName(VD, FrontendBufOS)) + return true; + } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + MC->mangleObjCMethodNameWithoutSize(MD, OS); + return false; + } else if (auto *ID = dyn_cast<ObjCInterfaceDecl>(D)) { + writeObjCClassName(ID, FrontendBufOS); + } else { + return true; + } + + // Now apply backend mangling. + llvm::Mangler::getNameWithPrefix(OS, FrontendBufOS.str(), DL); + return false; + } + + std::string getName(const Decl *D) { + std::string Name; + { + llvm::raw_string_ostream OS(Name); + // FIXME error handling + writeName(D, OS); + } + return Name; + } + + enum ObjCKind { + ObjCClass, + ObjCMetaclass, + }; + + std::vector<std::string> getAllManglings(const ObjCContainerDecl *OCD) { + StringRef ClassName; + if (const auto *OID = dyn_cast<ObjCInterfaceDecl>(OCD)) + ClassName = OID->getObjCRuntimeNameAsString(); + else if (const auto *OID = dyn_cast<ObjCImplementationDecl>(OCD)) + ClassName = OID->getObjCRuntimeNameAsString(); + + if (ClassName.empty()) + return {}; + + auto Mangle = [&](ObjCKind Kind, StringRef ClassName) -> std::string { + SmallString<40> Mangled; + auto Prefix = getClassSymbolPrefix(Kind, OCD->getASTContext()); + llvm::Mangler::getNameWithPrefix(Mangled, Prefix + ClassName, DL); + return Mangled.str(); + }; + + return { + Mangle(ObjCClass, ClassName), + Mangle(ObjCMetaclass, ClassName), + }; + } + + std::vector<std::string> getAllManglings(const Decl *D) { + if (const auto *OCD = dyn_cast<ObjCContainerDecl>(D)) + return getAllManglings(OCD); + + if (!(isa<CXXRecordDecl>(D) || isa<CXXMethodDecl>(D))) + return {}; + + const NamedDecl *ND = cast<NamedDecl>(D); + + ASTContext &Ctx = ND->getASTContext(); + std::unique_ptr<MangleContext> M(Ctx.createMangleContext()); + + std::vector<std::string> Manglings; + + auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { + auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, + /*IsCSSMethod=*/true); + auto CC = MD->getType()->getAs<FunctionProtoType>()->getCallConv(); + return CC == DefaultCC; + }; + + if (const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(ND)) { + Manglings.emplace_back(getMangledStructor(CD, Ctor_Base)); + + if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) + if (!CD->getParent()->isAbstract()) + Manglings.emplace_back(getMangledStructor(CD, Ctor_Complete)); + + if (Ctx.getTargetInfo().getCXXABI().isMicrosoft()) + if (CD->hasAttr<DLLExportAttr>() && CD->isDefaultConstructor()) + if (!(hasDefaultCXXMethodCC(Ctx, CD) && CD->getNumParams() == 0)) + Manglings.emplace_back(getMangledStructor(CD, Ctor_DefaultClosure)); + } else if (const auto *DD = dyn_cast_or_null<CXXDestructorDecl>(ND)) { + Manglings.emplace_back(getMangledStructor(DD, Dtor_Base)); + if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) { + Manglings.emplace_back(getMangledStructor(DD, Dtor_Complete)); + if (DD->isVirtual()) + Manglings.emplace_back(getMangledStructor(DD, Dtor_Deleting)); + } + } else if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(ND)) { + Manglings.emplace_back(getName(ND)); + if (MD->isVirtual()) + if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) + for (const auto &T : *TIV) + Manglings.emplace_back(getMangledThunk(MD, T)); + } + + return Manglings; + } + +private: + bool writeFuncOrVarName(const NamedDecl *D, raw_ostream &OS) { + if (MC->shouldMangleDeclName(D)) { + if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(D)) + MC->mangleCXXCtor(CtorD, Ctor_Complete, OS); + else if (const auto *DtorD = dyn_cast<CXXDestructorDecl>(D)) + MC->mangleCXXDtor(DtorD, Dtor_Complete, OS); + else + MC->mangleName(D, OS); + return false; + } else { + IdentifierInfo *II = D->getIdentifier(); + if (!II) + return true; + OS << II->getName(); + return false; + } + } + + void writeObjCClassName(const ObjCInterfaceDecl *D, raw_ostream &OS) { + OS << getClassSymbolPrefix(ObjCClass, D->getASTContext()); + OS << D->getObjCRuntimeNameAsString(); + } + + static StringRef getClassSymbolPrefix(ObjCKind Kind, + const ASTContext &Context) { + if (Context.getLangOpts().ObjCRuntime.isGNUFamily()) + return Kind == ObjCMetaclass ? "_OBJC_METACLASS_" : "_OBJC_CLASS_"; + return Kind == ObjCMetaclass ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_"; + } + + std::string getMangledStructor(const NamedDecl *ND, unsigned StructorType) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + if (const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(ND)) + MC->mangleCXXCtor(CD, static_cast<CXXCtorType>(StructorType), FOS); + else if (const auto *DD = dyn_cast_or_null<CXXDestructorDecl>(ND)) + MC->mangleCXXDtor(DD, static_cast<CXXDtorType>(StructorType), FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FOS.str(), DL); + + return BOS.str(); + } + + std::string getMangledThunk(const CXXMethodDecl *MD, const ThunkInfo &T) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + MC->mangleThunk(MD, T, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FOS.str(), DL); + + return BOS.str(); + } +}; + CodegenNameGenerator::CodegenNameGenerator(ASTContext &Ctx) - : Impl(new ASTNameGenerator(Ctx)) { -} + : Impl(new Implementation(Ctx)) {} -CodegenNameGenerator::~CodegenNameGenerator() { -} +CodegenNameGenerator::~CodegenNameGenerator() { delete Impl; } bool CodegenNameGenerator::writeName(const Decl *D, raw_ostream &OS) { return Impl->writeName(D, OS); Index: clang/lib/Index/FileIndexRecord.h =================================================================== --- clang/lib/Index/FileIndexRecord.h +++ /dev/null @@ -1,57 +0,0 @@ -//===--- FileIndexRecord.h - Index data per file ----------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H -#define LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H - -#include "clang/Basic/SourceLocation.h" -#include "clang/Index/DeclOccurrence.h" -#include "clang/Index/IndexSymbol.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallVector.h" -#include <vector> - -namespace clang { -class IdentifierInfo; - -namespace index { - -/// Stores the declaration occurrences seen in a particular source or header -/// file of a translation unit -class FileIndexRecord { -private: - FileID FID; - bool IsSystem; - std::vector<DeclOccurrence> Decls; - -public: - FileIndexRecord(FileID FID, bool IsSystem) : FID(FID), IsSystem(IsSystem) {} - - ArrayRef<DeclOccurrence> getDeclOccurrencesSortedByOffset() const { - return Decls; - } - - FileID getFileID() const { return FID; } - bool isSystem() const { return IsSystem; } - - /// Adds an occurrence of the canonical declaration \c D at the supplied - /// \c Offset - /// - /// \param Roles the roles the occurrence fulfills in this position. - /// \param Offset the offset in the file of this occurrence. - /// \param D the canonical declaration this is an occurrence of. - /// \param Relations the set of symbols related to this occurrence. - void addDeclOccurence(SymbolRoleSet Roles, unsigned Offset, const Decl *D, - ArrayRef<SymbolRelation> Relations); - void print(llvm::raw_ostream &OS) const; -}; - -} // end namespace index -} // end namespace clang - -#endif // LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H Index: clang/lib/Index/FileIndexRecord.cpp =================================================================== --- clang/lib/Index/FileIndexRecord.cpp +++ /dev/null @@ -1,60 +0,0 @@ -//===--- FileIndexRecord.cpp - Index data per file --------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "FileIndexRecord.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/DeclTemplate.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/Path.h" - -using namespace clang; -using namespace clang::index; - -void FileIndexRecord::addDeclOccurence(SymbolRoleSet Roles, unsigned Offset, - const Decl *D, - ArrayRef<SymbolRelation> Relations) { - assert(D->isCanonicalDecl() && - "Occurrences should be associated with their canonical decl"); - - auto IsNextOccurence = [&]() -> bool { - if (Decls.empty()) - return true; - auto &Last = Decls.back(); - return Last.Offset < Offset; - }; - - if (IsNextOccurence()) { - Decls.emplace_back(Roles, Offset, D, Relations); - return; - } - - DeclOccurrence NewInfo(Roles, Offset, D, Relations); - // We keep Decls in order as we need to access them in this order in all cases. - auto It = llvm::upper_bound(Decls, NewInfo); - Decls.insert(It, std::move(NewInfo)); -} - -void FileIndexRecord::print(llvm::raw_ostream &OS) const { - OS << "DECLS BEGIN ---\n"; - for (auto &DclInfo : Decls) { - const Decl *D = DclInfo.Dcl; - SourceManager &SM = D->getASTContext().getSourceManager(); - SourceLocation Loc = SM.getFileLoc(D->getLocation()); - PresumedLoc PLoc = SM.getPresumedLoc(Loc); - OS << llvm::sys::path::filename(PLoc.getFilename()) << ':' << PLoc.getLine() - << ':' << PLoc.getColumn(); - - if (auto ND = dyn_cast<NamedDecl>(D)) { - OS << ' ' << ND->getNameAsString(); - } - - OS << '\n'; - } - OS << "DECLS END ---\n"; -} Index: clang/lib/Index/GenerateIndexAction.cpp =================================================================== --- /dev/null +++ clang/lib/Index/GenerateIndexAction.cpp @@ -0,0 +1,145 @@ +//===- GenerateIndexAction.cpp --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/GenerateIndexAction.h" + +#include "clang/Basic/Version.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Index/IndexPPCallbacks.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/Support/Errc.h" + +namespace clang { +namespace index { + +static bool topLevelDeclVisitor(void *context, const Decl *D) { + IndexingContext &IndexCtx = *static_cast<IndexingContext *>(context); + return IndexCtx.indexTopLevelDecl(D); +} + +static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IndexCtx) { + Unit.visitLocalTopLevelDecls(&IndexCtx, topLevelDeclVisitor); +} + +static void indexPreprocessorMacros(const Preprocessor &PP, + IndexDataConsumer &DataConsumer) { + for (const auto &M : PP.macros()) + if (MacroDirective *MD = M.second.getLatest()) + DataConsumer.handleMacroOccurence( + M.first, MD->getMacroInfo(), + static_cast<unsigned>(SymbolRole::Definition), MD->getLocation()); +} + +void indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, + const IndexingOptions &Opts) { + IndexingContext IndexCtx(Opts, DataConsumer); + IndexCtx.setASTContext(Unit.getASTContext()); + DataConsumer.initialize(Unit.getASTContext()); + DataConsumer.setPreprocessor(Unit.getPreprocessorPtr()); + + if (Opts.IndexMacrosInPreprocessor) + indexPreprocessorMacros(Unit.getPreprocessor(), DataConsumer); + indexTranslationUnit(Unit, IndexCtx); + DataConsumer.finish(); +} + +void indexTopLevelDecls(ASTContext &Ctx, Preprocessor &PP, + ArrayRef<const Decl *> Decls, + IndexDataConsumer &DataConsumer, + const IndexingOptions &Opts) { + IndexingContext IndexCtx(Opts, DataConsumer); + IndexCtx.setASTContext(Ctx); + + DataConsumer.initialize(Ctx); + + if (Opts.IndexMacrosInPreprocessor) + indexPreprocessorMacros(PP, DataConsumer); + + for (const Decl *D : Decls) + IndexCtx.indexTopLevelDecl(D); + DataConsumer.finish(); +} + +std::unique_ptr<PPCallbacks> indexMacrosCallback(IndexDataConsumer &Consumer, + const IndexingOptions &Opts) { + return llvm::make_unique<IndexPPCallbacks>( + std::make_shared<IndexingContext>(Opts, Consumer)); +} + +void indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, + IndexDataConsumer &DataConsumer, + const IndexingOptions &Opts) { + ASTContext &Ctx = Reader.getContext(); + IndexingContext IndexCtx(Opts, DataConsumer); + IndexCtx.setASTContext(Ctx); + DataConsumer.initialize(Ctx); + + if (Opts.IndexMacrosInPreprocessor) + indexPreprocessorMacros(Reader.getPreprocessor(), DataConsumer); + + for (const Decl *D : Reader.getModuleFileLevelDecls(Mod)) { + IndexCtx.indexTopLevelDecl(D); + } + DataConsumer.finish(); +} + +class IndexASTConsumer : public ASTConsumer { + std::shared_ptr<IndexingContext> Ctx; + +public: + IndexASTConsumer(std::shared_ptr<IndexingContext> Ctx, CompilerInstance &CI) + : Ctx(Ctx) { + CI.getPreprocessor().addPPCallbacks( + llvm::make_unique<IndexPPCallbacks>(Ctx)); + } + +protected: + void Initialize(ASTContext &Context) override { + Ctx->setASTContext(Context); + Ctx->getDataConsumer().initialize(Context); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return Ctx->indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + Ctx->indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override {} +}; + +GenerateIndexAction::GenerateIndexAction( + std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts) + : DataConsumer(DataConsumer), + IndexCtx(new IndexingContext(Opts, *DataConsumer)) {} + +std::unique_ptr<ASTConsumer> +GenerateIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + + IndexCtx->getDataConsumer().setPreprocessor(CI.getPreprocessorPtr()); + return llvm::make_unique<IndexASTConsumer>(IndexCtx, CI); +} + +void GenerateIndexAction::EndSourceFileAction() { + if (DataConsumer) + DataConsumer->finish(); +} + +} // namespace index +} // namespace clang \ No newline at end of file Index: clang/lib/Index/IndexAction.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexAction.cpp @@ -0,0 +1,45 @@ +//===- IndexAction.cpp - Interface for index actions ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexAction.h" + +#include "clang/Frontend/MultiplexConsumer.h" + +#include <vector> + +namespace clang { +namespace index { + +// Using distinct names for parameters in order to avoid confusion with name +// resolution in assert. +IndexActionWrapper::IndexActionWrapper( + std::unique_ptr<IndexAction> IndexingActionParam, + std::unique_ptr<FrontendAction> WrappedActionParam) + : WrapperFrontendAction(std::move(WrappedActionParam)), + IndexingAction(std::move(IndexingActionParam)) { + assert(IndexingAction); +} + +std::unique_ptr<ASTConsumer> +IndexActionWrapper::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (OtherConsumer) + Consumers.push_back(std::move(OtherConsumer)); + Consumers.push_back(IndexingAction->CreateASTConsumer(CI, InFile)); + + return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); +} + +void IndexActionWrapper::EndSourceFileAction() { + WrapperFrontendAction::EndSourceFileAction(); + IndexingAction->EndSourceFileAction(); +} + +} // namespace index +} // namespace clang Index: clang/lib/Index/IndexBody.cpp =================================================================== --- clang/lib/Index/IndexBody.cpp +++ clang/lib/Index/IndexBody.cpp @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" -#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/ASTLambda.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Index/IndexingContext.h" using namespace clang; using namespace clang::index; Index: clang/lib/Index/IndexDecl.cpp =================================================================== --- clang/lib/Index/IndexDecl.cpp +++ clang/lib/Index/IndexDecl.cpp @@ -6,9 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" -#include "clang/Index/IndexDataConsumer.h" #include "clang/AST/DeclVisitor.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexingContext.h" using namespace clang; using namespace index; Index: clang/lib/Index/IndexOptions.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexOptions.cpp @@ -0,0 +1,26 @@ +//===--- IndexOptions.cpp - Options for indexing ----------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexOptions.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "llvm/Support/Path.h" + +namespace clang { +namespace index { + +IndexingOptions parseIndexGenerationOptions(const FrontendOptions &FEOpts) { + IndexingOptions IndexOpts; + if (FEOpts.IndexIgnoreSystemSymbols) { + IndexOpts.SystemSymbolFilter = + index::IndexingOptions::SystemSymbolFilterKind::None; + } + return IndexOpts; +} + +} // namespace index +} // namespace clang Index: clang/lib/Index/IndexTypeSourceInfo.cpp =================================================================== --- clang/lib/Index/IndexTypeSourceInfo.cpp +++ clang/lib/Index/IndexTypeSourceInfo.cpp @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Index/IndexingContext.h" using namespace clang; using namespace index; Index: clang/lib/Index/IndexWhileBuilding/BitstreamUtil.h =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/BitstreamUtil.h @@ -0,0 +1,32 @@ +//===--- BitstreamUtil.h - Bitstream manipulation utils -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_BITSTREAMUTIL_H +#define LLVM_CLANG_LIB_INDEX_BITSTREAMUTIL_H + +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +class BitstreamWriter; +} + +namespace clang { +namespace index { +namespace store { + +void emitBlockID(unsigned ID, const char *Name, llvm::BitstreamWriter &Stream, + llvm::SmallVectorImpl<uint64_t> &Record); + +void emitRecordID(unsigned ID, const char *Name, llvm::BitstreamWriter &Stream, + llvm::SmallVectorImpl<uint64_t> &Record); + +} // end namespace store +} // end namespace index +} // end namespace clang + +#endif Index: clang/lib/Index/IndexWhileBuilding/BitstreamUtil.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/BitstreamUtil.cpp @@ -0,0 +1,47 @@ +//===--- BitstreamUtil.cpp - Bitstream manipulation utils -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "BitstreamUtil.h" + +#include "llvm/Bitstream/BitCodes.h" +#include "llvm/Bitstream/BitstreamWriter.h" + +using namespace clang; +using namespace llvm; + +namespace clang { +namespace index { +namespace store { + +void emitBlockID(unsigned ID, const char *Name, llvm::BitstreamWriter &Stream, + llvm::SmallVectorImpl<uint64_t> &Record) { + Record.clear(); + Record.push_back(ID); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, Record); + + // Emit the block name if present. + if (!Name || Name[0] == 0) + return; + Record.clear(); + while (*Name) + Record.push_back(*Name++); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +void emitRecordID(unsigned ID, const char *Name, llvm::BitstreamWriter &Stream, + llvm::SmallVectorImpl<uint64_t> &Record) { + Record.clear(); + Record.push_back(ID); + while (*Name) + Record.push_back(*Name++); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); +} + +} // end namespace store +} // end namespace index +} // end namespace clang Index: clang/lib/Index/IndexWhileBuilding/BitstreamVisitor.h =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/BitstreamVisitor.h @@ -0,0 +1,186 @@ +//===--- BitstreamVisitor.h - Helper for reading a bitstream --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H +#define LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H + +#include "clang/Basic/LLVM.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include <string> + +namespace clang { +namespace index { +namespace store { + +/// Helper class that saves the current stream position and +/// then restores it when destroyed. +struct SavedStreamPosition { + explicit SavedStreamPosition(llvm::BitstreamCursor &Cursor) + : Cursor(Cursor), Offset(Cursor.GetCurrentBitNo()) {} + + ~SavedStreamPosition() { + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + llvm::report_fatal_error("SavedStreamPosition failed jumping: " + + toString(std::move(Err))); + } + +private: + llvm::BitstreamCursor &Cursor; + uint64_t Offset; +}; + +enum class StreamVisit { Continue, Skip, Abort }; + +template <typename ImplClass> class BitstreamVisitor { + SmallVector<unsigned, 4> BlockStack; + +protected: + llvm::BitstreamCursor &Stream; + Optional<llvm::BitstreamBlockInfo> BlockInfo; + std::string *Error; + +public: + BitstreamVisitor(llvm::BitstreamCursor &Stream) : Stream(Stream) {} + + StreamVisit visitBlock(unsigned ID) { return StreamVisit::Continue; } + + bool visit(std::string &Error) { + this->Error = &Error; + + ASTReader::RecordData Record; + while (1) { + Expected<llvm::BitstreamEntry> MaybeEntry = + Stream.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + if (!MaybeEntry) { + Error = toString(MaybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry Entry = MaybeEntry.get(); + + switch (Entry.Kind) { + case llvm::BitstreamEntry::Error: + Error = "malformed serialization"; + return false; + + case llvm::BitstreamEntry::EndBlock: + if (BlockStack.empty()) + return true; + BlockStack.pop_back(); + if (Stream.ReadBlockEnd()) { + Error = "malformed serialization"; + return false; + } + if (Stream.AtEndOfStream()) + return true; + break; + + case llvm::BitstreamEntry::SubBlock: { + if (Entry.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) { + Expected<Optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo = + Stream.ReadBlockInfoBlock(); + if (!MaybeBlockInfo) { + Error = toString(MaybeBlockInfo.takeError()); + return false; + } + BlockInfo = MaybeBlockInfo.get(); + if (!BlockInfo) { + Error = "malformed BlockInfoBlock"; + return false; + } + Stream.setBlockInfo(&*BlockInfo); + break; + } + + StreamVisit Ret = static_cast<ImplClass *>(this)->visitBlock(Entry.ID); + switch (Ret) { + case StreamVisit::Continue: + if (Stream.EnterSubBlock(Entry.ID)) { + Error = "malformed block record"; + return false; + } + if (llvm::Error Err = readBlockAbbrevs(Stream)) { + Error = toString(std::move(Err)); + return false; + } + BlockStack.push_back(Entry.ID); + break; + + case StreamVisit::Skip: + if (Stream.SkipBlock()) { + Error = "malformed serialization"; + return false; + } + if (Stream.AtEndOfStream()) + return true; + break; + + case StreamVisit::Abort: + return false; + } + break; + } + + case llvm::BitstreamEntry::Record: { + Record.clear(); + StringRef Blob; + Expected<unsigned> MaybeRecID = + Stream.readRecord(Entry.ID, Record, &Blob); + if (!MaybeRecID) { + Error = toString(MaybeRecID.takeError()); + return false; + } + unsigned RecID = MaybeRecID.get(); + unsigned BlockID = BlockStack.empty() ? 0 : BlockStack.back(); + StreamVisit Ret = static_cast<ImplClass *>(this)->visitRecord( + BlockID, RecID, Record, Blob); + switch (Ret) { + case StreamVisit::Continue: + break; + + case StreamVisit::Skip: + if (Expected<unsigned> Skipped = Stream.skipRecord(Entry.ID)) { + Error = toString(Skipped.takeError()); + return false; + } + break; + + case StreamVisit::Abort: + return false; + } + break; + } + } + } + } + + static llvm::Error readBlockAbbrevs(llvm::BitstreamCursor &Cursor) { + while (true) { + uint64_t Offset = Cursor.GetCurrentBitNo(); + Expected<unsigned> MaybeCode = Cursor.ReadCode(); + if (!MaybeCode) + return MaybeCode.takeError(); + unsigned Code = MaybeCode.get(); + + // We expect all abbrevs to be at the start of the block. + if (Code != llvm::bitc::DEFINE_ABBREV) { + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + return Err; + return llvm::Error::success(); + } + if (llvm::Error Err = Cursor.ReadAbbrevRecord()) + return Err; + } + } +}; + +} // end namespace store +} // end namespace index +} // end namespace clang + +#endif Index: clang/lib/Index/IndexWhileBuilding/CMakeLists.txt =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/CMakeLists.txt @@ -0,0 +1,30 @@ +set(LLVM_LINK_COMPONENTS + BitReader + BitstreamReader + Core + Support + ) + +add_clang_library(clangIndexWhileBuilding + BitstreamUtil.cpp + DeclOccurrenceCollector.cpp + EmitIndexAction.cpp + EmitIndexOptions.cpp + IndexDataFormat.cpp + IndexRecordReader.cpp + IndexRecordWriter.cpp + IndexUnitReader.cpp + IndexUnitWriter.cpp + PathStorage.cpp + + LINK_LIBS + clangAST + clangBasic + clangFormat + clangFrontend + clangIndex + clangLex + clangRewrite + clangSerialization + clangToolingCore + ) Index: clang/lib/Index/IndexWhileBuilding/DeclOccurrenceCollector.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/DeclOccurrenceCollector.cpp @@ -0,0 +1,40 @@ +//===- DeclOccurrenceCollector.cpp - Collecting data for index records ----===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/DeclOccurrenceCollector.h" + +namespace clang { +namespace index { + +bool DeclOccurrenceCollector::handleDeclOccurence( + const Decl *D, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) { + Loc = SM.getFileLoc(Loc); + if (Loc.isInvalid()) + return true; + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + + if (FID.isInvalid()) + return true; + + const FileEntry *FE = SM.getFileEntryForID(FID); + if (!FE) // Ignore predefined buffers. + return true; + + IndexRecordWriter &Rec = + RecordByFile.insert({FID, IndexRecordWriter(IndexCtx->isSystemFile(FID))}) + .first->second; + Rec.addDeclOccurence(Roles, FID, Offset, D, Relations); + return true; +} + +} // namespace index +} // namespace clang \ No newline at end of file Index: clang/lib/Index/IndexWhileBuilding/EmitIndexAction.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/EmitIndexAction.cpp @@ -0,0 +1,502 @@ +//===- EmitIndexAction.cpp - Index emitting action +//-------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/EmitIndexAction.h" + +#include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Index/IndexPPCallbacks.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/Support/Errc.h" + +namespace clang { +namespace index { + +using namespace llvm; + +static Module *getModBeingCompiled(const CompilerInstance &CI) { + return CI.getLangOpts().isCompilingModule() + ? CI.getPreprocessor().getHeaderSearchInfo().lookupModule( + CI.getLangOpts().CurrentModule, /*AllowSearch=*/false) + : nullptr; +} + +static std::string getOutputFile(const CompilerInstance &CI) { + std::string Result = CI.getFrontendOpts().OutputFile; + return !Result.empty() + ? Result + : CI.getFrontendOpts().Inputs[0].getFile().str() + ".o"; +} + +static const FileEntry *getMainFile(const CompilerInstance &CI) { + if (CI.getLangOpts().isCompilingModule() || + CI.getFrontendOpts().ProgramAction == frontend::GeneratePCH) + return nullptr; + + SourceManager &SM = CI.getSourceManager(); + return SM.getFileEntryForID(SM.getMainFileID()); +} + +class IncludePPCallbacks : public PPCallbacks { + IndexingContext &IndexCtx; + EmitIndexOptions RecordOpts; + IndexUnitWriter &UnitWriter; + SourceManager &SourceMgr; + +public: + IncludePPCallbacks(IndexingContext &indexCtx, EmitIndexOptions recordOpts, + IndexUnitWriter &UnitWriter, SourceManager &SourceMgr) + : IndexCtx(indexCtx), RecordOpts(recordOpts), UnitWriter(UnitWriter), + SourceMgr(SourceMgr) {} + +private: + virtual void + InclusionDirective(SourceLocation InclusionDirectiveLoc, const Token &, + StringRef, bool, CharSourceRange, + const FileEntry *IncludedFile, StringRef, StringRef, + const Module *ImportedModule, + SrcMgr::CharacteristicKind FileType) override { + // We don't want to treat imports as includes. + if (ImportedModule) + return; + + if (RecordOpts.RecordIncludes == + EmitIndexOptions::IncludesRecordingKind::None) + return; + + if (!InclusionDirectiveLoc.isFileID() || !IncludedFile || + !IncludedFile->isValid()) + return; + + std::pair<FileID, unsigned> InclusionDirectiveDecLoc = + SourceMgr.getDecomposedExpansionLoc(InclusionDirectiveLoc); + + if (RecordOpts.RecordIncludes == + EmitIndexOptions::IncludesRecordingKind::UserOnly && + IndexCtx.isSystemFile(InclusionDirectiveDecLoc.first)) + return; // Ignore includes of system headers. + + auto *FE = SourceMgr.getFileEntryForID(InclusionDirectiveDecLoc.first); + if (!FE) + return; + + UnitWriter.addIncludedFile( + FE, + SourceMgr.getLineNumber(InclusionDirectiveDecLoc.first, + InclusionDirectiveDecLoc.second), + IncludedFile); + } +}; + +SourceFilesDependencyCollector::SourceFilesDependencyCollector( + const CompilerInstance &CI, IndexingContext &indexCtx, + EmitIndexOptions recordOpts) + : CI(CI), IndexCtx(indexCtx), RecordOpts(recordOpts), + UnitWriter(makeIndexUnitWriter(CI, RecordOpts, getOutputFile(CI), + getMainFile(CI), + getModBeingCompiled(CI))) {} + +void SourceFilesDependencyCollector::attachToPreprocessor(Preprocessor &PP) { + DependencyCollector::attachToPreprocessor(PP); + PP.addPPCallbacks(llvm::make_unique<IncludePPCallbacks>( + IndexCtx, RecordOpts, UnitWriter, PP.getSourceManager())); +} + +/// \returns either a Module that is both a submodule of UnitModule and owns the +/// header \p FE. +static Module *findModuleForHeader(const CompilerInstance &CI, + Module &UnitModule, const FileEntry &FE) { + if (auto Mod = CI.getPreprocessor() + .getHeaderSearchInfo() + .findModuleForHeader(&FE) + .getModule()) + if (Mod->isSubModuleOf(&UnitModule)) + return Mod; + return nullptr; +} + +bool SourceFilesDependencyCollector::sawDependency(StringRef Filename, + bool FromModule, + bool IsSystem, + bool IsModuleFile, + bool IsMissing) { + bool sawIt = DependencyCollector::sawDependency( + Filename, FromModule, IsSystem, IsModuleFile, IsMissing); + if (sawIt) { + if (auto *FE = CI.getSourceManager().getFileManager().getFile(Filename)) { + assert(FE); + + Module *ModForHead = nullptr; + { + Module *Mod = getModBeingCompiled(CI); + if (Mod) { + ModForHead = findModuleForHeader(CI, *Mod, *FE); + UnitWriter.addModule(ModForHead); + } + } + + UnitWriter.addSourceFile(FE, IsSystem || isInSysroot(Filename), + ModForHead); + } + } + return sawIt; +} + +bool SourceFilesDependencyCollector::isInSysroot(StringRef Filename) { + const auto &Sysroot = CI.getHeaderSearchOpts().Sysroot; + // FIXME: Make this portable. + return !Sysroot.empty() && (Sysroot != "/") && Filename.startswith(Sysroot); +} + +ActionContext::ActionContext(const IndexingOptions &IndexOpts, + const CompilerInstance &CI, + const EmitIndexOptions &RecordOpts) + : CI(CI), DeclsDataCollector(CI.getSourceManager()), + Ctx(IndexOpts, DeclsDataCollector), DepCollector(CI, Ctx, RecordOpts), + RecordOpts(RecordOpts), IndexOpts(IndexOpts) { + DepCollector.attachToPreprocessor(CI.getPreprocessor()); + DeclsDataCollector.setIndexingContext(Ctx); +} + +// FIXME: Some better implementation. +template <unsigned N> +static void reportError(const CompilerInstance &CI, const char (&Format)[N], + const std::string &Param1 = "", + const std::string &Param2 = "") { + DiagnosticsEngine &Diag = CI.getDiagnostics(); + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, Format); + if (!Param2.empty()) + Diag.Report(DiagID) << Param1 << Param2; + else + Diag.Report(DiagID) << Param1; +}; + +/// Writes records in \p RecordByFile to disk and adds them as dependencies to +/// \p UnitModule. +void writeIndexRecordsToDisk( + const llvm::DenseMap<FileID, IndexRecordWriter> &RecordByFile, + IndexUnitWriter &UnitWriter, const CompilerInstance &CI, + const EmitIndexOptions &RecordOpts, const Module *UnitModule) { + auto &ASTCtx = CI.getASTContext(); + PrintingPolicy Policy(ASTCtx.getLangOpts()); + Policy.SuppressTemplateArgsInCXXConstructors = true; + + std::shared_ptr<CodegenNameGenerator> CGNameGen; + if (RecordOpts.RecordSymbolCodeGenName) + CGNameGen = std::make_shared<CodegenNameGenerator>(ASTCtx); + + for (const auto &It : RecordByFile) { + const FileID FID = It.first; + const IndexRecordWriter &Rec = It.second; + const FileEntry *FE = CI.getSourceManager().getFileEntryForID(FID); + assert(FE); + + if (Rec.isRecordEmpty()) + continue; + + llvm::Optional<std::string> RecordFilename = Rec.writeToDisk( + FE->getName(), RecordOpts, Policy, CGNameGen, CI.getDiagnostics()); + if (!RecordFilename) { + reportError(CI, + "failed to write record file for source file '%0' to disk", + FE->getName(), ""); + return; + } + + Module *Mod = nullptr; + if (UnitModule) { + if (auto ModCandidate = CI.getPreprocessor() + .getHeaderSearchInfo() + .findModuleForHeader(FE) + .getModule()) { + if (ModCandidate->isSubModuleOf(UnitModule)) { + Mod = ModCandidate; + UnitWriter.addModule(Mod); + } + } + } + + UnitWriter.addIndexRecord(RecordFilename.getValue(), FE, Rec.isSystem(), + Mod); + } +} + +void ActionContext::emitCollectedData() { + // FIXME: FrontendAction::EndSourceFile() should probably not call + // CI.getDiagnosticClient().EndSourceFile()' until after it has called + // 'EndSourceFileAction()', so that code executing during + // EndSourceFileAction() can emit diagnostics. If this is fixed, + // DiagClientBeginEndRAII can go away. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts()); + auto DiagnosticClientEndSourceFile = llvm::make_scope_exit( + [this]() { CI.getDiagnosticClient().EndSourceFile(); }); + + std::string Error; + if (initIndexDirectory(RecordOpts, Error)) { + reportError(CI, "failed creating index directory %0", Error); + return; + } + + IndexUnitWriter &UnitWriter = DepCollector.UnitWriter; + + const Module *UnitModule = getModBeingCompiled(CI); + std::string ModuleName = + UnitModule ? UnitModule->getFullModuleName() : std::string(); + + if (auto Reader = CI.getModuleManager()) { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + Reader->getModuleManager().visit( + [&](serialization::ModuleFile &Mod) -> bool { + Module *UnitMod = + HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false); + const bool isSystemMod = + Mod.isModule() && UnitMod && UnitMod->IsSystem; + + if (!isSystemMod || RecordOpts.RecordSystemDependencies) { + SmallString<256> AbsPath(Mod.File->getName()); + CI.getFileManager().makeAbsolutePath(AbsPath); + UnitWriter.addIndexUnit(Mod.File, AbsPath, isSystemMod, UnitMod); + if (Mod.isModule()) { + llvm::Optional<bool> IndexUnitExist = doesIndexUnitForModuleExist( + Mod.FileName, CI.getFileManager(), RecordOpts); + + if (!IndexUnitExist.hasValue()) + reportError(CI, "failed to stat index unit for module %0", + AbsPath.str()); + + if (!IndexUnitExist.getValue()) + emitIndexDataForModuleFile(Mod, CI, IndexOpts, RecordOpts); + } + } + return true; // skip module dependencies. + }); + } + + if (std::error_code EC = sys::fs::create_directory(RecordOpts.RecordDir)) { + reportError(CI, "failed to create directory '%0': %1", RecordOpts.RecordDir, + EC.message()); + return; + } + writeIndexRecordsToDisk(DeclsDataCollector.getRecordByFile(), UnitWriter, CI, + RecordOpts, UnitModule); + + { + std::string Error; + if (UnitWriter.writeToDisk(Error)) { + reportError(CI, "failed writing unit data: %0", Error); + return; + } + } +} + +class IndexRecordingASTConsumer : public ASTConsumer { + std::shared_ptr<ActionContext> Context; + +public: + IndexRecordingASTConsumer(std::shared_ptr<ActionContext> Context) + : Context(Context) {} + +protected: + void Initialize(ASTContext &ASTContextInst) override { + Context->Ctx.setASTContext(ASTContextInst); + Context->DeclsDataCollector.initialize(ASTContextInst); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return Context->Ctx.indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + Context->Ctx.indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override {} +}; + +EmitIndexAction::EmitIndexAction(const IndexingOptions &IndexOpts, + const EmitIndexOptions &RecordOpts) + : RecordOpts(RecordOpts), IndexOpts(IndexOpts) {} + +std::unique_ptr<ASTConsumer> +EmitIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef) { + Context = std::make_shared<ActionContext>(IndexOpts, CI, RecordOpts); + return llvm::make_unique<IndexRecordingASTConsumer>(Context); +} + +void EmitIndexAction::EndSourceFileAction() { + if (Context) + Context->emitCollectedData(); +} + +void emitIndexDataForModuleFile(serialization::ModuleFile &Mod, + const CompilerInstance &CI, + const IndexingOptions &IndexOpts, + const EmitIndexOptions &RecordOpts) { + // FIXME: FrontendAction::EndSourceFile() should probably not call + // CI.getDiagnosticClient().EndSourceFile()' until after it has called + // 'EndSourceFileAction()', so that code executing during + // EndSourceFileAction() can emit diagnostics. If this is fixed, + // DiagClientBeginEndRAII can go away. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts()); + auto DiagnosticClientEndSourceFile = llvm::make_scope_exit( + [&CI]() { CI.getDiagnosticClient().EndSourceFile(); }); + + DiagnosticsEngine &Diag = CI.getDiagnostics(); + Diag.Report(Mod.ImportLoc, diag::remark_index_producing_module_file_data) + << Mod.FileName; + + Module *UnitModule = CI.getPreprocessor().getHeaderSearchInfo().lookupModule( + Mod.ModuleName, /*AllowSearch=*/false); + + DeclOccurrenceCollector DeclsDataCollector(CI.getSourceManager()); + { + IndexingContext IndexCtx(IndexOpts, DeclsDataCollector); + DeclsDataCollector.setIndexingContext(IndexCtx); + + IndexCtx.setASTContext(CI.getASTContext()); + + for (const Decl *D : CI.getModuleManager()->getModuleFileLevelDecls(Mod)) { + IndexCtx.indexTopLevelDecl(D); + } + DeclsDataCollector.finish(); + } + + { + std::string Error; + if (initIndexDirectory(RecordOpts, Error)) { + reportError(CI, "failed creating index directory %0", Error); + return; + } + } + + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + + SmallString<128> WorkDir; + if (!CI.getFileManager().getFileSystemOpts().WorkingDir.empty()) { + WorkDir = CI.getFileManager().getFileSystemOpts().WorkingDir; + if (!llvm::sys::path::is_absolute(WorkDir)) { + llvm::sys::fs::make_absolute(WorkDir); + } + } else { + std::error_code EC = llvm::sys::fs::current_path(WorkDir); + if (EC) { + reportError(CI, "failed to determine current working directory"); + } + } + + IndexUnitWriter UnitWriter = + makeIndexUnitWriter(CI, RecordOpts, Mod.FileName, nullptr, UnitModule); + + if (UnitModule) { + serialization::ModuleFile *ModFile = + CI.getModuleManager()->getModuleManager().lookup( + UnitModule->getASTFile()); + assert(ModFile && "no module file loaded for module ?"); + + for (auto *Mod : ModFile->Imports) { + auto *UnitMod = HS.lookupModule(Mod->ModuleName, /*AllowSearch=*/false); + if (!UnitMod->IsSystem || RecordOpts.RecordSystemDependencies) { + SmallString<256> AbsPath(Mod->File->getName()); + CI.getFileManager().makeAbsolutePath(AbsPath); + UnitWriter.addIndexUnit(Mod->File, AbsPath, UnitMod->IsSystem, UnitMod); + } + } + + CI.getModuleManager()->visitInputFiles( + *ModFile, RecordOpts.RecordSystemDependencies, + /*Complain=*/false, + [&](const serialization::InputFile &IF, bool isSystemFile) { + auto *FE = IF.getFile(); + if (!FE) + return; + // Ignore module map files, they are not as important to track as + // source files and they may be auto-generated which would create an + // undesirable dependency on an intermediate build byproduct. + if (FE->getName().endswith("module.modulemap")) + return; + + // Try to find a Module that is both a submodule of UnitModule and + // owns the header. + Module *ModForHead = nullptr; + if (auto Mod = CI.getPreprocessor() + .getHeaderSearchInfo() + .findModuleForHeader(FE) + .getModule()) + if (Mod->isSubModuleOf(UnitModule)) + ModForHead = Mod; + + UnitWriter.addModule(ModForHead); + + UnitWriter.addSourceFile(FE, isSystemFile, ModForHead); + }); + } + + if (std::error_code EC = sys::fs::create_directory(RecordOpts.RecordDir)) { + reportError(CI, "failed to create directory '%0': %1", RecordOpts.RecordDir, + EC.message()); + return; + } + writeIndexRecordsToDisk(DeclsDataCollector.getRecordByFile(), UnitWriter, CI, + RecordOpts, UnitModule); + + std::string Error; + if (UnitWriter.writeToDisk(Error)) { + reportError(CI, "failed writing unit data: %0", Error); + return; + } + + if (UnitModule) { + serialization::ModuleFile *ModFile = + CI.getModuleManager()->getModuleManager().lookup( + UnitModule->getASTFile()); + for (auto *Mod : ModFile->Imports) { + auto *UnitMod = HS.lookupModule(Mod->ModuleName, /*AllowSearch=*/false); + + if (!UnitMod->IsSystem || RecordOpts.RecordSystemDependencies) { + if (Mod->isModule()) { + llvm::Optional<bool> IndexUnitExist = doesIndexUnitForModuleExist( + Mod->FileName, CI.getFileManager(), RecordOpts); + if (IndexUnitExist.hasValue() && !IndexUnitExist.getValue()) + emitIndexDataForModuleFile(*Mod, CI, IndexOpts, RecordOpts); + } + } + } + } +} + +bool emitIndexDataForModule(const Module *Mod, const CompilerInstance &CI) { + assert(Mod); + auto astReader = CI.getModuleManager(); + serialization::ModuleFile *ModFile = + astReader->getModuleManager().lookup(Mod->getASTFile()); + assert(ModFile && "no module file loaded for module ?"); + + const EmitIndexOptions RecOpts = parseEmitIndexOptions(CI.getFrontendOpts()); + + llvm::Optional<bool> IndexUnitExist = doesIndexUnitForModuleExist( + ModFile->FileName, CI.getFileManager(), RecOpts); + if (IndexUnitExist.hasValue() && IndexUnitExist.getValue()) + return false; + + emitIndexDataForModuleFile( + *ModFile, CI, parseIndexGenerationOptions(CI.getFrontendOpts()), RecOpts); + return true; +} + +} // namespace index +} // namespace clang Index: clang/lib/Index/IndexWhileBuilding/EmitIndexOptions.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/EmitIndexOptions.cpp @@ -0,0 +1,41 @@ +//===--- IndexOptions.cpp - Options for indexing ----------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/EmitIndexOptions.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "llvm/Support/Path.h" + +namespace clang { +namespace index { + +EmitIndexOptions::EmitIndexOptions(const StringRef RootDir, + const bool RecordSymbolCodeGenName, + const bool RecordSystemDependencies, + const IncludesRecordingKind RecordIncludes) + : RootDir(RootDir), IndexDir(llvm::Twine(RootDir) + .concat(llvm::sys::path::get_separator()) + .concat(store::getIndexDir()) + .str()), + UnitDir(llvm::Twine(IndexDir) + .concat(llvm::sys::path::get_separator()) + .concat(store::getIndexUnitSubdir()) + .str()), + RecordDir(llvm::Twine(IndexDir) + .concat(llvm::sys::path::get_separator()) + .concat(store::getIndexRecordSubdir()) + .str()), + RecordSymbolCodeGenName(RecordSymbolCodeGenName), + RecordSystemDependencies(RecordSystemDependencies), + RecordIncludes(RecordIncludes) {} + +EmitIndexOptions parseEmitIndexOptions(const FrontendOptions &FEOpts) { + return EmitIndexOptions(FEOpts.IndexStorePath, FEOpts.IndexRecordCodegenName); +} + +} // namespace index +} // namespace clang Index: clang/lib/Index/IndexWhileBuilding/FSUtil.h =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/FSUtil.h @@ -0,0 +1,66 @@ +//===--- FSUtil.h - File system utilities ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_FSUTIL_H +#define LLVM_CLANG_INDEX_FSUTIL_H + +#include "clang/Index/IndexWhileBuilding/IndexRecordWriter.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace index { + +using namespace llvm; + +/// Creates a unique file with name according to the given \p TempPathModel, +/// writes content of \p Buffer to the file and renames it to \p FinalPath. +/// \returns false when the file was successfully created, true otherwise. +inline bool atomicFileWrite(StringRef TempPathModel, StringRef FinalPath, + const SmallString<512> &Buffer, + std::string &Error) { + SmallString<128> GeneratedUniqPath; + int TempFD; + if (sys::fs::createUniqueFile(TempPathModel.str(), TempFD, + GeneratedUniqPath)) { + raw_string_ostream Err(Error); + Err << "failed to create temporary file: " << TempPathModel; + return true; + } + + raw_fd_ostream OS(TempFD, /*shouldClose=*/true); + OS.write(Buffer.data(), Buffer.size()); + OS.close(); + TempFD = -1; + + if (OS.has_error()) { + raw_string_ostream Err(Error); + Err << "failed to write '" << GeneratedUniqPath + << "': " << OS.error().message(); + OS.clear_error(); + return true; + } + + std::error_code EC = sys::fs::rename(/*from=*/GeneratedUniqPath.c_str(), + /*to=*/FinalPath.str().c_str()); + if (EC) { + raw_string_ostream Err(Error); + Err << "failed to rename '" << GeneratedUniqPath << "' to '" << FinalPath + << "': " << EC.message(); + return true; + } + + return false; +} + +} // end namespace index +} // end namespace clang + +#endif \ No newline at end of file Index: clang/lib/Index/IndexWhileBuilding/IndexDataFormat.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/IndexDataFormat.cpp @@ -0,0 +1,99 @@ +//===--- IndexDataFormat.cpp - Layout of index on a filesystem ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +#define INDEX_FORMAT_VERSION_VALUE 5 + +static const unsigned indexFormatVersion = INDEX_FORMAT_VERSION_VALUE; + +const char *const indexSubdir = "v" STR(INDEX_FORMAT_VERSION_VALUE); + +#undef INDEX_FORMAT_VERSION_VALUE +#undef STR +#undef STR_HELPER + +const char *const indexUnitSubdir = "units"; + +const char *const indexRecordSubdir = "records"; + +} // namespace + +namespace clang { +namespace index { +namespace store { + +unsigned getIndexFormatVersion() { return indexFormatVersion; } + +const char *getIndexDir() { return indexSubdir; } +const char *getIndexUnitSubdir() { return indexUnitSubdir; } +const char *getIndexRecordSubdir() { return indexRecordSubdir; } + +static void appendIndexSubDir(StringRef subdir, SmallVectorImpl<char> &Buffer) { + sys::path::append(Buffer, getIndexDir()); + sys::path::append(Buffer, subdir); +} + +void appendUnitBucketDir(StringRef UnitName, SmallVectorImpl<char> &Buffer) { + sys::path::append(Buffer, UnitName); +} + +void appendUnitSubDir(SmallVectorImpl<char> &Buffer) { + return appendIndexSubDir(getIndexUnitSubdir(), Buffer); +} + +void appendRecordSubDir(SmallVectorImpl<char> &Buffer) { + return appendIndexSubDir(getIndexRecordSubdir(), Buffer); +} + +void appendRecordBucketDir(StringRef RecordName, + SmallVectorImpl<char> &Buffer) { + // To avoid putting a huge number of files into the records directory, create + // subdirectories based on the last 2 characters from the hash. + sys::path::append(Buffer, RecordName.substr(RecordName.size() - 2)); +} + +void appendRecordFilename(StringRef RecordName, SmallVectorImpl<char> &Buffer) { + sys::path::append(Buffer, RecordName); +} + +void getUnitNameForOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str) { + StringRef Fname = sys::path::filename(FilePath); + Str.append(Fname.begin(), Fname.end()); + Str.push_back('-'); + llvm::hash_code PathHashVal = llvm::hash_value(FilePath); + llvm::APInt(64, PathHashVal).toString(Str, 36, /*Signed=*/false); +} + +std::string generateFilenameForIndexRecord( + StringRef SourceFilename, const llvm::SmallString<512> &IndexRecordBuffer) { + const llvm::hash_code RecordHash = llvm::hash_combine_range( + IndexRecordBuffer.begin(), IndexRecordBuffer.end()); + + std::string RecordFilename; + llvm::raw_string_ostream RN(RecordFilename); + RN << llvm::sys::path::filename(SourceFilename); + RN << "-" << llvm::APInt(64, RecordHash).toString(36, /*Signed=*/false); + return RecordFilename; +} + +} // end namespace store +} // end namespace index +} // end namespace clang \ No newline at end of file Index: clang/lib/Index/IndexWhileBuilding/IndexRecordReader.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/IndexRecordReader.cpp @@ -0,0 +1,388 @@ +//===--- IndexRecordReader.cpp - Index record deserialization -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/IndexRecordReader.h" +#include "BitstreamVisitor.h" +#include "indexstore/Conversions.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace index { + +using namespace clang::index::store; +using namespace llvm; + +class IndexBitstreamVisitor : public BitstreamVisitor<IndexBitstreamVisitor> { + IndexRecordReader &Reader; + +public: + IndexBitstreamVisitor(llvm::BitstreamCursor &Stream, + IndexRecordReader &Reader) + : BitstreamVisitor(Stream), Reader(Reader) {} + + StreamVisit visitBlock(unsigned ID) { + switch ((RecordBitBlock)ID) { + case REC_VERSION_BLOCK_ID: + case REC_DECLOFFSETS_BLOCK_ID: + return StreamVisit::Continue; + + case REC_DECLS_BLOCK_ID: + Reader.DeclCursor = Stream; + if (Reader.DeclCursor.EnterSubBlock(ID)) { + *Error = "malformed block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.DeclCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + + case REC_DECLOCCURRENCES_BLOCK_ID: + Reader.OccurCursor = Stream; + if (Reader.OccurCursor.EnterSubBlock(ID)) { + *Error = "malformed block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.OccurCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + } + + // Some newly introduced block in a minor version update that we cannot + // handle. + return StreamVisit::Skip; + } + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + SmallVectorImpl<uint64_t> &Record, StringRef Blob) { + switch (BlockID) { + case REC_VERSION_BLOCK_ID: { + unsigned StoreFormatVersion = Record[0]; + if (StoreFormatVersion != getIndexFormatVersion()) { + llvm::raw_string_ostream OS(*Error); + OS << "Store format version mismatch: " << StoreFormatVersion; + OS << " , expected: " << getIndexFormatVersion(); + return StreamVisit::Abort; + } + break; + } + case REC_DECLOFFSETS_BLOCK_ID: + assert(RecID == REC_DECLOFFSETS); + Reader.setDeclOffsets( + makeArrayRef((const uint32_t *)Blob.data(), Record[0])); + break; + + case REC_DECLS_BLOCK_ID: + case REC_DECLOCCURRENCES_BLOCK_ID: + llvm_unreachable("shouldn't visit this block'"); + } + return StreamVisit::Continue; + } +}; + +std::unique_ptr<IndexRecordReader> IndexRecordReader::createWithRecordFilename( + StringRef RecordFilename, StringRef StorePath, std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendRecordSubDir(PathBuf); + appendRecordBucketDir(RecordFilename, PathBuf); + appendRecordFilename(RecordFilename, PathBuf); + return createWithFilePath(PathBuf.str(), Error); +} + +std::unique_ptr<IndexRecordReader> +IndexRecordReader::createWithFilePath(StringRef FilePath, std::string &Error) { + auto ErrOrBuf = MemoryBuffer::getFile(FilePath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrOrBuf) { + raw_string_ostream(Error) << "failed opening index record '" << FilePath + << "': " << ErrOrBuf.getError().message(); + return nullptr; + } + return createWithBuffer(std::move(*ErrOrBuf), Error); +} + +std::unique_ptr<IndexRecordReader> +IndexRecordReader::createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer, + std::string &Error) { + llvm::BitstreamCursor Stream(*Buffer); + + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return nullptr; + } + + // Sniff for the signature. + for (unsigned char C : {'I', 'D', 'X', 'R'}) { + if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) { + if (Res.get() == C) + continue; + } else { + Error = toString(Res.takeError()); + return nullptr; + } + Error = "not a serialized index record file"; + return nullptr; + } + + std::unique_ptr<IndexRecordReader> Reader = + llvm::make_unique<IndexRecordReader>(); + Reader->Buffer = std::move(Buffer); + + IndexBitstreamVisitor BitVisitor(Stream, *Reader); + if (!BitVisitor.visit(Error)) + return nullptr; + + return Reader; +} + +bool IndexRecordReader::searchDecls( + llvm::function_ref<DeclSearchCheck> Checker, + llvm::function_ref<void(const IndexRecordDecl *)> Receiver) { + for (unsigned I = 0, E = getNumDecls(); I != E; ++I) { + if (const IndexRecordDecl *D = Decls[I]) { + DeclSearchReturn Ret = Checker(*D); + if (Ret.AcceptDecl) + Receiver(D); + if (!Ret.ContinueSearch) + return false; + continue; + } + + IndexRecordDecl LocalD; + readDecl(I, LocalD); + DeclSearchReturn Ret = Checker(LocalD); + if (Ret.AcceptDecl) { + IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>(); + *D = LocalD; + Decls[I] = D; + Receiver(D); + } + if (!Ret.ContinueSearch) + return false; + } + return true; +} + +bool IndexRecordReader::foreachDecl( + bool NoCache, function_ref<bool(const IndexRecordDecl *)> Receiver) { + for (unsigned I = 0, E = getNumDecls(); I != E; ++I) { + if (const IndexRecordDecl *D = Decls[I]) { + if (!Receiver(D)) + return false; + continue; + } + + if (NoCache) { + IndexRecordDecl LocalD; + readDecl(I, LocalD); + if (!Receiver(&LocalD)) + return false; + } else { + if (!Receiver(getDecl(I))) + return false; + } + } + return true; +} + +bool IndexRecordReader::foreachOccurrence( + function_ref<bool(const IndexRecordOccurrence &)> Receiver, + ArrayRef<const IndexRecordDecl *> WhitelistDecls, + ArrayRef<const IndexRecordDecl *> WhitelistRelated) { + class OccurBitVisitor : public BitstreamVisitor<OccurBitVisitor> { + IndexRecordReader &Reader; + ArrayRef<const IndexRecordDecl *> WhitelistDecls; + ArrayRef<const IndexRecordDecl *> WhitelistRelated; + function_ref<bool(const IndexRecordOccurrence &)> Receiver; + + public: + OccurBitVisitor(llvm::BitstreamCursor &Stream, IndexRecordReader &Reader, + ArrayRef<const IndexRecordDecl *> WhitelistDecls, + ArrayRef<const IndexRecordDecl *> WhitelistRelated, + function_ref<bool(const IndexRecordOccurrence &)> Receiver) + : BitstreamVisitor(Stream), Reader(Reader), + WhitelistDecls(WhitelistDecls), WhitelistRelated(WhitelistRelated), + Receiver(std::move(Receiver)) {} + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + SmallVectorImpl<uint64_t> &Record, StringRef Blob) { + assert(RecID == REC_DECLOCCURRENCE); + IndexRecordOccurrence RecOccur; + if (Reader.readOccurrence(Record, Blob, WhitelistDecls, WhitelistRelated, + RecOccur)) + if (!Receiver(RecOccur)) + return StreamVisit::Abort; + return StreamVisit::Continue; + } + }; + + SavedStreamPosition SavedPosition(OccurCursor); + OccurBitVisitor Visitor(OccurCursor, *this, WhitelistDecls, WhitelistRelated, + Receiver); + std::string Error; + return Visitor.visit(Error); +} + +bool IndexRecordReader::foreachOccurrenceInLineRange( + unsigned lineStart, unsigned lineCount, + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver) { + // FIXME: Use binary search and make this more efficient. + unsigned lineEnd = lineStart + lineCount; + return foreachOccurrence( + [&](const IndexRecordOccurrence &occur) -> bool { + if (occur.Line > lineEnd) + return false; // we're done. + if (occur.Line >= lineStart) { + if (!Receiver(occur)) + return false; + } + return true; + }, + None, None); +} + +void IndexRecordReader::setDeclOffsets(ArrayRef<uint32_t> Offs) { + DeclOffsets = Offs; + Decls = Allocator.Allocate<const IndexRecordDecl *>(Offs.size()); + memset(Decls, 0, sizeof(IndexRecordDecl *) * Offs.size()); +} + +unsigned IndexRecordReader::getNumDecls() const { return DeclOffsets.size(); } + +const IndexRecordDecl *IndexRecordReader::getDeclByID(unsigned DeclID) { + if (DeclID == 0) + return nullptr; + return getDecl(DeclID - 1); +} + +const IndexRecordDecl *IndexRecordReader::getDecl(unsigned Index) { + assert(Index < getNumDecls()); + if (const IndexRecordDecl *D = Decls[Index]) + return D; + + IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>(); + readDecl(Index, *D); + Decls[Index] = D; + return D; +} + +/// Reads occurrence data. +/// \param WhitelistDecls if non-empty indicates the list of decls that we want +/// to get occurrences for. If empty then indicates that we want occurrences +/// for all decls. +/// \param WhitelistRelated Same as \c WhitelistDecls but for related decls. +/// \returns true if the occurrence info was filled out, false if occurrence +/// was ignored. +bool IndexRecordReader::readOccurrence( + SmallVectorImpl<uint64_t> &Record, StringRef Blob, + ArrayRef<const IndexRecordDecl *> WhitelistDecls, + ArrayRef<const IndexRecordDecl *> WhitelistRelated, + IndexRecordOccurrence &RecOccur) { + auto isDeclIDContained = [](unsigned DeclID, + ArrayRef<const IndexRecordDecl *> Ds) -> bool { + if (Ds.empty()) + return true; // empty means accept all. + auto pred = [DeclID](const IndexRecordDecl *D) { + return D->DeclID == DeclID; + }; + return std::find_if(Ds.begin(), Ds.end(), pred) != Ds.end(); + }; + + unsigned I = 0; + unsigned DeclID = Record[I++]; + if (!isDeclIDContained(DeclID, WhitelistDecls)) + return false; + + if (!WhitelistRelated.empty()) { + unsigned RelI = I + 3; + unsigned NumRelated = Record[RelI++]; + bool FoundRelated = false; + while (NumRelated--) { + ++RelI; // roles; + unsigned RelDID = Record[RelI++]; + if (isDeclIDContained(RelDID, WhitelistRelated)) { + FoundRelated = true; + break; + } + } + if (!FoundRelated) + return false; + } + + RecOccur.Dcl = getDeclByID(DeclID); + RecOccur.Roles = getSymbolRoles(Record[I++]); + RecOccur.Line = Record[I++]; + RecOccur.Column = Record[I++]; + + unsigned NumRelated = Record[I++]; + while (NumRelated--) { + SymbolRoleSet RelRoles = getSymbolRoles(Record[I++]); + const IndexRecordDecl *RelD = getDeclByID(Record[I++]); + RecOccur.Relations.emplace_back(RelRoles, RelD); + } + + return true; +} + +void IndexRecordReader::readDecl(unsigned Index, IndexRecordDecl &RecD) { + SmallVector<uint64_t, 64> Record; + StringRef Blob; + if (llvm::Error Err = DeclCursor.JumpToBit(DeclOffsets[Index])) { + // FIXME this drops errors on the floor. + consumeError(std::move(Err)); + llvm_unreachable("Failure in JumpToBit()"); + // FIXME error handling + return; + } + Expected<unsigned> MaybeCode = DeclCursor.ReadCode(); + if (!MaybeCode) { + // FIXME this drops errors on the floor. + consumeError(MaybeCode.takeError()); + llvm_unreachable("Failure in ReadCode()"); + // FIXME error handling + return; + } + + Expected<unsigned> MaybeRecID = + DeclCursor.readRecord(MaybeCode.get(), Record, &Blob); + if (!MaybeRecID) { + // FIXME this drops errors on the floor. + consumeError(MaybeRecID.takeError()); + llvm_unreachable("Failure in ReadRecord()"); + // FIXME error handling + return; + } + assert(MaybeRecID.get() == REC_DECLINFO); + unsigned I = 0; + RecD.DeclID = Index + 1; + RecD.SymInfo.Kind = getSymbolKind((indexstore_symbol_kind_t)Record[I++]); + RecD.SymInfo.SubKind = + getSymbolSubKind((indexstore_symbol_subkind_t)Record[I++]); + RecD.SymInfo.Lang = + getSymbolLanguage((indexstore_symbol_language_t)Record[I++]); + RecD.SymInfo.Properties = getSymbolProperties(Record[I++]); + RecD.Roles = getSymbolRoles(Record[I++]); + RecD.RelatedRoles = getSymbolRoles(Record[I++]); + size_t NameLen = Record[I++]; + size_t USRLen = Record[I++]; + RecD.Name = Blob.substr(0, NameLen); + RecD.USR = Blob.substr(NameLen, USRLen); + RecD.CodeGenName = Blob.substr(NameLen + USRLen); +} + +} // namespace index +} // namespace clang \ No newline at end of file Index: clang/lib/Index/IndexWhileBuilding/IndexRecordWriter.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/IndexRecordWriter.cpp @@ -0,0 +1,336 @@ +//===--- IndexRecordWriter.cpp - Index record serialization -----*- 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/IndexRecordWriter.h" +#include "BitstreamUtil.h" +#include "FSUtil.h" +#include "indexstore/Conversions.h" +#include "indexstore/indexstore.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Path.h" + +namespace clang { +namespace index { + +using namespace llvm; +using namespace store; + +void IndexRecordWriter::addDeclOccurence(SymbolRoleSet Roles, FileID FID, + unsigned Offset, const Decl *D, + ArrayRef<SymbolRelation> Relations) { + assert(D->isCanonicalDecl() && + "Occurrences should be associated with their canonical decl"); + + auto getIdxForDecl = [this](const Decl *D) -> unsigned { + const auto MaybeNewDecl = IndexForDecl.insert({D, Decls.size()}); + if (MaybeNewDecl.second) + Decls.push_back(DeclInfo{D, SymbolRoleSet(), SymbolRoleSet()}); + return MaybeNewDecl.first->second; + }; + + const unsigned DeclIdx = getIdxForDecl(D); + Decls[DeclIdx].Roles |= Roles; + + const uint64_t REC_DECLOCCURRENCE = 3; + + SourceManager &SM = D->getASTContext().getSourceManager(); + const unsigned Line = SM.getLineNumber(FID, Offset); + const unsigned Column = SM.getColumnNumber(FID, Offset); + + SmallVector<uint64_t, 10> OccurrenceData{ + REC_DECLOCCURRENCE, // FIXME: This is constant value. We don't need to + // store it. + DeclIdx + 1, getIndexStoreRoles(Roles), Line, Column, + Relations.size()}; + + OccurrenceData.reserve(OccurrenceData.size() + 2 * Relations.size()); + for (auto &Rel : Relations) { + OccurrenceData.push_back(getIndexStoreRoles(Rel.Roles)); + + const unsigned RelDeclIdx = getIdxForDecl(Rel.RelatedSymbol); + Decls[RelDeclIdx].RelatedRoles |= Rel.Roles; + + OccurrenceData.push_back(RelDeclIdx + 1); + } + + Occurrences.emplace(Offset, OccurrenceData); +} + +static void writeHeader(BitstreamWriter &Stream) { + Stream.Emit('I', 8); + Stream.Emit('D', 8); + Stream.Emit('X', 8); + Stream.Emit('R', 8); +} + +static void writeBlockInfo(BitstreamWriter &Stream) { + SmallVector<uint64_t, 64> Record; + + Stream.EnterBlockInfoBlock(); +#define BLOCK(X) emitBlockID(X##_ID, #X, Stream, Record) +#define RECORD(X) emitRecordID(X, #X, Stream, Record) + + BLOCK(REC_VERSION_BLOCK); + RECORD(REC_VERSION); + + BLOCK(REC_DECLS_BLOCK); + RECORD(REC_DECLINFO); + + BLOCK(REC_DECLOFFSETS_BLOCK); + RECORD(REC_DECLOFFSETS); + + BLOCK(REC_DECLOCCURRENCES_BLOCK); + RECORD(REC_DECLOCCURRENCE); + +#undef RECORD +#undef BLOCK + Stream.ExitBlock(); +} + +static std::shared_ptr<BitCodeAbbrev> REC_VERSION_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(REC_VERSION), + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6) // Store format version + })); + + return CachedResult; +} + +static void writeVersionInfo(BitstreamWriter &Stream) { + using namespace llvm::sys; + + Stream.EnterSubblock(REC_VERSION_BLOCK_ID, 3); + unsigned AbbrevCode = Stream.EmitAbbrev(REC_VERSION_BLOCK_ID_Abbrev()); + + const uint64_t Record[] = {REC_VERSION, getIndexFormatVersion()}; + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + + Stream.ExitBlock(); +} + +static std::shared_ptr<BitCodeAbbrev> DECLS_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(REC_DECLINFO), + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5), // Kind + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5), // SubKind + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5), // Language + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, + SymbolPropertyBitNum), // Properties + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum), // Roles + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, + SymbolRoleBitNum), // Related Roles + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6), // Length of name in block + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6), // Length of USR in block + BitCodeAbbrevOp(BitCodeAbbrevOp::Blob) // Name + USR + CodeGen symbol name + })); + + return CachedResult; +} + +static std::shared_ptr<BitCodeAbbrev> DECLOFFSETS_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(REC_DECLOFFSETS), + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32), // Number of Decls + BitCodeAbbrevOp(BitCodeAbbrevOp::Blob) // Offsets array + })); + + return CachedResult; +} + +static std::shared_ptr<BitCodeAbbrev> DECLOCCURRENCES_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(REC_DECLOCCURRENCE), + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8), // Decl ID + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum), // Roles + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12), // Line + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8), // Column + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4), // Num related + BitCodeAbbrevOp(BitCodeAbbrevOp::Array), // Related Roles/IDs + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16) // Roles or ID + })); + + return CachedResult; +} + +static SmallString<256> getUSR(const Decl *D) { + SmallString<256> Buf; + bool Ignore = generateUSRForDecl(D, Buf); + if (Ignore) + return {}; + return Buf; +} + +void IndexRecordWriter::getBitcodeRepresentation( + SmallString<512> &Buffer, const PrintingPolicy &Policy, + std::shared_ptr<CodegenNameGenerator> CGNameGen) const { + BitstreamWriter Stream(Buffer); + writeHeader(Stream); + writeBlockInfo(Stream); + writeVersionInfo(Stream); + + SmallVector<uint32_t, 32> DeclOffsets; + + { + Stream.EnterSubblock(REC_DECLS_BLOCK_ID, 3); + const unsigned AbbrevCode = Stream.EmitAbbrev(DECLS_BLOCK_ID_Abbrev()); + +#ifndef NDEBUG + StringSet<> USRSet; + bool enableValidation = getenv("CLANG_INDEX_VALIDATION_CHECKS") != nullptr; +#endif + + DeclOffsets.reserve(Decls.size()); + + llvm::SmallString<256> Scratch; + for (auto &Declaration : Decls) { + DeclOffsets.push_back(Stream.GetCurrentBitNo()); + Scratch.clear(); + + const Decl *D = static_cast<const Decl *>(Declaration.D); + auto Info = getSymbolInfo(D); + assert(Info.Kind != SymbolKind::Unknown); + + std::string Name; + std::string CGName; + if (auto *ND = dyn_cast<NamedDecl>(D)) { + DeclarationName DeclName = ND->getDeclName(); + if (!DeclName.isEmpty()) { + llvm::raw_string_ostream OS(Name); + DeclName.print(OS, Policy); + } + if (CGNameGen) { + llvm::raw_string_ostream OS(CGName); + CGNameGen->writeName(ND, OS); + } + } + + const SmallString<256> USR = getUSR(D); + assert(!USR.empty() && "Recorded decl without USR!"); + +#ifndef NDEBUG + if (enableValidation) { + bool IsNew = USRSet.insert(USR).second; + if (!IsNew) { + llvm::errs() << "Index: Duplicate USR! " << USR << "\n"; + // FIXME: print more information so it's easier to find the + // declaration. + } + } +#endif + + const uint64_t Record[] = {REC_DECLINFO, + getIndexStoreKind(Info.Kind), + getIndexStoreSubKind(Info.SubKind), + getIndexStoreLang(Info.Lang), + getIndexStoreProperties(Info.Properties), + getIndexStoreRoles(Declaration.Roles), + getIndexStoreRoles(Declaration.RelatedRoles), + Name.size(), + USR.size()}; + + Stream.EmitRecordWithBlob( + AbbrevCode, Record, + llvm::Twine(Name).concat(USR).concat(CGName).str()); + } + + Stream.ExitBlock(); + } + + { + Stream.EnterSubblock(REC_DECLOFFSETS_BLOCK_ID, 3); + const unsigned AbbrevCode = + Stream.EmitAbbrev(DECLOFFSETS_BLOCK_ID_Abbrev()); + const uint64_t Record[] = {REC_DECLOFFSETS, DeclOffsets.size()}; + StringRef DeclOffsetsData( + reinterpret_cast<const char *>(DeclOffsets.data()), + sizeof(uint32_t) * DeclOffsets.size()); + + Stream.EmitRecordWithBlob(AbbrevCode, Record, DeclOffsetsData); + Stream.ExitBlock(); + } + + Stream.EnterSubblock(REC_DECLOCCURRENCES_BLOCK_ID, 3); + const unsigned AbbrevCode = + Stream.EmitAbbrev(DECLOCCURRENCES_BLOCK_ID_Abbrev()); + + for (auto &OffsetAndOccurrence : Occurrences) { + Stream.EmitRecordWithAbbrev(AbbrevCode, OffsetAndOccurrence.second); + } + + Stream.ExitBlock(); +} + +template <unsigned N> +static void reportError(DiagnosticsEngine &Diag, const char (&Format)[N], + const std::string &Filename, + const std::string &Message) { + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, Format); + Diag.Report(DiagID) << Filename << Message; +} + +llvm::Optional<std::string> +IndexRecordWriter::writeToDisk(StringRef SourceFileName, + const EmitIndexOptions &RecordOpts, + const PrintingPolicy &Policy, + std::shared_ptr<CodegenNameGenerator> CGNameGen, + DiagnosticsEngine &Diag) const { + + llvm::SmallString<512> IndexRecordBuffer; + getBitcodeRepresentation(IndexRecordBuffer, Policy, CGNameGen); + + const std::string RecordFilename = + generateFilenameForIndexRecord(SourceFileName, IndexRecordBuffer); + + llvm::SmallString<256> RecordBucketSubdir(RecordOpts.RecordDir); + store::appendRecordBucketDir(RecordFilename, RecordBucketSubdir); + + if (std::error_code EC = sys::fs::create_directory(RecordBucketSubdir)) { + reportError(Diag, "failed to create directory '%0': %1", + RecordBucketSubdir.str(), EC.message()); + return llvm::None; + } + + llvm::SmallString<256> RecordPath(RecordBucketSubdir); + store::appendRecordFilename(RecordFilename, RecordPath); + + if (std::error_code EC = llvm::sys::fs::access( + RecordPath.str().str().c_str(), llvm::sys::fs::AccessMode::Exist)) { + if (EC == llvm::errc::no_such_file_or_directory) { + // Create a unique file to write to so that we can move the result into + // place atomically. If this process crashes we don't want to interfere + // with any other concurrent processes. + // FIXME: We should also make sure we don't eat up all the disk in case + // we're crashing repeatedly. + const SmallString<128> TempPath( + llvm::Twine(RecordPath).concat("-temp-%%%%%%%%").str()); + std::string Error; + if (atomicFileWrite(TempPath, RecordPath, IndexRecordBuffer, Error)) { + // FIXME: This assumes getDiagnosticClient().BeginSourceFile() has been + // called. + reportError(Diag, "failed writing record '%0': %1", RecordFilename, + Error); + return llvm::None; + } + } else { + reportError(Diag, "failed stating record file '%0': %1", RecordFilename, + ""); + return llvm::None; + } + } + + return RecordFilename; +} + +} // end namespace index +} // end namespace clang \ No newline at end of file Index: clang/lib/Index/IndexWhileBuilding/IndexUnitReader.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/IndexUnitReader.cpp @@ -0,0 +1,472 @@ +//===--- IndexUnitReader.cpp - Index unit deserialization -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/IndexUnitReader.h" +#include "BitstreamVisitor.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" + +#include "clang/Index/IndexWhileBuilding/PathStorage.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +namespace { + +class IndexUnitBitstreamVisitor + : public BitstreamVisitor<IndexUnitBitstreamVisitor> { +public: + llvm::BitstreamCursor DependenciesBlockCursor; + llvm::BitstreamCursor IncludedFilesCursor; + StringRef ProviderIdentifier; + StringRef ProviderVersion; + bool IsSystemUnit; + bool IsModuleUnit; + bool IsDebugCompilation; + StringRef ModuleName; + StringRef Target; + + int MainPathIndex; + + std::vector<FileBitPath> Paths; + StringRef PathsBuffer; + size_t WorkDirOffset; + size_t WorkDirSize; + size_t OutputFileOffset; + size_t OutputFileSize; + size_t SysrootOffset; + size_t SysrootSize; + + std::vector<ModuleInfo> Modules; + StringRef ModuleNamesBuffer; + + IndexUnitBitstreamVisitor(llvm::BitstreamCursor &Stream) + : BitstreamVisitor(Stream) {} + + StreamVisit visitBlock(unsigned ID) { + switch ((UnitBitBlock)ID) { + case UNIT_VERSION_BLOCK_ID: + case UNIT_INFO_BLOCK_ID: + case UNIT_PATHS_BLOCK_ID: + case UNIT_MODULES_BLOCK_ID: + return StreamVisit::Continue; + + case UNIT_DEPENDENCIES_BLOCK_ID: + DependenciesBlockCursor = Stream; + if (DependenciesBlockCursor.EnterSubBlock(ID)) { + *Error = "malformed unit dependencies block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(DependenciesBlockCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + case UNIT_INCLUDES_BLOCK_ID: + IncludedFilesCursor = Stream; + if (IncludedFilesCursor.EnterSubBlock(ID)) { + *Error = "malformed unit includes block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(IncludedFilesCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + } + + // Some newly introduced block in a minor version update that we cannot + // handle. + return StreamVisit::Skip; + } + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + SmallVectorImpl<uint64_t> &Record, StringRef Blob) { + switch (BlockID) { + case UNIT_VERSION_BLOCK_ID: { + unsigned StoreFormatVersion = Record[0]; + if (StoreFormatVersion != getIndexFormatVersion()) { + llvm::raw_string_ostream OS(*Error); + OS << "Store format version mismatch: " << StoreFormatVersion; + OS << " , expected: " << getIndexFormatVersion(); + return StreamVisit::Abort; + } + break; + } + + case UNIT_INFO_BLOCK_ID: { + assert(RecID == UNIT_INFO); + unsigned I = 0; + IsSystemUnit = Record[I++]; + + // Save these to lookup them up after we get the paths buffer. + WorkDirOffset = Record[I++]; + WorkDirSize = Record[I++]; + OutputFileOffset = Record[I++]; + OutputFileSize = Record[I++]; + SysrootOffset = Record[I++]; + SysrootSize = Record[I++]; + MainPathIndex = (int)Record[I++] - 1; + IsDebugCompilation = Record[I++]; + IsModuleUnit = Record[I++]; + + const size_t moduleNameSize = Record[I++]; + const size_t providerIdentifierSize = Record[I++]; + const size_t providerVersionSize = Record[I++]; + I++; // Reserved for ProviderDataVersion. + ModuleName = Blob.substr(0, moduleNameSize); + Blob = Blob.drop_front(moduleNameSize); + ProviderIdentifier = Blob.substr(0, providerIdentifierSize); + Blob = Blob.drop_front(providerIdentifierSize); + ProviderVersion = Blob.substr(0, providerVersionSize); + Target = Blob.drop_front(providerVersionSize); + break; + } + + case UNIT_PATHS_BLOCK_ID: + switch (RecID) { + case UNIT_PATH: { + unsigned I = 0; + UnitFilePathPrefixKind Kind = (UnitFilePathPrefixKind)Record[I++]; + size_t DirOffset = Record[I++]; + size_t DirSize = Record[I++]; + size_t FilenameOffset = Record[I++]; + size_t FilenameSize = Record[I++]; + + Paths.emplace_back(Kind, BitPathComponent(DirOffset, DirSize), + BitPathComponent(FilenameOffset, FilenameSize)); + } break; + case UNIT_PATH_BUFFER: + PathsBuffer = Blob; + break; + default: + llvm_unreachable("shouldn't visit this record"); + } + break; + + case UNIT_MODULES_BLOCK_ID: + switch (RecID) { + case UNIT_MODULE: { + unsigned I = 0; + unsigned NameOffset = Record[I++]; + unsigned NameSize = Record[I++]; + Modules.push_back({NameOffset, NameSize}); + } break; + case UNIT_MODULE_BUFFER: + ModuleNamesBuffer = Blob; + break; + default: + llvm_unreachable("shouldn't visit this record"); + } + break; + + case UNIT_DEPENDENCIES_BLOCK_ID: + case UNIT_INCLUDES_BLOCK_ID: + llvm_unreachable("shouldn't visit this block'"); + } + return StreamVisit::Continue; + } +}; + +typedef std::function<bool(SmallVectorImpl<uint64_t> &Record, StringRef Blob)> + BlockVisitorCallback; + +class IndexUnitBlockBitstreamVisitor + : public BitstreamVisitor<IndexUnitBlockBitstreamVisitor> { + unsigned RecID; + BlockVisitorCallback Callback; + +public: + IndexUnitBlockBitstreamVisitor(unsigned RecID, + llvm::BitstreamCursor &BlockStream, + BlockVisitorCallback Callback) + : BitstreamVisitor(BlockStream), RecID(RecID), Callback(Callback) {} + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + SmallVectorImpl<uint64_t> &Record, StringRef Blob) { + if (RecID != this->RecID) + llvm_unreachable("shouldn't be called with this RecID"); + + if (Callback(Record, Blob)) + return StreamVisit::Continue; + return StreamVisit::Abort; + } +}; + +} // anonymous namespace + +std::unique_ptr<IndexUnitReader> +IndexUnitReader::create(std::unique_ptr<MemoryBuffer> Buf, + sys::TimePoint<> ModTime, std::string &Error) { + if (!Buf) { + Error = "null buffer"; + return nullptr; + } + + llvm::BitstreamCursor Stream(*Buf); + + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return nullptr; + } + + // Sniff for the signature. + for (unsigned char C : {'I', 'D', 'X', 'U'}) { + if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) { + if (Res.get() == C) + continue; + } else { + Error = toString(Res.takeError()); + return nullptr; + } + Error = "not a serialized index unit file"; + return nullptr; + } + + IndexUnitBitstreamVisitor Visitor(Stream); + if (!Visitor.visit(Error)) { + // Error is set by Visitor + return nullptr; + } + + return llvm::make_unique<IndexUnitReader>( + std::move(Buf), std::move(Visitor.DependenciesBlockCursor), + std::move(Visitor.IncludedFilesCursor), Visitor.ProviderIdentifier, + Visitor.ProviderVersion, Visitor.IsSystemUnit, Visitor.IsModuleUnit, + Visitor.IsDebugCompilation, Visitor.ModuleName, Visitor.Target, + Visitor.MainPathIndex, std::move(Visitor.Paths), Visitor.PathsBuffer, + Visitor.WorkDirOffset, Visitor.WorkDirSize, Visitor.OutputFileOffset, + Visitor.OutputFileSize, Visitor.SysrootOffset, Visitor.SysrootSize, + std::move(Visitor.Modules), Visitor.ModuleNamesBuffer, ModTime); +} + +std::unique_ptr<IndexUnitReader> IndexUnitReader::createWithUnitFilename( + StringRef UnitFilename, StringRef StorePath, std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendUnitSubDir(PathBuf); + sys::path::append(PathBuf, UnitFilename); + return createWithFilePath(PathBuf.str(), Error); +} + +std::unique_ptr<IndexUnitReader> +IndexUnitReader::createWithFilePath(StringRef FilePath, std::string &Error) { + int FD; + std::error_code EC = sys::fs::openFileForRead(FilePath, FD); + if (EC) { + raw_string_ostream(Error) + << "Failed opening '" << FilePath << "': " << EC.message(); + return nullptr; + } + assert(FD != -1); + auto FDScopeGuard = llvm::make_scope_exit( + [FD]() { llvm::sys::Process::SafelyCloseFileDescriptor(FD); }); + + sys::fs::file_status FileStat; + EC = sys::fs::status(FD, FileStat); + if (EC) { + Error = EC.message(); + return nullptr; + } + + auto ErrOrBuf = MemoryBuffer::getOpenFile(sys::fs::convertFDToNativeFile(FD), + FilePath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrOrBuf) { + raw_string_ostream(Error) << "Failed opening '" << FilePath + << "': " << ErrOrBuf.getError().message(); + return nullptr; + } + + return IndexUnitReader::create(std::move(*ErrOrBuf), + FileStat.getLastModificationTime(), Error); +} + +Optional<sys::TimePoint<>> IndexUnitReader::getModificationTimeForUnit( + StringRef UnitFilename, StringRef StorePath, std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendUnitSubDir(PathBuf); + sys::path::append(PathBuf, UnitFilename); + + sys::fs::file_status FileStat; + std::error_code EC = sys::fs::status(PathBuf.str(), FileStat); + if (EC) { + Error = EC.message(); + return None; + } + return FileStat.getLastModificationTime(); +} + +StringRef IndexUnitReader::getProviderIdentifier() const { + return ProviderIdentifier; +} + +StringRef IndexUnitReader::getProviderVersion() const { + return ProviderVersion; +} + +llvm::sys::TimePoint<> IndexUnitReader::getModificationTime() const { + return ModTime; +} + +StringRef IndexUnitReader::getWorkingDirectory() const { + return PathsBuffer.substr(WorkDirOffset, WorkDirSize); +} + +StringRef IndexUnitReader::getOutputFile() const { + return PathsBuffer.substr(OutputFileOffset, OutputFileSize); +} + +StringRef IndexUnitReader::getSysrootPath() const { + return PathsBuffer.substr(SysrootOffset, SysrootSize); +} + +StringRef IndexUnitReader::getMainFilePath() const { return MainFilePath; } + +StringRef IndexUnitReader::getModuleName() const { return ModuleName; } + +StringRef IndexUnitReader::getTarget() const { return Target; } + +bool IndexUnitReader::hasMainFile() const { return !MainFilePath.empty(); } + +bool IndexUnitReader::isSystemUnit() const { return IsSystemUnit; } + +bool IndexUnitReader::isModuleUnit() const { return IsModuleUnit; } + +bool IndexUnitReader::isDebugCompilation() const { return IsDebugCompilation; } + +void IndexUnitReader::constructFilePath(SmallVectorImpl<char> &PathBuf, + int PathIndex) const { + + if (PathIndex < 0) + return; + assert(PathIndex < Paths.size()); + const FileBitPath &Path = Paths[PathIndex]; + StringRef Prefix; + switch (Path.PrefixKind) { + case UNIT_PATH_PREFIX_NONE: + break; + case UNIT_PATH_PREFIX_WORKDIR: + Prefix = getWorkingDirectory(); + break; + case UNIT_PATH_PREFIX_SYSROOT: + Prefix = getSysrootPath(); + break; + } + PathBuf.append(Prefix.begin(), Prefix.end()); + sys::path::append( + PathBuf, PathsBuffer.substr(Path.Dir.Offset, Path.Dir.Size), + PathsBuffer.substr(Path.Filename.Offset, Path.Filename.Size)); +} + +/// \c Index is the index in the \c getDependencies array. +/// Unit dependencies are provided ahead of record ones, record ones +/// ahead of the file ones. +bool IndexUnitReader::foreachDependency( + llvm::function_ref<bool(const DependencyInfo &Info)> Receiver) { + store::SavedStreamPosition SavedDepPosition(DependenciesBlockCursor); + IndexUnitBlockBitstreamVisitor Visitor( + UNIT_DEPENDENCY, DependenciesBlockCursor, + [&](SmallVectorImpl<uint64_t> &Record, StringRef Blob) { + unsigned I = 0; + UnitDependencyKind UnitDepKind = (UnitDependencyKind)Record[I++]; + bool IsSystem = Record[I++]; + int PathIndex = (int)Record[I++] - 1; + int ModuleIndex = (int)Record[I++] - 1; + I++; // Reserved field. + I++; // Reserved field. + StringRef Name = Blob; + + IndexUnitReader::DependencyKind DepKind; + switch (UnitDepKind) { + case UNIT_DEPEND_KIND_UNIT: + DepKind = IndexUnitReader::DependencyKind::Unit; + break; + case UNIT_DEPEND_KIND_RECORD: + DepKind = IndexUnitReader::DependencyKind::Record; + break; + case UNIT_DEPEND_KIND_FILE: + DepKind = IndexUnitReader::DependencyKind::File; + break; + } + + SmallString<512> PathBuf; + constructFilePath(PathBuf, PathIndex); + + StringRef ModuleName; + if (ModuleIndex >= 0) { + auto &ModInfo = Modules[ModuleIndex]; + ModuleName = StringRef(ModuleNamesBuffer.data() + ModInfo.NameOffset, + ModInfo.NameSize); + } + + return Receiver(IndexUnitReader::DependencyInfo{ + DepKind, IsSystem, Name, PathBuf.str(), ModuleName}); + }); + + std::string Error; // FIXME + return Visitor.visit(Error); +} + +bool IndexUnitReader::foreachInclude( + llvm::function_ref<bool(const IncludeInfo &Info)> Receiver) { + store::SavedStreamPosition SavedIncPosition(IncludedFilesCursor); + IndexUnitBlockBitstreamVisitor Visitor( + UNIT_INCLUDE, IncludedFilesCursor, + [&](SmallVectorImpl<uint64_t> &Record, StringRef Blob) { + unsigned I = 0; + int SourcePathIndex = (int)Record[I++] - 1; + unsigned Line = Record[I++]; + int TargetPathIndex = (int)Record[I++] - 1; + + SmallString<512> SourceBuf, TargetBuf; + this->constructFilePath(SourceBuf, SourcePathIndex); + this->constructFilePath(TargetBuf, TargetPathIndex); + return Receiver(IndexUnitReader::IncludeInfo{SourceBuf.str(), Line, + TargetBuf.str()}); + }); + + std::string Error; + return Visitor.visit(Error); +} + +IndexUnitReader::IndexUnitReader( + std::unique_ptr<llvm::MemoryBuffer> Buf, + llvm::BitstreamCursor &&DependenciesBlockCursor, + llvm::BitstreamCursor &&IncludedFilesCursor, StringRef ProviderIdentifier, + StringRef ProviderVersion, bool IsSystemUnit, bool IsModuleUnit, + bool IsDebugCompilation, StringRef ModuleName, StringRef Target, + int MainPathIndex, std::vector<FileBitPath> &&Paths, StringRef PathsBuffer, + // Offset-size pairs to the PathsBuffer. + size_t WorkDirOffset, size_t WorkDirSize, size_t OutputFileOffset, + size_t OutputFileSize, size_t SysrootOffset, size_t SysrootSize, + std::vector<ModuleInfo> &&Modules, StringRef ModuleNamesBuffer, + llvm::sys::TimePoint<> ModTime) + : Buf(std::move(Buf)), DependenciesBlockCursor(DependenciesBlockCursor), + IncludedFilesCursor(IncludedFilesCursor), + ProviderIdentifier(ProviderIdentifier), ProviderVersion(ProviderVersion), + IsSystemUnit(IsSystemUnit), IsModuleUnit(IsModuleUnit), + IsDebugCompilation(IsDebugCompilation), ModuleName(ModuleName), + Target(Target), Paths(Paths), PathsBuffer(PathsBuffer), + WorkDirOffset(WorkDirOffset), WorkDirSize(WorkDirSize), + OutputFileOffset(OutputFileOffset), OutputFileSize(OutputFileSize), + SysrootOffset(SysrootOffset), SysrootSize(SysrootSize), Modules(Modules), + ModuleNamesBuffer(ModuleNamesBuffer), ModTime(ModTime) { + constructFilePath(MainFilePath, MainPathIndex); +} \ No newline at end of file Index: clang/lib/Index/IndexWhileBuilding/IndexUnitWriter.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/IndexUnitWriter.cpp @@ -0,0 +1,559 @@ +//===--- IndexUnitWriter.cpp - Index unit serialization -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/IndexUnitWriter.h" +#include "BitstreamUtil.h" +#include "FSUtil.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/Module.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +// FIXME: should this be in clang/Basic/Version? +std::string getClangVersion() { + // Try picking the version from an Apple Clang tag. + const std::string RepositoryPath = clang::getClangRepositoryPath(); + llvm::StringRef BuildNumber(RepositoryPath); + if (BuildNumber.startswith("clang")) { + const size_t DashOffset = BuildNumber.find('-'); + if (DashOffset != llvm::StringRef::npos) + return BuildNumber.substr(DashOffset + 1); + } + // Fallback to the generic version. + return CLANG_VERSION_STRING; +} +} // namespace + +namespace clang { +namespace index { + +using namespace clang::index::store; +using namespace llvm; +using namespace llvm::sys; + +unsigned IndexUnitWriter::addModule(writer::OpaqueModule Mod) { + if (!Mod) + return 0; + const auto Inserted = ModToIdx.insert({Mod, ModToIdx.size() + 1}); + if (Inserted.second) { + SmallString<64> ModuleName; + StringRef name = GetInfoForModuleFn(Mod, ModuleName).Name; + const size_t ModuleNameOffset = ModuleNamesBuf.size(); + ModuleNamesBuf += name; + + Modules.push_back({UNIT_MODULE, ModuleNameOffset, name.size()}); + } + return Inserted.first->second; +} + +IndexUnitWriter::IndexUnitWriter( + FileManager &FileMgr, const EmitIndexOptions &RecOpts, + StringRef ProviderIdentifier, StringRef ProviderVersion, + StringRef OutputFile, StringRef AbsPathToOutputFile, + const FileEntry *MainFile, + const llvm::Optional<ModuleIndexData> &ModuleData, bool IsDebugCompilation, + StringRef TargetTriple, StringRef WorkDir, StringRef SysrootPath, + writer::ModuleInfoWriterCallback GetInfoForModule) + : RecOpts(RecOpts), ProviderIdentifier(ProviderIdentifier), + ProviderVersion(ProviderVersion), OutputFile(OutputFile), + AbsPathToOutputFile(AbsPathToOutputFile), MainFile(MainFile), + ModuleData(ModuleData), IsDebugCompilation(IsDebugCompilation), + TargetTriple(TargetTriple), WorkDir(WorkDir), SysrootPath(SysrootPath), + PathStore(WorkDir, SysrootPath), GetInfoForModuleFn(GetInfoForModule) {} + +void IndexUnitWriter::addSourceFile(const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod) { + assert(File); + + SourceFiles[File->getUID()] = std::array<uint64_t, 7>{ + UNIT_DEPENDENCY, + UNIT_DEPEND_KIND_FILE, + IsSystem, + static_cast<uint64_t>(PathStore.getPathIndex(File) + 1), + addModule(Mod), + 0, // Reserved. + 0 // Reserved. + }; +} + +void IndexUnitWriter::addIndexRecord(StringRef FileName, + const FileEntry *SourceFile, bool IsSystem, + writer::OpaqueModule Mod) { + assert(SourceFile); + + SourceFileHasARecord.insert(SourceFile->getUID()); + + IndexRecordDependencies.push_back( + std::make_pair<std::string, std::array<uint64_t, 7>>( + FileName, + { + UNIT_DEPENDENCY, UNIT_DEPEND_KIND_RECORD, IsSystem, + static_cast<uint64_t>(PathStore.getPathIndex(SourceFile) + 1), + addModule(Mod), + 0, // Reserved. + 0 // Reserved. + })); +} + +void IndexUnitWriter::addIndexUnit(const FileEntry *ASTFile, + StringRef AbsolutePath, bool IsSystem, + writer::OpaqueModule Mod, + bool withoutUnitName) { + assert(ASTFile); + if (!ASTFilesOfSeenIndexUnits.insert(ASTFile->getUID()).second) + return; + + SmallString<64> UnitName; + if (!withoutUnitName) { + getUnitNameForOutputFile(AbsolutePath, UnitName); + } + + IndexUnitDependencies.emplace_back( + UnitName, std::array<uint64_t, 7>{ + UNIT_DEPENDENCY, UNIT_DEPEND_KIND_UNIT, IsSystem, + static_cast<uint64_t>(PathStore.getPathIndex(ASTFile) + 1), + addModule(Mod), + 0, // Reserved. + 0 // Reserved. + }); +} + +void IndexUnitWriter::addIncludedFile(const FileEntry *IncludingFile, + unsigned LineNo, + const FileEntry *IncludedFile) { + IncludedFiles.push_back( + {UNIT_INCLUDE, // FIXME: This is static data which don't have to be stored + // here. + static_cast<uint64_t>(PathStore.getPathIndex(IncludingFile) + 1), LineNo, + static_cast<uint64_t>(PathStore.getPathIndex(IncludedFile) + 1)}); +}; + +static void writeBlockInfo(BitstreamWriter &Stream) { + SmallVector<uint64_t, 64> Record; + + Stream.EnterBlockInfoBlock(); +#define BLOCK(X) emitBlockID(X##_ID, #X, Stream, Record) +#define RECORD(X) emitRecordID(X, #X, Stream, Record) + + BLOCK(UNIT_VERSION_BLOCK); + RECORD(UNIT_VERSION); + + BLOCK(UNIT_INFO_BLOCK); + RECORD(UNIT_INFO); + + BLOCK(UNIT_DEPENDENCIES_BLOCK); + RECORD(UNIT_DEPENDENCY); + + BLOCK(UNIT_INCLUDES_BLOCK); + RECORD(UNIT_INCLUDE); + + BLOCK(UNIT_PATHS_BLOCK); + RECORD(UNIT_PATH); + RECORD(UNIT_PATH_BUFFER); + + BLOCK(UNIT_MODULES_BLOCK); + RECORD(UNIT_MODULE); + RECORD(UNIT_MODULE_BUFFER); + +#undef RECORD +#undef BLOCK + Stream.ExitBlock(); +} + +static std::shared_ptr<BitCodeAbbrev> UNIT_VERSION_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_VERSION), + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6) // Store format version + })); + + return CachedResult; +} + +static void writeVersionInfo(BitstreamWriter &Stream) { + using namespace llvm::sys; + + Stream.EnterSubblock(UNIT_VERSION_BLOCK_ID, 3); + unsigned AbbrevCode = Stream.EmitAbbrev(UNIT_VERSION_BLOCK_ID_Abbrev()); + + const uint64_t Record[2]{UNIT_VERSION, getIndexFormatVersion()}; + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + + Stream.ExitBlock(); +} + +bool IndexUnitWriter::writeToDisk(std::string &Error) const { + SmallString<512> BitcodeRepresentation; + getBitcodeRepresentation(BitcodeRepresentation, Error); + + SmallString<256> UnitPath = + StringRef(llvm::Twine(RecOpts.UnitDir) + .concat(llvm::sys::path::get_separator()) + .str()); + + getUnitNameForOutputFile(AbsPathToOutputFile, UnitPath); + + const SmallString<128> TempPath( + llvm::Twine(path::parent_path(RecOpts.UnitDir)) + .concat(llvm::sys::path::get_separator().str()) + .concat(path::filename(UnitPath)) + .concat("-%%%%%%%%") + .str()); + + return atomicFileWrite(TempPath, UnitPath, BitcodeRepresentation, Error); +} + +void IndexUnitWriter::getBitcodeRepresentation(SmallString<512> &Buffer, + std::string &Error) const { + using namespace llvm::sys; + + BitstreamWriter Stream(Buffer); + Stream.Emit('I', 8); + Stream.Emit('D', 8); + Stream.Emit('X', 8); + Stream.Emit('U', 8); + + writeBlockInfo(Stream); + writeVersionInfo(Stream); + writeUnitInfo(Stream); + writeDependencies(Stream); + writeIncludes(Stream); + writePaths(Stream); + writeModules(Stream); +} + +static std::shared_ptr<BitCodeAbbrev> UNIT_INFO_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_INFO), + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1), // IsSystemUnit + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10), // WorkDir offset + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8), // WorkDir size + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10), // OutputFile offset + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8), // OutputFile size + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10), // Sysroot offset + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8), // Sysroot size + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10), // Main path id + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1), // IsDebugCompilation + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1), // IsModuleUnit + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5), // Module name size + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5), // ProviderIdentifier size + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5), // ProviderVersion size + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5), // ProviderDataVersion + BitCodeAbbrevOp(BitCodeAbbrevOp::Blob) // Module name + ProviderIdentifier + // + ProviderVersion + target + // triple + })); + + return CachedResult; +} + +void IndexUnitWriter::writeUnitInfo(llvm::BitstreamWriter &Stream) const { + Stream.EnterSubblock(UNIT_INFO_BLOCK_ID, 3); + unsigned AbbrevCode = Stream.EmitAbbrev(UNIT_INFO_BLOCK_ID_Abbrev()); + + const std::string ModuleName = ModuleData.hasValue() ? ModuleData->Name : ""; + + const uint64_t Record[15]{ + UNIT_INFO, + ModuleData.hasValue() && + ModuleData->IsSystem, // We don't expect compilation of any source + // code that would be considered system unless + // it's a dependency in form of module. + PathStore.getPathOffset(WorkDir), WorkDir.size(), + PathStore.getPathOffset(OutputFile), OutputFile.size(), + PathStore.getPathOffset(SysrootPath), SysrootPath.size(), + static_cast<uint64_t>(PathStore.getPathIndex(MainFile) + + 1), // Make 1-based with 0=invalid + IsDebugCompilation, ModuleData.hasValue(), ModuleName.size(), + ProviderIdentifier.size(), ProviderVersion.size(), + // ProviderDataVersion is reserved. Not sure it is a good to idea to have + // clients consider the specifics of a 'provider data version', but + // reserving to avoid store format version change in case there is a use + // case in the future. + 0 // ProviderDataVersion + }; + const std::string InfoStrings = llvm::Twine(ModuleName) + .concat(ProviderIdentifier) + .concat(ProviderVersion) + .concat(TargetTriple) + .str(); + + Stream.EmitRecordWithBlob(AbbrevCode, Record, InfoStrings); + + Stream.ExitBlock(); +} + +static std::shared_ptr<BitCodeAbbrev> UNIT_DEPENDENCIES_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_DEPENDENCY), + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, + UnitDependencyKindBitNum), // Dependency kind + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1), // IsSystem + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, + 10), // PathIndex (1-based, 0 = none) + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, + 8), // ModuleIndex (1-based, 0 = none) + // Reserved. These used to be time_t & file size but we decided against + // writing these in order to get reproducible build products (index data + // output being the same with the same inputs). Keep these reserved for + // the future, for coming up with a better scheme to track state of + // dependencies without using modification time. + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 0), // Reserved + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 0), // Reserved + BitCodeAbbrevOp(BitCodeAbbrevOp::Blob) // Name + })); + + return CachedResult; +} + +void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream) const { + Stream.EnterSubblock(UNIT_DEPENDENCIES_BLOCK_ID, 3); + + unsigned AbbrevCode = Stream.EmitAbbrev(UNIT_DEPENDENCIES_BLOCK_ID_Abbrev()); + + for (auto &IdxUnit : IndexUnitDependencies) { + Stream.EmitRecordWithBlob(AbbrevCode, IdxUnit.second, IdxUnit.first); + } + + for (auto &recordData : IndexRecordDependencies) { + Stream.EmitRecordWithBlob(AbbrevCode, recordData.second, recordData.first); + } + + for (const auto &FileAndData : SourceFiles) { + if (SourceFileHasARecord.count(FileAndData.first) > 0) + continue; + Stream.EmitRecordWithBlob(AbbrevCode, FileAndData.second, StringRef()); + } + + Stream.ExitBlock(); +} + +static std::shared_ptr<BitCodeAbbrev> UNIT_INCLUDES_BLOCK_ID_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_INCLUDE), + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, + 10), // source path index (1-based, 0 = no path) + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12), // source include line + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, + 10) // target path index (1-based, 0 = no path) + })); + + return CachedResult; +} + +void IndexUnitWriter::writeIncludes(llvm::BitstreamWriter &Stream) const { + Stream.EnterSubblock(UNIT_INCLUDES_BLOCK_ID, 3); + + unsigned AbbrevCode = Stream.EmitAbbrev(UNIT_INCLUDES_BLOCK_ID_Abbrev()); + + for (const auto &IncludeDirective : IncludedFiles) { + Stream.EmitRecordWithAbbrev(AbbrevCode, IncludeDirective); + } + + Stream.ExitBlock(); +} + +static std::shared_ptr<BitCodeAbbrev> UNIT_PATHS_BLOCK_ID_Paths_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_PATH), + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, + UnitFilePathPrefixKindBitNum), // Path prefix kind + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10), // DirPath offset + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8), // DirPath size + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10), // Filename offset + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6) // Filename size + })); + + return CachedResult; +} + +static std::shared_ptr<BitCodeAbbrev> +UNIT_PATHS_BLOCK_ID_Path_Buffers_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_PATH_BUFFER), + BitCodeAbbrevOp(BitCodeAbbrevOp::Blob) // Paths buffer + })); + + return CachedResult; +} + +void IndexUnitWriter::writePaths(llvm::BitstreamWriter &Stream) const { + Stream.EnterSubblock(UNIT_PATHS_BLOCK_ID, 3); + + const unsigned PathAbbrevCode = + Stream.EmitAbbrev(UNIT_PATHS_BLOCK_ID_Paths_Abbrev()); + const unsigned PathBufferAbbrevCode = + Stream.EmitAbbrev(UNIT_PATHS_BLOCK_ID_Path_Buffers_Abbrev()); + + for (auto &BitPath : PathStore.getBitPaths()) { + const uint64_t Record[6]{ + UNIT_PATH, BitPath.PrefixKind, BitPath.Dir.Offset, + BitPath.Dir.Size, BitPath.Filename.Offset, BitPath.Filename.Size}; + Stream.EmitRecordWithAbbrev(PathAbbrevCode, Record); + } + + Stream.EmitRecordWithBlob(PathBufferAbbrevCode, UNIT_PATH_BUFFER, + PathStore.getPathsBuffer()); + + Stream.ExitBlock(); +} + +static std::shared_ptr<BitCodeAbbrev> UNIT_MODULES_BLOCK_ID_Module_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_MODULE), + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 9), // Module name offset + BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6) // Module name size + })); + + return CachedResult; +} + +static std::shared_ptr<BitCodeAbbrev> +UNIT_MODULES_BLOCK_ID_Module_Buffer_Abbrev() { + static const std::shared_ptr<BitCodeAbbrev> CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(UNIT_MODULE_BUFFER), + BitCodeAbbrevOp(BitCodeAbbrevOp::Blob) // Module names buffer + })); + + return CachedResult; +} + +void IndexUnitWriter::writeModules(llvm::BitstreamWriter &Stream) const { + Stream.EnterSubblock(UNIT_MODULES_BLOCK_ID, 3); + unsigned AbbrevCode = + Stream.EmitAbbrev(UNIT_MODULES_BLOCK_ID_Module_Abbrev()); + unsigned BufferAbbrevCode = + Stream.EmitAbbrev(UNIT_MODULES_BLOCK_ID_Module_Buffer_Abbrev()); + + for (auto &Module : Modules) { + Stream.EmitRecordWithAbbrev(AbbrevCode, Module); + } + + Stream.EmitRecordWithBlob(BufferAbbrevCode, UNIT_MODULE_BUFFER, + ModuleNamesBuf.str()); + Stream.ExitBlock(); +} + +static writer::ModuleInfo getModuleInfo(writer::OpaqueModule mod, + SmallVectorImpl<char> &Scratch) { + assert(mod); + writer::ModuleInfo info; + const std::string fullName = + static_cast<const Module *>(mod)->getFullModuleName(); + unsigned offset = Scratch.size(); + Scratch.append(fullName.begin(), fullName.end()); + info.Name = StringRef(Scratch.data() + offset, fullName.size()); + return info; +} + +IndexUnitWriter makeIndexUnitWriter(const CompilerInstance &CI, + const EmitIndexOptions &RecordOpts, + StringRef OutputFile, + const FileEntry *MainFile, + Module *UnitModule) { + assert(MainFile == nullptr || UnitModule == nullptr); + + SmallString<256> AbsOutputFilePath(OutputFile); + CI.getFileManager().makeAbsolutePath(AbsOutputFilePath); + + llvm::Optional<ModuleIndexData> ModuleData = + [UnitModule]() -> llvm::Optional<ModuleIndexData> { + if (UnitModule) { + return ModuleIndexData{UnitModule->getFullModuleName(), + static_cast<bool>(UnitModule->IsSystem)}; + } + return llvm::None; + }(); + + SmallString<128> WorkDir; + if (!CI.getFileManager().getFileSystemOpts().WorkingDir.empty()) { + WorkDir = CI.getFileManager().getFileSystemOpts().WorkingDir; + if (!llvm::sys::path::is_absolute(WorkDir)) { + llvm::sys::fs::make_absolute(WorkDir); + } + } else { + std::error_code EC = llvm::sys::fs::current_path(WorkDir); + if (EC) { + // FIXME: error handling + llvm_unreachable("failed to determine current working directory"); + } + } + + return IndexUnitWriter{CI.getFileManager(), + RecordOpts, + "clang", + getClangVersion(), + OutputFile, + AbsOutputFilePath, + MainFile, + ModuleData, + CI.getCodeGenOpts().OptimizationLevel == 0, + CI.getTargetOpts().Triple, + WorkDir.str(), + CI.getHeaderSearchOpts().Sysroot, + getModuleInfo}; +} + +bool initIndexDirectory(const EmitIndexOptions &RecordingOpts, + std::string &Error /* <- kill */) { + using namespace llvm::sys; + auto CreateDir = [&](StringRef DirPath) { + std::error_code EC = fs::create_directories(DirPath); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create directory '" << DirPath << "': " << EC.message(); + return true; + } + return false; + }; + if (CreateDir(RecordingOpts.IndexDir)) + return true; + if (CreateDir(RecordingOpts.RecordDir)) + return true; + if (CreateDir(RecordingOpts.UnitDir)) + return true; + + return false; +} + +llvm::Optional<bool> +doesIndexUnitForModuleExist(StringRef ModuleFilePath, + const FileManager &FileMgr, + const EmitIndexOptions &RecOpts) { + SmallString<256> UnitPath( + StringRef(llvm::Twine(RecOpts.UnitDir) + .concat(llvm::sys::path::get_separator().str()) + .str())); + + SmallString<256> AbsPath(ModuleFilePath); + FileMgr.makeAbsolutePath(AbsPath); + + getUnitNameForOutputFile(AbsPath, UnitPath); + + llvm::sys::fs::file_status UnitStat; + if (std::error_code EC = llvm::sys::fs::status(UnitPath.c_str(), UnitStat)) { + if (EC == llvm::errc::no_such_file_or_directory) + return false; + + return None; + } + return true; +} + +} // namespace index +} // namespace clang \ No newline at end of file Index: clang/lib/Index/IndexWhileBuilding/PathStorage.cpp =================================================================== --- /dev/null +++ clang/lib/Index/IndexWhileBuilding/PathStorage.cpp @@ -0,0 +1,78 @@ +//===--- PathStorage.cpp -- Index of paths ----------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexWhileBuilding/PathStorage.h" + +using namespace llvm; +using namespace clang; + +DirBitPath::DirBitPath(UnitFilePathPrefixKind Kind, BitPathComponent Dir) + : PrefixKind(Kind), Dir(Dir) {} +DirBitPath::DirBitPath() : PrefixKind(UNIT_PATH_PREFIX_NONE) {} + +PathStorage::PathStorage(StringRef workDir, StringRef sysrootPath) { + WorkDir = workDir; + if (sysrootPath == "/") + sysrootPath = StringRef(); + SysrootPath = sysrootPath; +} + +int PathStorage::getPathIndex(const FileEntry *FE) { + if (!FE) + return -1; + auto Pair = FileToIndex.insert(std::make_pair(FE, FileBitPaths.size())); + bool IsNew = Pair.second; + size_t Index = Pair.first->getSecond(); + + if (IsNew) { + StringRef Filename = sys::path::filename(FE->getName()); + DirBitPath Dir = getDirBitPath(sys::path::parent_path(FE->getName())); + FileBitPaths.emplace_back( + Dir.PrefixKind, Dir.Dir, + BitPathComponent(getPathOffset(Filename), Filename.size())); + } + return Index; +} + +size_t PathStorage::getPathOffset(StringRef Path) { + if (Path.empty()) + return 0; + size_t offset = PathsBuf.size(); + PathsBuf += Path; + return offset; +} + +static bool isPathInDir(StringRef dir, StringRef path) { + if (dir.empty() || !path.startswith(dir)) + return false; + StringRef rest = path.drop_front(dir.size()); + return !rest.empty() && sys::path::is_separator(rest.front()); +} + +DirBitPath PathStorage::getDirBitPath(StringRef dirStr) { + auto pair = Dirs.insert(std::make_pair(dirStr, DirBitPath())); + bool isNew = pair.second; + auto &dirPath = pair.first->second; + + if (isNew) { + if (isPathInDir(SysrootPath, dirStr)) { + dirPath.PrefixKind = UNIT_PATH_PREFIX_SYSROOT; + dirStr = dirStr.drop_front(SysrootPath.size()); + while (!dirStr.empty() && dirStr[0] == '/') + dirStr = dirStr.drop_front(); + } else if (isPathInDir(WorkDir, dirStr)) { + dirPath.PrefixKind = UNIT_PATH_PREFIX_WORKDIR; + dirStr = dirStr.drop_front(WorkDir.size()); + while (!dirStr.empty() && dirStr[0] == '/') + dirStr = dirStr.drop_front(); + } + dirPath.Dir.Offset = getPathOffset(dirStr); + dirPath.Dir.Size = dirStr.size(); + } + return dirPath; +} Index: clang/lib/Index/IndexingAction.cpp =================================================================== --- clang/lib/Index/IndexingAction.cpp +++ /dev/null @@ -1,279 +0,0 @@ -//===- IndexingAction.cpp - Frontend index action -------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "clang/Index/IndexingAction.h" -#include "IndexingContext.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" -#include "clang/Frontend/MultiplexConsumer.h" -#include "clang/Index/IndexDataConsumer.h" -#include "clang/Lex/PPCallbacks.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Serialization/ASTReader.h" -#include "llvm/ADT/STLExtras.h" -#include <memory> - -using namespace clang; -using namespace clang::index; - -bool IndexDataConsumer::handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, - ArrayRef<SymbolRelation> Relations, - SourceLocation Loc, - ASTNodeInfo ASTNode) { - return true; -} - -bool IndexDataConsumer::handleMacroOccurence(const IdentifierInfo *Name, - const MacroInfo *MI, - SymbolRoleSet Roles, - SourceLocation Loc) { - return true; -} - -bool IndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD, - const Module *Mod, - SymbolRoleSet Roles, - SourceLocation Loc) { - return true; -} - -namespace { - -class IndexASTConsumer : public ASTConsumer { - std::shared_ptr<Preprocessor> PP; - std::shared_ptr<IndexingContext> IndexCtx; - -public: - IndexASTConsumer(std::shared_ptr<Preprocessor> PP, - std::shared_ptr<IndexingContext> IndexCtx) - : PP(std::move(PP)), IndexCtx(std::move(IndexCtx)) {} - -protected: - void Initialize(ASTContext &Context) override { - IndexCtx->setASTContext(Context); - IndexCtx->getDataConsumer().initialize(Context); - IndexCtx->getDataConsumer().setPreprocessor(PP); - } - - bool HandleTopLevelDecl(DeclGroupRef DG) override { - return IndexCtx->indexDeclGroupRef(DG); - } - - void HandleInterestingDecl(DeclGroupRef DG) override { - // Ignore deserialized decls. - } - - void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { - IndexCtx->indexDeclGroupRef(DG); - } - - void HandleTranslationUnit(ASTContext &Ctx) override { - } -}; - -class IndexPPCallbacks : public PPCallbacks { - std::shared_ptr<IndexingContext> IndexCtx; - -public: - IndexPPCallbacks(std::shared_ptr<IndexingContext> IndexCtx) - : IndexCtx(std::move(IndexCtx)) {} - - void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, - SourceRange Range, const MacroArgs *Args) override { - IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), - Range.getBegin(), *MD.getMacroInfo()); - } - - void MacroDefined(const Token &MacroNameTok, - const MacroDirective *MD) override { - IndexCtx->handleMacroDefined(*MacroNameTok.getIdentifierInfo(), - MacroNameTok.getLocation(), - *MD->getMacroInfo()); - } - - void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, - const MacroDirective *Undef) override { - if (!MD.getMacroInfo()) // Ignore noop #undef. - return; - IndexCtx->handleMacroUndefined(*MacroNameTok.getIdentifierInfo(), - MacroNameTok.getLocation(), - *MD.getMacroInfo()); - } -}; - -class IndexActionBase { -protected: - std::shared_ptr<IndexDataConsumer> DataConsumer; - std::shared_ptr<IndexingContext> IndexCtx; - - IndexActionBase(std::shared_ptr<IndexDataConsumer> dataConsumer, - IndexingOptions Opts) - : DataConsumer(std::move(dataConsumer)), - IndexCtx(new IndexingContext(Opts, *DataConsumer)) {} - - std::unique_ptr<IndexASTConsumer> - createIndexASTConsumer(CompilerInstance &CI) { - return llvm::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), - IndexCtx); - } - - std::unique_ptr<PPCallbacks> createIndexPPCallbacks() { - return llvm::make_unique<IndexPPCallbacks>(IndexCtx); - } - - void finish() { - DataConsumer->finish(); - } -}; - -class IndexAction : public ASTFrontendAction, IndexActionBase { -public: - IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts) - : IndexActionBase(std::move(DataConsumer), Opts) {} - -protected: - std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) override { - return createIndexASTConsumer(CI); - } - - bool BeginSourceFileAction(clang::CompilerInstance &CI) override { - CI.getPreprocessor().addPPCallbacks(createIndexPPCallbacks()); - return true; - } - - void EndSourceFileAction() override { - FrontendAction::EndSourceFileAction(); - finish(); - } -}; - -class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { - bool IndexActionFailed = false; - -public: - WrappingIndexAction(std::unique_ptr<FrontendAction> WrappedAction, - std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts) - : WrapperFrontendAction(std::move(WrappedAction)), - IndexActionBase(std::move(DataConsumer), Opts) {} - -protected: - std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) override { - auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); - if (!OtherConsumer) { - IndexActionFailed = true; - return nullptr; - } - - std::vector<std::unique_ptr<ASTConsumer>> Consumers; - Consumers.push_back(std::move(OtherConsumer)); - Consumers.push_back(createIndexASTConsumer(CI)); - return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); - } - - bool BeginSourceFileAction(clang::CompilerInstance &CI) override { - WrapperFrontendAction::BeginSourceFileAction(CI); - CI.getPreprocessor().addPPCallbacks(createIndexPPCallbacks()); - return true; - } - - void EndSourceFileAction() override { - // Invoke wrapped action's method. - WrapperFrontendAction::EndSourceFileAction(); - if (!IndexActionFailed) - finish(); - } -}; - -} // anonymous namespace - -std::unique_ptr<FrontendAction> -index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts, - std::unique_ptr<FrontendAction> WrappedAction) { - if (WrappedAction) - return llvm::make_unique<WrappingIndexAction>(std::move(WrappedAction), - std::move(DataConsumer), - Opts); - return llvm::make_unique<IndexAction>(std::move(DataConsumer), Opts); -} - -static bool topLevelDeclVisitor(void *context, const Decl *D) { - IndexingContext &IndexCtx = *static_cast<IndexingContext*>(context); - return IndexCtx.indexTopLevelDecl(D); -} - -static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IndexCtx) { - Unit.visitLocalTopLevelDecls(&IndexCtx, topLevelDeclVisitor); -} - -static void indexPreprocessorMacros(const Preprocessor &PP, - IndexDataConsumer &DataConsumer) { - for (const auto &M : PP.macros()) - if (MacroDirective *MD = M.second.getLatest()) - DataConsumer.handleMacroOccurence( - M.first, MD->getMacroInfo(), - static_cast<unsigned>(index::SymbolRole::Definition), - MD->getLocation()); -} - -void index::indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, - IndexingOptions Opts) { - IndexingContext IndexCtx(Opts, DataConsumer); - IndexCtx.setASTContext(Unit.getASTContext()); - DataConsumer.initialize(Unit.getASTContext()); - DataConsumer.setPreprocessor(Unit.getPreprocessorPtr()); - - if (Opts.IndexMacrosInPreprocessor) - indexPreprocessorMacros(Unit.getPreprocessor(), DataConsumer); - indexTranslationUnit(Unit, IndexCtx); - DataConsumer.finish(); -} - -void index::indexTopLevelDecls(ASTContext &Ctx, Preprocessor &PP, - ArrayRef<const Decl *> Decls, - IndexDataConsumer &DataConsumer, - IndexingOptions Opts) { - IndexingContext IndexCtx(Opts, DataConsumer); - IndexCtx.setASTContext(Ctx); - - DataConsumer.initialize(Ctx); - - if (Opts.IndexMacrosInPreprocessor) - indexPreprocessorMacros(PP, DataConsumer); - - for (const Decl *D : Decls) - IndexCtx.indexTopLevelDecl(D); - DataConsumer.finish(); -} - -std::unique_ptr<PPCallbacks> -index::indexMacrosCallback(IndexDataConsumer &Consumer, IndexingOptions Opts) { - return llvm::make_unique<IndexPPCallbacks>( - std::make_shared<IndexingContext>(Opts, Consumer)); -} - -void index::indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, - IndexDataConsumer &DataConsumer, - IndexingOptions Opts) { - ASTContext &Ctx = Reader.getContext(); - IndexingContext IndexCtx(Opts, DataConsumer); - IndexCtx.setASTContext(Ctx); - DataConsumer.initialize(Ctx); - - if (Opts.IndexMacrosInPreprocessor) - indexPreprocessorMacros(Reader.getPreprocessor(), DataConsumer); - - for (const Decl *D : Reader.getModuleFileLevelDecls(Mod)) { - IndexCtx.indexTopLevelDecl(D); - } - DataConsumer.finish(); -} Index: clang/lib/Index/IndexingContext.cpp =================================================================== --- clang/lib/Index/IndexingContext.cpp +++ clang/lib/Index/IndexingContext.cpp @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexingContext.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Index/IndexDataConsumer.h" using namespace clang; using namespace index; @@ -119,12 +119,7 @@ if (FID.isInvalid()) return true; - bool Invalid = false; - const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); - if (Invalid || !SEntry.isFile()) - return true; - - if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + if (isSystemFile(FID)) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; @@ -193,6 +188,57 @@ return true; } +void IndexingContext::setSysrootPath(StringRef path) { + // Ignore sysroot path if it points to root, otherwise every header will be + // treated as system one. + // FIXMIE: make this portable + if (path == "/") + path = StringRef(); + SysrootPath = path; +} + +bool IndexingContext::isSystemFile(FileID FID) { + if (LastFileCheck.first == FID) + return LastFileCheck.second; + + auto cacheResult = [&](bool res) -> bool { + LastFileCheck = {FID, res}; + return res; + }; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = + Ctx->getSourceManager().getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return cacheResult(false); + + const SrcMgr::FileInfo &FI = SEntry.getFile(); + if (FI.getFileCharacteristic() != SrcMgr::C_User) + return cacheResult(true); + + auto *CC = FI.getContentCache(); + if (!CC) + return cacheResult(false); + auto *FE = CC->OrigEntry; + if (!FE) + return cacheResult(false); + + if (SysrootPath.empty()) + return cacheResult(false); + + // Check if directory is in sysroot so that we can consider system headers + // even the headers found via a user framework search path, pointing inside + // sysroot. + auto dirEntry = FE->getDir(); + auto pair = DirEntries.insert(std::make_pair(dirEntry, false)); + bool &isSystemDir = pair.first->second; + bool wasInserted = pair.second; + if (wasInserted) { + isSystemDir = StringRef(dirEntry->getName()).startswith(SysrootPath); + } + return cacheResult(isSystemDir); +} + static const CXXRecordDecl * getDeclContextForTemplateInstationPattern(const Decl *D) { if (const auto *CTSD = @@ -355,7 +401,7 @@ const Expr *OrigE, const Decl *OrigD, const DeclContext *ContainerDC) { - if (D->isImplicit() && !isa<ObjCMethodDecl>(D)) + if (D->isImplicit() && !(isa<ObjCMethodDecl>(D) || isa<ObjCIvarDecl>(D))) return true; if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D))) return true; @@ -365,12 +411,7 @@ if (FID.isInvalid()) return true; - bool Invalid = false; - const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); - if (Invalid || !SEntry.isFile()) - return true; - - if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + if (isSystemFile(FID)) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; Index: clang/lib/IndexDataStore/CMakeLists.txt =================================================================== --- /dev/null +++ clang/lib/IndexDataStore/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_library(clangIndexDataStore + IndexDataStore.cpp + + LINK_LIBS + clangDirectoryWatcher + clangIndexWhileBuilding + ) Index: clang/lib/IndexDataStore/IndexDataStore.cpp =================================================================== --- /dev/null +++ clang/lib/IndexDataStore/IndexDataStore.cpp @@ -0,0 +1,226 @@ +//===--- IndexDataStore.cpp - Index data store info -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/IndexDataStore/IndexDataStore.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +namespace { + +class UnitEventHandlerData { + mutable sys::Mutex Mtx; + IndexDataStore::UnitEventHandler Handler; + +public: + void setHandler(IndexDataStore::UnitEventHandler handler) { + sys::ScopedLock L(Mtx); + Handler = std::move(handler); + } + IndexDataStore::UnitEventHandler getHandler() const { + sys::ScopedLock L(Mtx); + return Handler; + } +}; + +class IndexDataStoreImpl { + std::string FilePath; + std::shared_ptr<UnitEventHandlerData> TheUnitEventHandlerData; + std::unique_ptr<DirectoryWatcher> DirWatcher; + +public: + explicit IndexDataStoreImpl(StringRef indexStorePath) + : FilePath(indexStorePath) { + TheUnitEventHandlerData = std::make_shared<UnitEventHandlerData>(); + } + + StringRef getFilePath() const { return FilePath; } + bool foreachUnitName(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver); + void setUnitEventHandler(IndexDataStore::UnitEventHandler Handler); + bool startEventListening(bool waitInitialSync, std::string &Error); + void stopEventListening(); + void discardUnit(StringRef UnitName); + void discardRecord(StringRef RecordName); + void purgeStaleData(); +}; + +} // anonymous namespace + +bool IndexDataStoreImpl::foreachUnitName( + bool sorted, llvm::function_ref<bool(StringRef unitName)> receiver) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + std::vector<std::string> filenames; + + std::error_code EC; + for (auto It = sys::fs::directory_iterator(UnitPath, EC), + End = sys::fs::directory_iterator(); + !EC && It != End; It.increment(EC)) { + StringRef unitName = sys::path::filename(It->path()); + if (!sorted) { + if (!receiver(unitName)) + return false; + } else { + filenames.push_back(unitName); + } + } + + if (sorted) { + std::sort(filenames.begin(), filenames.end()); + for (auto &fname : filenames) + if (!receiver(fname)) + return false; + } + return true; +} + +void IndexDataStoreImpl::setUnitEventHandler( + IndexDataStore::UnitEventHandler handler) { + TheUnitEventHandlerData->setHandler(std::move(handler)); +} + +bool IndexDataStoreImpl::startEventListening(bool waitInitialSync, + std::string &Error) { + if (DirWatcher) { + Error = "event listener already active"; + return true; + } + + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + auto localUnitEventHandlerData = TheUnitEventHandlerData; + auto OnUnitsChange = [localUnitEventHandlerData]( + ArrayRef<DirectoryWatcher::Event> Events, + bool isInitial) { + SmallVector<IndexDataStore::UnitEvent, 16> UnitEvents; + UnitEvents.reserve(Events.size()); + for (const DirectoryWatcher::Event &evt : Events) { + IndexDataStore::UnitEventKind K; + StringRef UnitName = sys::path::filename(evt.Filename); + switch (evt.Kind) { + case DirectoryWatcher::Event::EventKind::Removed: + K = IndexDataStore::UnitEventKind::Removed; + break; + case DirectoryWatcher::Event::EventKind::Modified: + K = IndexDataStore::UnitEventKind::Modified; + break; + case DirectoryWatcher::Event::EventKind::WatchedDirRemoved: + K = IndexDataStore::UnitEventKind::DirectoryDeleted; + UnitName = StringRef(); + break; + case DirectoryWatcher::Event::EventKind::WatcherGotInvalidated: + K = IndexDataStore::UnitEventKind::Failure; + UnitName = StringRef(); + break; + } + UnitEvents.push_back(IndexDataStore::UnitEvent{K, UnitName}); + } + + if (auto handler = localUnitEventHandlerData->getHandler()) { + IndexDataStore::UnitEventNotification EventNote{isInitial, UnitEvents}; + handler(EventNote); + } + }; + + DirWatcher = + DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, waitInitialSync); + if (!DirWatcher) { + Error = "failed to create directory watcher"; + return true; + } + + return false; +} + +void IndexDataStoreImpl::stopEventListening() { DirWatcher.reset(); } + +void IndexDataStoreImpl::discardUnit(StringRef UnitName) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + appendUnitBucketDir(UnitName, UnitPath); + sys::fs::remove(UnitPath); +} + +void IndexDataStoreImpl::discardRecord(StringRef RecordName) { + // TODO check - is this correct or is the order of appended elements opposite? + SmallString<128> RecordPath; + RecordPath = FilePath; + appendRecordSubDir(RecordPath); + appendRecordBucketDir(RecordName, RecordPath); + appendRecordFilename(RecordName, RecordPath); + sys::fs::remove(RecordPath); +} + +void IndexDataStoreImpl::purgeStaleData() { + // FIXME: Implement. +} + +std::unique_ptr<IndexDataStore> IndexDataStore::create(StringRef IndexStorePath, + std::string &Error) { + if (!sys::fs::exists(IndexStorePath)) { + raw_string_ostream OS(Error); + OS << "index store path does not exist: " << IndexStorePath; + return nullptr; + } + + return std::unique_ptr<IndexDataStore>( + new IndexDataStore(new IndexDataStoreImpl(IndexStorePath))); +} + +#define IMPL static_cast<IndexDataStoreImpl *>(Impl) + +IndexDataStore::~IndexDataStore() { delete IMPL; } + +StringRef IndexDataStore::getFilePath() const { return IMPL->getFilePath(); } + +bool IndexDataStore::foreachUnitName( + bool sorted, llvm::function_ref<bool(StringRef unitName)> receiver) { + return IMPL->foreachUnitName(sorted, std::move(receiver)); +} + +unsigned IndexDataStore::getFormatVersion() { + return clang::index::store::getIndexFormatVersion(); +} + +void IndexDataStore::setUnitEventHandler(UnitEventHandler Handler) { + return IMPL->setUnitEventHandler(std::move(Handler)); +} + +bool IndexDataStore::startEventListening(bool waitInitialSync, + std::string &Error) { + return IMPL->startEventListening(waitInitialSync, Error); +} + +void IndexDataStore::stopEventListening() { return IMPL->stopEventListening(); } + +void IndexDataStore::discardUnit(StringRef UnitName) { + IMPL->discardUnit(UnitName); +} + +void IndexDataStore::discardRecord(StringRef RecordName) { + IMPL->discardRecord(RecordName); +} + +void IndexDataStore::purgeStaleData() { IMPL->purgeStaleData(); } Index: clang/test/Index/Store/Inputs/head.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/head.h @@ -0,0 +1,3 @@ + +extern void test1_func(void); +extern void test2_func(void); Index: clang/test/Index/Store/Inputs/json.c.json =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/json.c.json @@ -0,0 +1,151 @@ +{ + "files": [ + "/test1.o", + "/Inputs/test1.c", + "/Inputs/head.h", + "/test2.o", + "/Inputs/test2.c", + "/test3.o", + "/Inputs/test3.cpp" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test1_func", + "name": "test1_func", + "codegen": "_test1_func", + "roles": "Decl,Def" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test2_func", + "name": "test2_func", + "codegen": "_test2_func", + "roles": "Decl,Def" + }, + { + "kind": "class", + "lang": "C++", + "usr": "c:@S@Base", + "name": "Base", + "roles": "Def,Ref,RelBase,RelCont" + }, + { + "kind": "class", + "lang": "C++", + "usr": "c:@S@Sub", + "name": "Sub", + "roles": "Def", + "rel-roles": "RelBase,RelCont" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 3, + "col": 6, + "roles": "Def" + } + ] + }, + { + "occurrences": [ + { + "symbol": 0, + "line": 2, + "col": 13, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 13, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 1, + "line": 3, + "col": 6, + "roles": "Def" + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 1, + "col": 7, + "roles": "Def" + }, + { + "symbol": 3, + "line": 2, + "col": 7, + "roles": "Def" + }, + { + "symbol": 2, + "line": 2, + "col": 20, + "roles": "Ref,RelBase,RelCont", + "relations": [ + { + "symbol": 3, + "rel-roles": "RelBase,RelCont" + } + ] + + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "sources": [ + { + "file": 1, + "records": [0] + }, + { + "file": 2, + "records": [1] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 3, + "sources": [ + { + "file": 4, + "records": [2] + }, + { + "file": 2, + "records": [1] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 5, + "sources": [ + { + "file": 6, + "records": [3] + } + ] + } + ] +} Index: clang/test/Index/Store/Inputs/module/ModDep.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/module/ModDep.h @@ -0,0 +1,3 @@ +#include "ModTop.h" + +void ModDep_func(ModTopStruct s); Index: clang/test/Index/Store/Inputs/module/ModSystem.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/module/ModSystem.h @@ -0,0 +1,4 @@ + +typedef struct {} ModSystemStruct; + +void ModSystem_func(void); Index: clang/test/Index/Store/Inputs/module/ModTop.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/module/ModTop.h @@ -0,0 +1,4 @@ + +typedef struct {} ModTopStruct; + +void ModTop_func(void); Index: clang/test/Index/Store/Inputs/module/ModTopSub1.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/module/ModTopSub1.h @@ -0,0 +1 @@ +void ModTopSub1_func(void); Index: clang/test/Index/Store/Inputs/module/ModTopSub2.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/module/ModTopSub2.h @@ -0,0 +1 @@ +// This header has no symbols, intended to show up as file dependency. Index: clang/test/Index/Store/Inputs/module/module.modulemap =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/module/module.modulemap @@ -0,0 +1,12 @@ +module ModTop { + header "ModTop.h" + export * + module Sub1 { + header "ModTopSub1.h" + } + module Sub2 { + header "ModTopSub2.h" + } +} +module ModDep { header "ModDep.h" export * } +module ModSystem [system] { header "ModSystem.h" export * } Index: clang/test/Index/Store/Inputs/overlay.yaml =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/overlay.yaml @@ -0,0 +1,6 @@ +{ + 'version': 0, + 'roots': [{ 'type': 'file', 'name': 'OUT_DIR/using-overlay.h', + 'external-contents': 'INPUT_DIR/using-overlay.h' + }] +} Index: clang/test/Index/Store/Inputs/print-unit.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/print-unit.h @@ -0,0 +1,2 @@ +#include "head.h" +#include "using-overlay.h" Index: clang/test/Index/Store/Inputs/sys/another.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/sys/another.h @@ -0,0 +1,2 @@ + +extern void sys_another_func(void); Index: clang/test/Index/Store/Inputs/sys/syshead.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/sys/syshead.h @@ -0,0 +1,4 @@ + +#include "another.h" + +extern void sys_test1_func(void); Index: clang/test/Index/Store/Inputs/test1.c =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/test1.c @@ -0,0 +1,3 @@ +#include "head.h" + +void test1_func(void) {} Index: clang/test/Index/Store/Inputs/test2.c =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/test2.c @@ -0,0 +1,3 @@ +#include "head.h" + +void test2_func(void) {} Index: clang/test/Index/Store/Inputs/test3.cpp =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/test3.cpp @@ -0,0 +1,2 @@ +class Base {}; +class Sub : public Base {}; Index: clang/test/Index/Store/Inputs/using-overlay.h =================================================================== --- /dev/null +++ clang/test/Index/Store/Inputs/using-overlay.h @@ -0,0 +1 @@ +void using_overlay(void); Index: clang/test/Index/Store/assembly-invocation.c =================================================================== --- /dev/null +++ clang/test/Index/Store/assembly-invocation.c @@ -0,0 +1,3 @@ +// Make sure it doesn't crash. +// RUN: %clang -target x86_64-apple-macosx10.7 -S %s -o %t.s +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -c %t.s -o %t.o Index: clang/test/Index/Store/empty-unit.c =================================================================== --- /dev/null +++ clang/test/Index/Store/empty-unit.c @@ -0,0 +1,18 @@ +void foo(int i); + +// RUN: rm -rf %t/idx +// RUN: %clang_cc1 -index-store-path %t/idx %s -o %t.o +// RUN: touch %t.empty + +// RUN: cp %t.empty $(find %t/idx -name "empty-unit.c*o*") +// RUN: not c-index-test core -print-unit %t/idx 2> %t.err +// RUN: FileCheck %s -input-file %t.err -check-prefix ERR-UNIT +// ERR-UNIT: error loading unit: empty file + +// Also check for empty record files. +// RUN: rm -rf %t/idx2 +// RUN: %clang_cc1 -index-store-path %t/idx2 %s -o %t.o +// RUN: cp %t.empty $(find %t/idx2 -name "empty-unit.c-*") +// RUN: not c-index-test core -print-record %t/idx2 2> %t2.err +// RUN: FileCheck %s -input-file %t2.err -check-prefix ERR-RECORD +// ERR-RECORD: error loading record: empty file Index: clang/test/Index/Store/external-source-symbol-hash.m =================================================================== --- /dev/null +++ clang/test/Index/Store/external-source-symbol-hash.m @@ -0,0 +1,47 @@ +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx -D USE_EXTERNAL +// RUN: c-index-test core -print-record %t.idx | FileCheck %s +// RUN: %clang_cc1 %s -index-store-path %t.idx +// RUN: find %t.idx/*/records -name "external-source-symbol-hash*" | count 2 + +#ifdef USE_EXTERNAL +# define EXT_DECL(mod_name) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name))) +#else +# define EXT_DECL(mod_name) +#endif + +#define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type + +// Forward declarations should pick up the attribute from later decls +@protocol P1; +// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref | rel: 0 +@class I2; +// CHECK: [[@LINE-1]]:8 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref | rel: 0 +enum E3: int; +// CHECK: [[@LINE-1]]:6 | enum/Swift | c:@M@third_module@E@E3 | Decl | rel: 0 + +void test(id<P1> first, I2 *second, enum E3 third) {} +// CHECK: [[@LINE-1]]:14 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref,RelCont | rel: 1 +// CHECK: [[@LINE-2]]:25 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref,RelCont | rel: 1 +// CHECK: [[@LINE-3]]:42 | enum/Swift | c:@M@third_module@E@E3 | Ref,RelCont | rel: 1 + +EXT_DECL("some_module") +@protocol P1 +// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Decl | rel: 0 +-(void)method; +// CHECK: [[@LINE-1]]:8 | instance-method(protocol)/Swift | c:@M@some_module@objc(pl)P1(im)method | Decl,Dyn,RelChild | rel: 1 +@end + +EXT_DECL("other_module") +@interface I2 +// CHECK: [[@LINE-1]]:12 | class/Swift | c:@M@other_module@objc(cs)I2 | Decl | rel: 0 +-(void)method; +// CHECK: [[@LINE-1]]:8 | instance-method/Swift | c:@M@other_module@objc(cs)I2(im)method | Decl,Dyn,RelChild | rel: 1 +@end + + +typedef NS_ENUM(E3, int) { +// CHECK: [[@LINE-1]]:17 | enum/Swift | c:@M@third_module@E@E3 | Def | rel: 0 + firstCase = 1, + // CHECK: [[@LINE-1]]:3 | enumerator/Swift | c:@M@third_module@E@E3@firstCase | Def,RelChild | rel: 1 +} EXT_DECL("third_module"); Index: clang/test/Index/Store/handle-prebuilt-module.m =================================================================== --- /dev/null +++ clang/test/Index/Store/handle-prebuilt-module.m @@ -0,0 +1,23 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx1 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err1 +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err2 +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err3 +// RUN: FileCheck -input-file=%t.err1 -check-prefix=CREATING_MODULES %s -allow-empty +// RUN: FileCheck -input-file=%t.err2 -check-prefix=CREATING_INDEX_DATA_FROM_MODULE_FILES %s +// RUN: FileCheck -input-file=%t.err3 -check-prefix=EXISTING_INDEX_DATA_FROM_MODULE_FILES %s -allow-empty +// RUN: c-index-test core -print-unit %t/idx1 | sort > %t/all-units1.txt +// RUN: c-index-test core -print-unit %t/idx2 | sort > %t/all-units2.txt +// RUN: c-index-test core -print-record %t/idx1 | sort > %t/all-records1.txt +// RUN: c-index-test core -print-record %t/idx2 | sort > %t/all-records2.txt +// RUN: diff -u %t/all-units1.txt %t/all-units2.txt +// RUN: diff -u %t/all-records1.txt %t/all-records2.txt + +@import ModDep; + +// CREATING_MODULES-NOT: remark: + +// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModDep{{.*}}.pcm +// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModTop{{.*}}.pcm + +// EXISTING_INDEX_DATA_FROM_MODULE_FILES-NOT: remark: Index: clang/test/Index/Store/json-with-module.m =================================================================== --- /dev/null +++ clang/test/Index/Store/json-with-module.m @@ -0,0 +1,7 @@ +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json +// RUN: diff -u %s.json %t.final.json + +@import ModDep; Index: clang/test/Index/Store/json-with-module.m.json =================================================================== --- /dev/null +++ clang/test/Index/Store/json-with-module.m.json @@ -0,0 +1,151 @@ +{ + "files": [ + "/json-with-module.m.tmp.mcp/ModDep.pcm", + "/json-with-module.m.tmp.mcp/ModTop.pcm", + "/Inputs/module/ModDep.h", + "/Inputs/module/ModTop.h", + "/Inputs/module/ModTopSub1.h", + "/Inputs/module/ModTopSub2.h", + "/json-with-module.m.tmp.o", + "/json-with-module.m", + "/Inputs/module/module.modulemap" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModDep_func", + "name": "ModDep_func", + "roles": "Decl", + "rel-roles": "RelCont" + }, + { + "kind": "type-alias", + "lang": "C", + "usr": "c:@T@ModTopStruct", + "name": "ModTopStruct", + "roles": "Def,Ref,RelCont" + }, + { + "kind": "struct", + "lang": "C", + "usr": "c:@SA@ModTopStruct", + "name": "", + "roles": "Def" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModTop_func", + "name": "ModTop_func", + "roles": "Decl" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModTopSub1_func", + "name": "ModTopSub1_func", + "roles": "Decl" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 3, + "col": 6, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 18, + "roles": "Ref,RelCont", + "relations": [ + { + "symbol": 0, + "rel-roles": "RelCont" + } + ] + + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 2, + "col": 9, + "roles": "Def" + }, + { + "symbol": 1, + "line": 2, + "col": 19, + "roles": "Def" + }, + { + "symbol": 3, + "line": 4, + "col": 6, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 4, + "line": 1, + "col": 6, + "roles": "Decl" + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "unit-dependencies": [1], + "sources": [ + { + "file": 2, + "records": [0] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 1, + "sources": [ + { + "file": 3, + "records": [1] + }, + { + "file": 4, + "records": [2] + }, + { + "file": 5 + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 6, + "unit-dependencies": [0], + "sources": [ + { + "file": 7 + }, + { + "file": 8 + } + ] + } + ] +} Index: clang/test/Index/Store/json-with-pch.c =================================================================== --- /dev/null +++ clang/test/Index/Store/json-with-pch.c @@ -0,0 +1,10 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json +// RUN: diff -u %s.json %t.final.json + +int main() { + test1_func(); +} Index: clang/test/Index/Store/json-with-pch.c.json =================================================================== --- /dev/null +++ clang/test/Index/Store/json-with-pch.c.json @@ -0,0 +1,96 @@ +{ + "files": [ + "/json-with-pch.c.tmp.h.pch", + "/Inputs/head.h", + "/json-with-pch.c.tmp.o", + "/json-with-pch.c" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test1_func", + "name": "test1_func", + "roles": "Decl,Ref,Call,RelCall,RelCont" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test2_func", + "name": "test2_func", + "roles": "Decl" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@main", + "name": "main", + "roles": "Def", + "rel-roles": "RelCall,RelCont" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 2, + "col": 13, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 13, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 8, + "col": 5, + "roles": "Def" + }, + { + "symbol": 0, + "line": 9, + "col": 3, + "roles": "Ref,Call,RelCall,RelCont", + "relations": [ + { + "symbol": 2, + "rel-roles": "RelCall,RelCont" + } + ] + + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "sources": [ + { + "file": 1, + "records": [0] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 2, + "unit-dependencies": [0], + "sources": [ + { + "file": 3, + "records": [1] + } + ] + } + ] +} Index: clang/test/Index/Store/json.c =================================================================== --- /dev/null +++ clang/test/Index/Store/json.c @@ -0,0 +1,8 @@ +// RUN: rm -rf %t.idx +// RUN: mkdir -p %t.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test1.c -o %t.o/test1.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test2.c -o %t.o/test2.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test3.cpp -o %t.o/test3.o +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%t.o::g" %t.json > %t.final.json +// RUN: diff -u %S/Inputs/json.c.json %t.final.json Index: clang/test/Index/Store/print-record.mm =================================================================== --- /dev/null +++ clang/test/Index/Store/print-record.mm @@ -0,0 +1,28 @@ +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx +// RUN: c-index-test core -print-record %t.idx | FileCheck %s + + + +@class MyCls; + +@interface MyCls +@end + +// CHECK: [[@LINE+2]]:6 | function/C | c:@F@foo#*$objc(cs)MyCls# | Decl | rel: 0 +// CHECK: [[@LINE+1]]:10 | class/ObjC | c:objc(cs)MyCls | Ref,RelCont | rel: 1 +void foo(MyCls *p); + + +// RANGE-NOT: before_range +void before_range(); + +// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range1# | Decl +void in_range1(); +// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range2# | Decl +void in_range2(); + +// RANGE-NOT: after_range +void after_range(); + +// RUN: c-index-test core -print-record %t.idx -filepath %s:21:23 | FileCheck -check-prefix=RANGE %s Index: clang/test/Index/Store/print-unit.c =================================================================== --- /dev/null +++ clang/test/Index/Store/print-unit.c @@ -0,0 +1,39 @@ + + +#include "print-unit.h" +#include "syshead.h" + +void foo(int i); + +// RUN: rm -rf %t +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx %s -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx | FileCheck %s +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt1 %s -triple x86_64-apple-macosx10.8 -O2 +// RUN: c-index-test core -print-unit %t/idx_opt1 | FileCheck %s -check-prefix=OPT +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt2 %s -triple x86_64-apple-macosx10.8 -Os +// RUN: c-index-test core -print-unit %t/idx_opt2 | FileCheck %s -check-prefix=OPT + +// CHECK: print-unit.c.o +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}/print-unit.c +// CHECK: out-file: {{.*}}/print-unit.c.o +// CHECK: target: x86_64-apple-macosx10.8 +// CHECK: is-debug: 1 +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}/print-unit.c | print-unit.c- +// CHECK: Record | user | {{.*}}/Inputs/head.h | head.h- +// CHECK: Record | user | {{.*}}/Inputs/using-overlay.h | using-overlay.h- +// CHECK: Record | system | {{.*}}/Inputs/sys/syshead.h | syshead.h- +// CHECK: Record | system | {{.*}}/Inputs/sys/another.h | another.h- +// CHECK: File | user | {{.*}}/Inputs/print-unit.h{{$}} +// CHECK: DEPEND END (6) +// CHECK: INCLUDE START +// CHECK-DAG: {{.*}}/print-unit.c:3 | {{.*}}/Inputs/print-unit.h +// CHECK-DAG: {{.*}}/print-unit.c:4 | {{.*}}/Inputs/sys/syshead.h +// CHECK-DAG: {{.*}}/Inputs/print-unit.h:1 | {{.*}}/Inputs/head.h +// CHECK-DAG: {{.*}}/Inputs/print-unit.h:2 | {{.*}}/Inputs/using-overlay.h +// CHECK: INCLUDE END (4) + +// OPT: is-debug: 0 Index: clang/test/Index/Store/print-units-with-modules.m =================================================================== --- /dev/null +++ clang/test/Index/Store/print-units-with-modules.m @@ -0,0 +1,57 @@ +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +@import ModDep; +@import ModSystem; + +// CHECK: ModDep.pcm +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: is-module: 1 +// CHECK: module-name: ModDep +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/ModDep.pcm +// CHECK: DEPEND START +// CHECK: Unit | user | ModTop | {{.*}}/ModTop.pcm | ModTop.pcm +// CHECK: Record | user | ModDep | {{.*}}/Inputs/module/ModDep.h | ModDep.h +// CHECK: DEPEND END (2) + +// CHECK: ModSystem.pcm +// CHECK: is-system: 1 +// CHECK: is-module: 1 +// CHECK: module-name: ModSystem +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/ModSystem.pcm +// CHECK: DEPEND START +// CHECK: Record | system | ModSystem | {{.*}}/Inputs/module/ModSystem.h | ModSystem.h +// CHECK: DEPEND END (1) + +// CHECK: ModTop.pcm +// CHECK: is-system: 0 +// CHECK: is-module: 1 +// CHECK: module-name: ModTop +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/ModTop.pcm +// CHECK: DEPEND START +// CHECK: Record | user | ModTop | {{.*}}/Inputs/module/ModTop.h | ModTop.h +// CHECK: Record | user | ModTop.Sub1 | {{.*}}/Inputs/module/ModTopSub1.h | ModTopSub1.h +// CHECK: File | user | ModTop.Sub2 | {{.*}}/Inputs/module/ModTopSub2.h{{$}} +// CHECK: DEPEND END (3) + +// CHECK: print-units-with-modules.m.tmp.o +// CHECK: is-system: 0 +// CHECK: is-module: 0 +// CHECK: module-name: <none> +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}/print-units-with-modules.m +// CHECK: out-file: {{.*}}/print-units-with-modules.m.tmp.o +// CHECK: DEPEND START +// CHECK: Unit | user | ModDep | {{.*}}/ModDep.pcm | ModDep.pcm +// CHECK: Unit | system | ModSystem | {{.*}}/ModSystem.pcm | ModSystem.pcm +// CHECK: File | user | {{.*}}/print-units-with-modules.m{{$}} +// CHECK: File | user | {{.*}}/Inputs/module/module.modulemap{{$}} +// CHECK: DEPEND END (4) Index: clang/test/Index/Store/print-units-with-pch.c =================================================================== --- /dev/null +++ clang/test/Index/Store/print-units-with-pch.c @@ -0,0 +1,27 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +int main() { + test1_func(); +} + +// CHECK: print-units-with-pch.c.tmp.h.pch +// CHECK: is-system: 0 +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/print-units-with-pch.c.tmp.h.pch +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}/Inputs/head.h | head.h +// CHECK: DEPEND END (1) + +// CHECK: print-units-with-pch.c.tmp.o +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}/print-units-with-pch.c +// CHECK: out-file: {{.*}}/print-units-with-pch.c.tmp.o +// CHECK: DEPEND START +// CHECK: Unit | user | {{.*}}/print-units-with-pch.c.tmp.h.pch | print-units-with-pch.c.tmp.h.pch +// CHECK: Record | user | {{.*}}/print-units-with-pch.c | print-units-with-pch.c +// CHECK: DEPEND END (2) Index: clang/test/Index/Store/record-hash-crash-invalid-name.cpp =================================================================== --- /dev/null +++ clang/test/Index/Store/record-hash-crash-invalid-name.cpp @@ -0,0 +1,15 @@ +// Makes sure it doesn't crash. + +// RUN: rm -rf %t +// RUN: not %clang_cc1 %s -index-store-path %t/idx -std=c++14 +// RUN: c-index-test core -print-record %t/idx | FileCheck %s + +namespace rdar32474406 { +void foo(); +typedef void (*Func_t)(); +// CHECK: [[@LINE+4]]:1 | type-alias/C | c:record-hash-crash-invalid-name.cpp@N@rdar32474406@T@Func_t | Ref,RelCont | rel: 1 +// CHECK-NEXT: RelCont | c:@N@rdar32474406 +// CHECK: [[@LINE+2]]:14 | function/C | c:@N@rdar32474406@F@foo# | Ref,RelCont | rel: 1 +// CHECK-NEXT: RelCont | c:@N@rdar32474406 +Func_t[] = { foo }; // invalid decomposition +} Index: clang/test/Index/Store/record-hash-crash.cpp =================================================================== --- /dev/null +++ clang/test/Index/Store/record-hash-crash.cpp @@ -0,0 +1,29 @@ +// Makes sure it doesn't crash. + +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -index-store-path %t/idx -std=c++14 +// RUN: c-index-test core -print-record %t/idx | FileCheck %s + +namespace crash1 { +// CHECK: [[@LINE+1]]:6 | function/C +auto getit() { return []() {}; } +} + +namespace crash2 { +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Decl,RelChild | rel: 1 +template <typename T> +class Foo; // canonical decl + +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Def,RelChild | rel: 1 +template <typename T> +class Foo {}; + +// CHECK: [[@LINE+2]]:8 | struct(Gen)/C++ | c:@N@crash2@ST>1#t>1#pT@Wrapper | Def,RelChild | rel: 1 +template <template <typename... ARGS> class TYPE> +struct Wrapper {}; + +// CHECK: [[@LINE+3]]:8 | struct(Gen,TS)/C++ | c:@N@crash2@S@Wrapper>#@N@crash2@ST>1#T@Foo | Def,RelChild,RelSpecialization | rel: 2 +// CHECK: RelSpecialization | c:@N@crash2@ST>1#t>1#pT@Wrapper +template <> +struct Wrapper<Foo> {}; +} Index: clang/test/Index/Store/record-hash-using.cpp =================================================================== --- /dev/null +++ clang/test/Index/Store/record-hash-using.cpp @@ -0,0 +1,44 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=B -DTYPE2=A -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=B -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=B -DTYPE2=B -DTYPE3=T -DTYPE4=T +// RUN: find %t/idx/*/records -name "record-hash*" | count 4 +// +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=U -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=U +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=U -DTYPE4=U +// RUN: find %t/idx/*/records -name "record-hash*" | count 4 + +template<typename T> +struct A { + typedef int X; + void foo(); +}; + +template<typename T> +struct B : public A<T> { + typedef float X; + void foo(int); +}; + +template<typename T> +struct C : public B<T> { +// This should result in different records, due to the different types. + using TYPE1<T>::X; + using TYPE2<T>::foo; +}; + +template <typename T> +struct D { + typedef T X; + void foo(T); +}; +template <typename T, typename U> +struct E : public D<T>, public D<U> { +// This should result in different records, due to the different template parameter. + using D<TYPE3>::X; + using D<TYPE4>::foo; +}; Index: clang/test/Index/Store/record-hash.cpp =================================================================== --- /dev/null +++ clang/test/Index/Store/record-hash.cpp @@ -0,0 +1,10 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=long +// RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=char +// RUN: find %t/idx/*/records -name "record-hash*" | count 2 + +template<typename T> +class TC {}; + +// This should result in different records, due to the different template parameter type. +void some_func(TC<THE_TYPE>); Index: clang/test/Index/Store/relative-out-path.c =================================================================== --- /dev/null +++ clang/test/Index/Store/relative-out-path.c @@ -0,0 +1,19 @@ +// Needs 'find'. +// REQUIRES: shell + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang %s -index-store-path %t/idx1 -c -o %t/outfile.o +// RUN: cd %t +// RUN: %clang %s -index-store-path %t/idx2 -c -o outfile.o +// RUN: cd .. +// RUN: %clang %s -index-store-path %t/idx3 -fsyntax-only -o outfile.o -working-directory=%t +// RUN: diff -r -u %t/idx2 %t/idx3 + +// RUN: find %t/idx1 -name '*outfile.o*' > %t/hashes.txt +// RUN: find %t/idx3 -name '*outfile.o*' >> %t/hashes.txt +// RUN: FileCheck %s --input-file=%t/hashes.txt +// CHECK: outfile.o[[OUT_HASH:.*$]] +// CHECK-NEXT: outfile.o[[OUT_HASH]] + +void foo(); Index: clang/test/Index/Store/syntax-only.c =================================================================== --- /dev/null +++ clang/test/Index/Store/syntax-only.c @@ -0,0 +1,9 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -fsyntax-only %s -index-store-path %t.idx -o %T/syntax-only.c.myoutfile +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s -check-prefix=CHECK-UNIT +// RUN: c-index-test core -print-record %t.idx | FileCheck %s -check-prefix=CHECK-RECORD + +// CHECK-UNIT: out-file: {{.*}}/syntax-only.c.myoutfile +// CHECK-RECORD: function/C | foo | c:@F@foo + +void foo(); Index: clang/test/Index/Store/unit-with-vfs.c =================================================================== --- /dev/null +++ clang/test/Index/Store/unit-with-vfs.c @@ -0,0 +1,10 @@ +// RUN: sed -e "s:INPUT_DIR:%S/Inputs:g" -e "s:OUT_DIR:%t:g" %S/Inputs/overlay.yaml > %t.yaml +// REQUIRES: shell + +#include "using-overlay.h" + +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx -I %t -ivfsoverlay %t.yaml +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +// CHECK: Record | user | {{.*}}test/Index/Store/Inputs/using-overlay.h Index: clang/test/Index/Store/unit-workdir-prefix.c =================================================================== --- /dev/null +++ clang/test/Index/Store/unit-workdir-prefix.c @@ -0,0 +1,28 @@ +#include "header.h" + +void foo(void) { + bar(); +} + +// RUN: rm -rf %t +// RUN: mkdir -p %t/Directory +// RUN: mkdir -p %t/Directory.surprise +// RUN: mkdir -p %t/sdk +// RUN: mkdir -p %t/sdk_other +// RUN: echo "void bar(void);" > %t/sdk_other/header.h +// RUN: cp %s %t/Directory.surprise/main.c +// +// RUN: %clang_cc1 -isystem %t/sdk_other -isysroot %t/sdk -index-store-path %t/idx %t/Directory.surprise/main.c -triple x86_64-apple-macosx10.8 -working-directory %t/Directory +// RUN: c-index-test core -print-unit %t/idx | FileCheck %s + +// CHECK: main.c.o +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}Directory.surprise{{/|\\}}main.c +// CHECK: out-file: {{.*}}Directory.surprise{{/|\\}}main.c.o +// CHECK: target: x86_64-apple-macosx10.8 +// CHECK: is-debug: 1 +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}Directory.surprise{{/|\\}}main.c | main.c- +// CHECK: Record | system | {{.*}}sdk_other{{/|\\}}header.h | header.h- Index: clang/test/Index/Store/using-libstdcpp-arc.mm =================================================================== --- /dev/null +++ clang/test/Index/Store/using-libstdcpp-arc.mm @@ -0,0 +1,18 @@ +// Test to make sure we don't crash, rdar://30816887&36162712. + +// RUN: mkdir -p %t/include +// RUN: echo 'module Foo { header "test.h" }' > %t/include/module.modulemap +// RUN: echo '' > %t/include/test.h + +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang_cc1 %s -index-store-path %t.idx -I %t/include -fobjc-arc -fobjc-arc-cxxlib=libstdc++ -fmodules -fmodules-cache-path=%t.mcp -fimplicit-module-maps +// RUN: c-index-test core -print-record %t.idx | FileCheck %s + +// RUN: rm -rf %t.idx2 +// RUN: %clang_cc1 %s -index-store-path %t.idx2 -I %t/include -fobjc-arc -fobjc-arc-cxxlib=libstdc++ -fmodules -fmodules-cache-path=%t.mcp -fimplicit-module-maps +// RUN: c-index-test core -print-record %t.idx2 | FileCheck %s + +@import Foo; + +// CHECK: [[@LINE+1]]:6 | function/C +void test1(void); Index: clang/tools/CMakeLists.txt =================================================================== --- clang/tools/CMakeLists.txt +++ clang/tools/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_subdirectory(clang-scan-deps) add_clang_subdirectory(c-index-test) +add_clang_subdirectory(IndexStore) add_clang_subdirectory(clang-rename) add_clang_subdirectory(clang-refactor) Index: clang/tools/IndexStore/CMakeLists.txt =================================================================== --- /dev/null +++ clang/tools/IndexStore/CMakeLists.txt @@ -0,0 +1,77 @@ +include(CheckIncludeFiles) + +set(SOURCES + IndexStore.cpp + + ADDITIONAL_HEADERS + ../../include/indexstore/indexstore.h + ../../include/indexstore/IndexStoreCXX.h + ) + +set(LIBS + clangIndex + clangIndexWhileBuilding + clangIndexDataStore +) + +if(NOT CMAKE_SYSTEM_NAME STREQUAL Windows) + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/IndexStore.exports) +endif() + +if(WIN32) + set(output_name "libIndexStore") +else() + set(output_name "IndexStore") +endif() + +add_clang_library(IndexStore SHARED + OUTPUT_NAME ${output_name} + ${SOURCES} + + LINK_LIBS + ${LIBS} + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Core + Support + ) + +if(APPLE) + set(INDEXSTORE_LIBRARY_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + + set(INDEXSTORE_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -Wl,-current_version -Wl,${INDEXSTORE_LIBRARY_VERSION}") + + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -framework CoreServices") + endif() + + set_property(TARGET IndexStore APPEND_STRING PROPERTY + LINK_FLAGS ${INDEXSTORE_LINK_FLAGS}) +endif() + +if (LLVM_INSTALL_TOOLCHAIN_ONLY) + install(TARGETS IndexStore + COMPONENT IndexStore + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} + RUNTIME DESTINATION bin) + + if (NOT CMAKE_CONFIGURATION_TYPES) + add_llvm_install_targets(install-IndexStore + DEPENDS IndexStore + COMPONENT IndexStore) + endif() +endif() + +set(INDEXSTORE_HEADERS_INSTALL_DESTINATION "local/include") + +install(DIRECTORY ../../include/indexstore + COMPONENT IndexStore + DESTINATION "${INDEXSTORE_HEADERS_INSTALL_DESTINATION}" + FILES_MATCHING + PATTERN "*.h" + PATTERN ".svn" EXCLUDE + ) Index: clang/tools/IndexStore/IndexStore.cpp =================================================================== --- /dev/null +++ clang/tools/IndexStore/IndexStore.cpp @@ -0,0 +1,811 @@ +//===- IndexStore.cpp - Index store API -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the API for the index store. +// +//===----------------------------------------------------------------------===// + +#include "indexstore/indexstore.h" +#include "indexstore/Conversions.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "clang/Index/IndexWhileBuilding/IndexDataFormat.h" +#include "clang/Index/IndexWhileBuilding/IndexRecordReader.h" +#include "clang/Index/IndexWhileBuilding/IndexUnitReader.h" +#include "clang/Index/IndexWhileBuilding/IndexUnitWriter.h" +#include "clang/IndexDataStore/IndexDataStore.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/ManagedStatic.h" + +#if INDEXSTORE_HAS_BLOCKS +#include <Block.h> +#endif + +using namespace clang; +using namespace clang::index; +using namespace llvm; + +static indexstore_string_ref_t toIndexStoreString(StringRef str) { + return indexstore_string_ref_t{str.data(), str.size()}; +} + +static timespec toTimeSpec(sys::TimePoint<> tp) { + std::chrono::seconds sec = + std::chrono::time_point_cast<std::chrono::seconds>(tp).time_since_epoch(); + std::chrono::nanoseconds nsec = + std::chrono::time_point_cast<std::chrono::nanoseconds>(tp - sec) + .time_since_epoch(); + timespec ts; + ts.tv_sec = sec.count(); + ts.tv_nsec = nsec.count(); + return ts; +} + +//===----------------------------------------------------------------------===// +// Fatal error handling +//===----------------------------------------------------------------------===// + +static void fatal_error_handler(void *user_data, const std::string &reason, + bool gen_crash_diag) { + // Write the result out to stderr avoiding errs() because raw_ostreams can + // call report_fatal_error. + fprintf(stderr, "INDEXSTORE FATAL ERROR: %s\n", reason.c_str()); + ::abort(); +} + +namespace { +struct RegisterFatalErrorHandler { + RegisterFatalErrorHandler() { + llvm::install_fatal_error_handler(fatal_error_handler, nullptr); + } +}; +} // namespace + +static llvm::ManagedStatic<RegisterFatalErrorHandler> + RegisterFatalErrorHandlerOnce; + +//===----------------------------------------------------------------------===// +// C API +//===----------------------------------------------------------------------===// + +namespace { + +struct IndexStoreError { + std::string Error; +}; + +} // anonymous namespace + +const char *indexstore_error_get_description(indexstore_error_t err) { + return static_cast<IndexStoreError *>(err)->Error.c_str(); +} + +void indexstore_error_dispose(indexstore_error_t err) { + delete static_cast<IndexStoreError *>(err); +} + +unsigned indexstore_format_version(void) { + return IndexDataStore::getFormatVersion(); +} + +indexstore_t indexstore_store_create(const char *store_path, + indexstore_error_t *c_error) { + // Look through the managed static to trigger construction of the managed + // static which registers our fatal error handler. This ensures it is only + // registered once. + (void)*RegisterFatalErrorHandlerOnce; + + std::unique_ptr<IndexDataStore> store; + std::string error; + store = IndexDataStore::create(store_path, error); + if (!store) { + if (c_error) + *c_error = new IndexStoreError{error}; + return nullptr; + } + return store.release(); +} + +void indexstore_store_dispose(indexstore_t store) { + delete static_cast<IndexDataStore *>(store); +} + +#if INDEXSTORE_HAS_BLOCKS +bool indexstore_store_units_apply( + indexstore_t c_store, unsigned sorted, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_string_ref_t unit_name)) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool { + return applier(toIndexStoreString(unitName)); + }); +} +#endif + +bool indexstore_store_units_apply_f( + indexstore_t c_store, unsigned sorted, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_string_ref_t unit_name)) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool { + return applier(context, toIndexStoreString(unitName)); + }); +} + +size_t indexstore_unit_event_notification_get_events_count( + indexstore_unit_event_notification_t c_evtnote) { + auto *evtnote = + static_cast<IndexDataStore::UnitEventNotification *>(c_evtnote); + return evtnote->Events.size(); +} + +indexstore_unit_event_t indexstore_unit_event_notification_get_event( + indexstore_unit_event_notification_t c_evtnote, size_t index) { + auto *evtnote = + static_cast<IndexDataStore::UnitEventNotification *>(c_evtnote); + return (indexstore_unit_event_t)&evtnote->Events[index]; +} + +bool indexstore_unit_event_notification_is_initial( + indexstore_unit_event_notification_t c_evtnote) { + auto *evtnote = + static_cast<IndexDataStore::UnitEventNotification *>(c_evtnote); + return evtnote->IsInitial; +} + +indexstore_unit_event_kind_t +indexstore_unit_event_get_kind(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent *>(c_evt); + indexstore_unit_event_kind_t k; + switch (evt->Kind) { + case IndexDataStore::UnitEventKind::Removed: + k = INDEXSTORE_UNIT_EVENT_REMOVED; + break; + case IndexDataStore::UnitEventKind::Modified: + k = INDEXSTORE_UNIT_EVENT_MODIFIED; + break; + case IndexDataStore::UnitEventKind::DirectoryDeleted: + k = INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED; + break; + case IndexDataStore::UnitEventKind::Failure: + k = INDEXSTORE_UNIT_EVENT_FAILURE; + break; + } + return k; +} + +indexstore_string_ref_t +indexstore_unit_event_get_unit_name(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent *>(c_evt); + return toIndexStoreString(evt->UnitName); +} + +#if INDEXSTORE_HAS_BLOCKS +void indexstore_store_set_unit_event_handler( + indexstore_t c_store, indexstore_unit_event_handler_t blk_handler) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + if (!blk_handler) { + store->setUnitEventHandler(nullptr); + return; + } + + class BlockWrapper { + indexstore_unit_event_handler_t blk_handler; + + public: + BlockWrapper(indexstore_unit_event_handler_t handler) { + blk_handler = Block_copy(handler); + } + BlockWrapper(const BlockWrapper &other) { + blk_handler = Block_copy(other.blk_handler); + } + ~BlockWrapper() { Block_release(blk_handler); } + + void operator()(indexstore_unit_event_notification_t evt_note) const { + blk_handler(evt_note); + } + }; + + BlockWrapper handler(blk_handler); + + store->setUnitEventHandler( + [handler](IndexDataStore::UnitEventNotification evtNote) { + handler(&evtNote); + }); +} +#endif + +void indexstore_store_set_unit_event_handler_f( + indexstore_t c_store, void *context, + void (*fn_handler)(void *context, indexstore_unit_event_notification_t), + void (*finalizer)(void *context)) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + if (!fn_handler) { + store->setUnitEventHandler(nullptr); + return; + } + + class BlockWrapper { + void *context; + void (*fn_handler)(void *context, indexstore_unit_event_notification_t); + void (*finalizer)(void *context); + + public: + BlockWrapper(void *_context, + void (*_fn_handler)(void *context, + indexstore_unit_event_notification_t), + void (*_finalizer)(void *context)) + : context(_context), fn_handler(_fn_handler), finalizer(_finalizer) {} + + ~BlockWrapper() { + if (finalizer) { + finalizer(context); + } + } + + void operator()(indexstore_unit_event_notification_t evt_note) const { + fn_handler(context, evt_note); + } + }; + + auto handler = std::make_shared<BlockWrapper>(context, fn_handler, finalizer); + + store->setUnitEventHandler( + [handler](IndexDataStore::UnitEventNotification evtNote) { + (*handler)(&evtNote); + }); +} + +bool indexstore_store_start_unit_event_listening( + indexstore_t c_store, indexstore_unit_event_listen_options_t *client_opts, + size_t listen_options_struct_size, indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + indexstore_unit_event_listen_options_t listen_opts; + memset(&listen_opts, 0, sizeof(listen_opts)); + unsigned clientOptSize = listen_options_struct_size < sizeof(listen_opts) + ? listen_options_struct_size + : sizeof(listen_opts); + memcpy(&listen_opts, client_opts, clientOptSize); + + std::string error; + bool err = store->startEventListening(listen_opts.wait_initial_sync, error); + if (err && c_error) + *c_error = new IndexStoreError{error}; + return err; +} + +void indexstore_store_stop_unit_event_listening(indexstore_t c_store) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->stopEventListening(); +} + +void indexstore_store_discard_unit(indexstore_t c_store, + const char *unit_name) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->discardUnit(unit_name); +} + +void indexstore_store_discard_record(indexstore_t c_store, + const char *record_name) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->discardRecord(record_name); +} + +void indexstore_store_purge_stale_data(indexstore_t c_store) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + store->purgeStaleData(); +} + +indexstore_symbol_kind_t indexstore_symbol_get_kind(indexstore_symbol_t sym) { + return getIndexStoreKind(static_cast<IndexRecordDecl *>(sym)->SymInfo.Kind); +} + +indexstore_symbol_subkind_t +indexstore_symbol_get_subkind(indexstore_symbol_t sym) { + return getIndexStoreSubKind( + static_cast<IndexRecordDecl *>(sym)->SymInfo.SubKind); +} + +indexstore_symbol_language_t +indexstore_symbol_get_language(indexstore_symbol_t sym) { + return getIndexStoreLang(static_cast<IndexRecordDecl *>(sym)->SymInfo.Lang); +} + +uint64_t indexstore_symbol_get_properties(indexstore_symbol_t sym) { + return getIndexStoreProperties( + static_cast<IndexRecordDecl *>(sym)->SymInfo.Properties); +} + +uint64_t indexstore_symbol_get_roles(indexstore_symbol_t sym) { + return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->Roles); +} + +uint64_t indexstore_symbol_get_related_roles(indexstore_symbol_t sym) { + return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->RelatedRoles); +} + +indexstore_string_ref_t indexstore_symbol_get_name(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl *>(sym); + return toIndexStoreString(D->Name); +} + +indexstore_string_ref_t indexstore_symbol_get_usr(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl *>(sym); + return toIndexStoreString(D->USR); +} + +indexstore_string_ref_t +indexstore_symbol_get_codegen_name(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl *>(sym); + return toIndexStoreString(D->CodeGenName); +} + +uint64_t +indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t sym_rel) { + return getIndexStoreRoles(static_cast<IndexRecordRelation *>(sym_rel)->Roles); +} + +indexstore_symbol_t +indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t sym_rel) { + return (indexstore_symbol_t) static_cast<IndexRecordRelation *>(sym_rel)->Dcl; +} + +indexstore_symbol_t +indexstore_occurrence_get_symbol(indexstore_occurrence_t occur) { + return (indexstore_symbol_t) static_cast<IndexRecordOccurrence *>(occur)->Dcl; +} + +#if INDEXSTORE_HAS_BLOCKS +bool indexstore_occurrence_relations_apply( + indexstore_occurrence_t occur, + INDEXSTORE_NOESCAPE bool (^applier)( + indexstore_symbol_relation_t symbol_rel)) { + auto *recOccur = static_cast<IndexRecordOccurrence *>(occur); + for (auto &rel : recOccur->Relations) { + if (!applier(&rel)) + return false; + } + return true; +} +#endif + +bool indexstore_occurrence_relations_apply_f( + indexstore_occurrence_t occur, void *context, + INDEXSTORE_NOESCAPE bool (*applier)( + void *context, indexstore_symbol_relation_t symbol_rel)) { + auto *recOccur = static_cast<IndexRecordOccurrence *>(occur); + for (auto &rel : recOccur->Relations) { + if (!applier(context, &rel)) + return false; + } + return true; +} + +uint64_t indexstore_occurrence_get_roles(indexstore_occurrence_t occur) { + return getIndexStoreRoles(static_cast<IndexRecordOccurrence *>(occur)->Roles); +} + +void indexstore_occurrence_get_line_col(indexstore_occurrence_t occur, + unsigned *line, unsigned *column) { + auto *recOccur = static_cast<IndexRecordOccurrence *>(occur); + if (line) + *line = recOccur->Line; + if (column) + *column = recOccur->Column; +} + +typedef void *indexstore_record_reader_t; + +indexstore_record_reader_t +indexstore_record_reader_create(indexstore_t c_store, const char *record_name, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + std::unique_ptr<IndexRecordReader> reader; + std::string error; + reader = IndexRecordReader::createWithRecordFilename( + record_name, store->getFilePath(), error); + if (!reader) { + if (c_error) + *c_error = new IndexStoreError{error}; + return nullptr; + } + return reader.release(); +} + +void indexstore_record_reader_dispose(indexstore_record_reader_t rdr) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + delete reader; +} + +#if INDEXSTORE_HAS_BLOCKS +/// Goes through the symbol data and passes symbols to \c receiver, for the +/// symbol data that \c filter returns true on. +/// +/// This allows allocating memory only for the record symbols that the caller is +/// interested in. +bool indexstore_record_reader_search_symbols( + indexstore_record_reader_t rdr, + INDEXSTORE_NOESCAPE bool (^filter)(indexstore_symbol_t symbol, bool *stop), + INDEXSTORE_NOESCAPE void (^receiver)(indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + + auto filterFn = + [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn { + bool stop = false; + bool accept = filter((indexstore_symbol_t)&D, &stop); + return {accept, !stop}; + }; + auto receiverFn = [&](const IndexRecordDecl *D) { + receiver((indexstore_symbol_t)D); + }; + + return reader->searchDecls(filterFn, receiverFn); +} + +bool indexstore_record_reader_symbols_apply( + indexstore_record_reader_t rdr, bool nocache, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordDecl *D) -> bool { + return applier((indexstore_symbol_t)D); + }; + return reader->foreachDecl(nocache, receiverFn); +} + +bool indexstore_record_reader_occurrences_apply( + indexstore_record_reader_t rdr, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence(receiverFn); +} + +bool indexstore_record_reader_occurrences_in_line_range_apply( + indexstore_record_reader_t rdr, unsigned line_start, unsigned line_count, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrenceInLineRange(line_start, line_count, + receiverFn); +} + +/// \param symbols if non-zero \c symbols_count, indicates the list of symbols +/// that we want to get occurrences for. An empty array indicates that we want +/// occurrences for all symbols. +/// \param related_symbols Same as \c symbols but for related symbols. +bool indexstore_record_reader_occurrences_of_symbols_apply( + indexstore_record_reader_t rdr, indexstore_symbol_t *symbols, + size_t symbols_count, indexstore_symbol_t *related_symbols, + size_t related_symbols_count, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence( + receiverFn, {(IndexRecordDecl **)symbols, symbols_count}, + {(IndexRecordDecl **)related_symbols, related_symbols_count}); +} +#endif + +bool indexstore_record_reader_search_symbols_f( + indexstore_record_reader_t rdr, void *filter_ctx, + INDEXSTORE_NOESCAPE bool (*filter)(void *filter_ctx, + indexstore_symbol_t symbol, bool *stop), + void *receiver_ctx, + INDEXSTORE_NOESCAPE void (*receiver)(void *receiver_ctx, + indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + + auto filterFn = + [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn { + bool stop = false; + bool accept = filter(filter_ctx, (indexstore_symbol_t)&D, &stop); + return {accept, !stop}; + }; + auto receiverFn = [&](const IndexRecordDecl *D) { + receiver(receiver_ctx, (indexstore_symbol_t)D); + }; + + return reader->searchDecls(filterFn, receiverFn); +} + +bool indexstore_record_reader_symbols_apply_f( + indexstore_record_reader_t rdr, bool nocache, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordDecl *D) -> bool { + return applier(context, (indexstore_symbol_t)D); + }; + return reader->foreachDecl(nocache, receiverFn); +} + +bool indexstore_record_reader_occurrences_apply_f( + indexstore_record_reader_t rdr, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier(context, (indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence(receiverFn); +} + +bool indexstore_record_reader_occurrences_in_line_range_apply_f( + indexstore_record_reader_t rdr, unsigned line_start, unsigned line_count, + void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier(context, (indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrenceInLineRange(line_start, line_count, + receiverFn); +} + +bool indexstore_record_reader_occurrences_of_symbols_apply_f( + indexstore_record_reader_t rdr, indexstore_symbol_t *symbols, + size_t symbols_count, indexstore_symbol_t *related_symbols, + size_t related_symbols_count, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier(context, (indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence( + receiverFn, {(IndexRecordDecl **)symbols, symbols_count}, + {(IndexRecordDecl **)related_symbols, related_symbols_count}); +} + +size_t indexstore_store_get_unit_name_from_output_path(indexstore_t store, + const char *output_path, + char *name_buf, + size_t buf_size) { + llvm::SmallString<256> unitName; + clang::index::store::getUnitNameForOutputFile(output_path, unitName); + size_t nameLen = unitName.size(); + if (buf_size != 0) { + strncpy(name_buf, unitName.c_str(), buf_size - 1); + name_buf[buf_size - 1] = '\0'; + } + return nameLen; +} + +bool indexstore_store_get_unit_modification_time(indexstore_t c_store, + const char *unit_name, + int64_t *seconds, + int64_t *nanoseconds, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + std::string error; + // FIXME: This provides mod time with second-only accuracy. + auto optModTime = IndexUnitReader::getModificationTimeForUnit( + unit_name, store->getFilePath(), error); + if (!optModTime) { + if (c_error) + *c_error = new IndexStoreError{error}; + return true; + } + + timespec ts = toTimeSpec(*optModTime); + if (seconds) + *seconds = ts.tv_sec; + if (nanoseconds) + *nanoseconds = ts.tv_nsec; + + return false; +} + +indexstore_unit_reader_t +indexstore_unit_reader_create(indexstore_t c_store, const char *unit_name, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore *>(c_store); + std::unique_ptr<IndexUnitReader> reader; + std::string error; + reader = IndexUnitReader::createWithUnitFilename(unit_name, + store->getFilePath(), error); + if (!reader) { + if (c_error) + *c_error = new IndexStoreError{error}; + return nullptr; + } + return reader.release(); +} + +void indexstore_unit_reader_dispose(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + delete reader; +} + +indexstore_string_ref_t +indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getProviderIdentifier()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getProviderVersion()); +} + +void indexstore_unit_reader_get_modification_time(indexstore_unit_reader_t rdr, + int64_t *seconds, + int64_t *nanoseconds) { + auto reader = static_cast<IndexUnitReader *>(rdr); + // FIXME: This provides mod time with second-only accuracy. + sys::TimePoint<> timeVal = reader->getModificationTime(); + timespec ts = toTimeSpec(timeVal); + if (seconds) + *seconds = ts.tv_sec; + if (nanoseconds) + *nanoseconds = ts.tv_nsec; +} + +bool indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->isSystemUnit(); +} + +bool indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->isModuleUnit(); +} + +bool indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->isDebugCompilation(); +} + +bool indexstore_unit_reader_has_main_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->hasMainFile(); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_main_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getMainFilePath()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_module_name(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getModuleName()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getWorkingDirectory()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_output_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getOutputFile()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getSysrootPath()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_target(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return toIndexStoreString(reader->getTarget()); +} + +indexstore_unit_dependency_kind_t +indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + switch (dep->Kind) { + case IndexUnitReader::DependencyKind::Unit: + return INDEXSTORE_UNIT_DEPENDENCY_UNIT; + case IndexUnitReader::DependencyKind::Record: + return INDEXSTORE_UNIT_DEPENDENCY_RECORD; + case IndexUnitReader::DependencyKind::File: + return INDEXSTORE_UNIT_DEPENDENCY_FILE; + } +} + +bool indexstore_unit_dependency_is_system(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return dep->IsSystem; +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return toIndexStoreString(dep->FilePath); +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return toIndexStoreString(dep->ModuleName); +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_name(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo *>(c_dep); + return toIndexStoreString(dep->UnitOrRecordName); +} + +indexstore_string_ref_t +indexstore_unit_include_get_source_path(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo *>(c_inc); + return toIndexStoreString(inc->SourcePath); +} + +indexstore_string_ref_t +indexstore_unit_include_get_target_path(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo *>(c_inc); + return toIndexStoreString(inc->TargetPath); +} + +unsigned +indexstore_unit_include_get_source_line(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo *>(c_inc); + return inc->SourceLine; +} + +#if INDEXSTORE_HAS_BLOCKS +bool indexstore_unit_reader_dependencies_apply( + indexstore_unit_reader_t rdr, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_unit_dependency_t)) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->foreachDependency( + [&](const IndexUnitReader::DependencyInfo &depInfo) -> bool { + return applier((void *)&depInfo); + }); +} + +bool indexstore_unit_reader_includes_apply( + indexstore_unit_reader_t rdr, + INDEXSTORE_NOESCAPE bool (^applier)(indexstore_unit_include_t)) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->foreachInclude( + [&](const IndexUnitReader::IncludeInfo &incInfo) -> bool { + return applier((void *)&incInfo); + }); +} +#endif + +bool indexstore_unit_reader_dependencies_apply_f( + indexstore_unit_reader_t rdr, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_unit_dependency_t)) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->foreachDependency( + [&](const IndexUnitReader::DependencyInfo &depInfo) -> bool { + return applier(context, (void *)&depInfo); + }); +} + +bool indexstore_unit_reader_includes_apply_f( + indexstore_unit_reader_t rdr, void *context, + INDEXSTORE_NOESCAPE bool (*applier)(void *context, + indexstore_unit_include_t)) { + auto reader = static_cast<IndexUnitReader *>(rdr); + return reader->foreachInclude( + [&](const IndexUnitReader::IncludeInfo &incInfo) -> bool { + return applier(context, (void *)&incInfo); + }); +} Index: clang/tools/IndexStore/IndexStore.exports =================================================================== --- /dev/null +++ clang/tools/IndexStore/IndexStore.exports @@ -0,0 +1,76 @@ +indexstore_error_get_description +indexstore_error_dispose +indexstore_format_version +indexstore_store_create +indexstore_store_dispose +indexstore_store_get_unit_modification_time +indexstore_store_get_unit_name_from_output_path +indexstore_store_units_apply +indexstore_store_units_apply_f +indexstore_store_set_unit_event_handler +indexstore_store_set_unit_event_handler_f +indexstore_store_start_unit_event_listening +indexstore_store_stop_unit_event_listening +indexstore_store_discard_unit +indexstore_store_discard_record +indexstore_store_purge_stale_data +indexstore_symbol_get_kind +indexstore_symbol_get_language +indexstore_symbol_get_properties +indexstore_symbol_get_roles +indexstore_symbol_get_related_roles +indexstore_symbol_get_subkind +indexstore_symbol_get_name +indexstore_symbol_get_usr +indexstore_symbol_get_codegen_name +indexstore_symbol_relation_get_roles +indexstore_symbol_relation_get_symbol +indexstore_occurrence_get_symbol +indexstore_occurrence_get_roles +indexstore_occurrence_get_line_col +indexstore_occurrence_relations_apply +indexstore_occurrence_relations_apply_f +indexstore_record_reader_create +indexstore_record_reader_dispose +indexstore_record_reader_search_symbols +indexstore_record_reader_search_symbols_f +indexstore_record_reader_symbols_apply +indexstore_record_reader_symbols_apply_f +indexstore_record_reader_occurrences_apply +indexstore_record_reader_occurrences_apply_f +indexstore_record_reader_occurrences_in_line_range_apply +indexstore_record_reader_occurrences_in_line_range_apply_f +indexstore_record_reader_occurrences_of_symbols_apply +indexstore_record_reader_occurrences_of_symbols_apply_f +indexstore_unit_dependency_get_kind +indexstore_unit_dependency_get_filepath +indexstore_unit_dependency_get_modulename +indexstore_unit_dependency_get_name +indexstore_unit_dependency_is_system +indexstore_unit_event_get_kind +indexstore_unit_event_get_unit_name +indexstore_unit_event_notification_get_event +indexstore_unit_event_notification_get_events_count +indexstore_unit_event_notification_is_initial +indexstore_unit_reader_create +indexstore_unit_reader_dispose +indexstore_unit_reader_get_main_file +indexstore_unit_reader_get_modification_time +indexstore_unit_reader_get_module_name +indexstore_unit_reader_get_provider_identifier +indexstore_unit_reader_get_provider_version +indexstore_unit_reader_get_working_dir +indexstore_unit_reader_get_output_file +indexstore_unit_reader_get_sysroot_path +indexstore_unit_reader_get_target +indexstore_unit_reader_dependencies_apply +indexstore_unit_reader_dependencies_apply_f +indexstore_unit_reader_includes_apply +indexstore_unit_reader_includes_apply_f +indexstore_unit_reader_has_main_file +indexstore_unit_reader_is_debug_compilation +indexstore_unit_reader_is_module_unit +indexstore_unit_reader_is_system_unit +indexstore_unit_include_get_source_path +indexstore_unit_include_get_target_path +indexstore_unit_include_get_source_line Index: clang/tools/c-index-test/CMakeLists.txt =================================================================== --- clang/tools/c-index-test/CMakeLists.txt +++ clang/tools/c-index-test/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CheckIncludeFiles) + set(LLVM_LINK_COMPONENTS support ) @@ -5,8 +7,13 @@ add_clang_executable(c-index-test c-index-test.c core_main.cpp + JSONAggregation.cpp ) +set(INDEXSTORE_LIB) +set(CINDEXTEST_LIBS) +set(INDEXSTORE_LIB IndexStore) + if(NOT MSVC) set_property( SOURCE c-index-test.c @@ -20,17 +27,23 @@ libclang_static clangCodeGen clangIndex + clangIndexWhileBuilding + ${CINDEXTEST_LIBS} ) else() target_link_libraries(c-index-test PRIVATE libclang + ${INDEXSTORE_LIB} clangAST clangBasic clangCodeGen + clangDirectoryWatcher + clangIndexWhileBuilding clangFrontend clangIndex clangSerialization + ${CINDEXTEST_LIBS} ) endif() @@ -48,6 +61,13 @@ target_link_libraries(c-index-test PRIVATE ${LIBXML2_LIBRARIES}) endif() +if(APPLE) + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + target_link_libraries(c-index-test PRIVATE "-framework CoreServices") + endif() +endif() + if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) if(INTERNAL_INSTALL_PREFIX) set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") Index: clang/tools/c-index-test/JSONAggregation.h =================================================================== --- /dev/null +++ clang/tools/c-index-test/JSONAggregation.h @@ -0,0 +1,24 @@ +//===--- JSONAggregation.h - Index data aggregation in JSON format --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H +#define LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H + +#include "clang/Basic/LLVM.h" + +namespace clang { +namespace index { + +/// Returns true if an error occurred, false otherwise. +bool aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS); + +} // end namespace index +} // end namespace clang + +#endif Index: clang/tools/c-index-test/JSONAggregation.cpp =================================================================== --- /dev/null +++ clang/tools/c-index-test/JSONAggregation.cpp @@ -0,0 +1,416 @@ +//===--- JSONAggregation.cpp - Index data aggregation in JSON format ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "JSONAggregation.h" +#include "indexstore/Conversions.h" +#include "indexstore/IndexStoreCXX.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BuryPointer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace indexstore; +using namespace llvm; + +namespace { + +typedef size_t FilePathIndex; +typedef size_t RecordIndex; +typedef size_t SymbolIndex; + +struct UnitSourceInfo { + FilePathIndex FilePath; + SmallVector<RecordIndex, 2> AssociatedRecords; +}; + +struct UnitInfo { + std::string Name; + SmallVector<UnitSourceInfo, 8> Sources; + SmallVector<std::string, 3> UnitDepends; + FilePathIndex OutFile; + StringRef Triple; +}; + +struct SymbolInfo { + SymbolKind Kind; + SymbolLanguage Lang; + StringRef USR; + StringRef Name; + StringRef CodegenName; + SymbolRoleSet Roles = 0; + SymbolRoleSet RelatedRoles = 0; +}; + +struct SymbolRelationInfo { + SymbolIndex RelatedSymbol; + SymbolRoleSet Roles; + SymbolRelationInfo(SymbolIndex relSymbol, SymbolRoleSet roles) + : RelatedSymbol(relSymbol), Roles(roles) {} +}; + +struct SymbolOccurrenceInfo { + SymbolIndex Symbol; + SymbolRoleSet Roles = 0; + std::vector<SymbolRelationInfo> Relations; + unsigned Line; + unsigned Column; +}; + +struct RecordInfo { + SmallVector<SymbolOccurrenceInfo, 8> Occurrences; +}; + +class Aggregator { + IndexStore Store; + + BumpPtrAllocator Allocator; + + StringMap<FilePathIndex, BumpPtrAllocator &> FilePathIndices; + std::vector<StringRef> FilePaths; + StringMap<char, BumpPtrAllocator &> Triples; + + std::vector<std::unique_ptr<UnitInfo>> Units; + + StringMap<RecordIndex, BumpPtrAllocator &> RecordIndices; + std::vector<std::unique_ptr<RecordInfo>> Records; + + StringMap<SymbolIndex, BumpPtrAllocator &> SymbolIndices; + std::vector<SymbolInfo> Symbols; + +public: + explicit Aggregator(IndexStore store) + : Store(std::move(store)), FilePathIndices(Allocator), Triples(Allocator), + RecordIndices(Allocator), SymbolIndices(Allocator) {} + + bool process(); + void processUnit(StringRef name, IndexUnitReader &UnitReader); + void dumpJSON(raw_ostream &OS); + +private: + StringRef copyStr(StringRef str) { + if (str.empty()) + return StringRef(); + char *buf = Allocator.Allocate<char>(str.size()); + std::copy(str.begin(), str.end(), buf); + return StringRef(buf, str.size()); + } + + StringRef getTripleString(StringRef inputTriple) { + return Triples.insert(std::make_pair(inputTriple, 0)).first->first(); + } + + FilePathIndex getFilePathIndex(StringRef path, StringRef workingDir); + RecordIndex getRecordIndex(StringRef recordFile); + SymbolIndex getSymbolIndex(IndexRecordSymbol sym); + std::unique_ptr<RecordInfo> processRecord(StringRef recordFile); +}; + +} // anonymous namespace + +bool Aggregator::process() { + bool succ = + Store.foreachUnit(/*sorted=*/true, [&](StringRef unitName) -> bool { + std::string error; + auto unitReader = IndexUnitReader(Store, unitName, error); + if (!unitReader) { + errs() << "error opening unit file '" << unitName << "': " << error + << '\n'; + return false; + } + + processUnit(unitName, unitReader); + return true; + }); + + return !succ; +} + +void Aggregator::processUnit(StringRef name, IndexUnitReader &UnitReader) { + auto workDir = UnitReader.getWorkingDirectory(); + auto unit = llvm::make_unique<UnitInfo>(); + unit->Name = name; + unit->Triple = getTripleString(UnitReader.getTarget()); + unit->OutFile = getFilePathIndex(UnitReader.getOutputFile(), workDir); + + struct DepInfo { + UnitSourceInfo source; + std::string unitName; + }; + SmallVector<DepInfo, 32> Deps; + UnitReader.foreachDependency([&](IndexUnitDependency dep) -> bool { + Deps.resize(Deps.size() + 1); + auto &depInfo = Deps.back(); + switch (dep.getKind()) { + case IndexUnitDependency::DependencyKind::Unit: { + depInfo.unitName = dep.getName(); + StringRef filePath = dep.getFilePath(); + if (!filePath.empty()) + depInfo.source.FilePath = getFilePathIndex(filePath, workDir); + break; + } + case IndexUnitDependency::DependencyKind::Record: { + depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); + RecordIndex recIndex = getRecordIndex(dep.getName()); + depInfo.source.AssociatedRecords.push_back(recIndex); + break; + } + case IndexUnitDependency::DependencyKind::File: + depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); + } + return true; + }); + + unit->Sources.reserve(Deps.size()); + for (auto &dep : Deps) { + if (!dep.unitName.empty()) { + unit->UnitDepends.emplace_back(std::move(dep.unitName)); + } else { + unit->Sources.push_back(std::move(dep.source)); + } + } + + Units.push_back(std::move(unit)); +} + +FilePathIndex Aggregator::getFilePathIndex(StringRef path, + StringRef workingDir) { + StringRef absPath; + SmallString<128> absPathBuf; + if (sys::path::is_absolute(path) || workingDir.empty()) { + absPath = path; + } else { + absPathBuf = workingDir; + sys::path::append(absPathBuf, path); + absPath = absPathBuf.str(); + } + + auto pair = FilePathIndices.insert(std::make_pair(absPath, FilePaths.size())); + bool wasInserted = pair.second; + if (wasInserted) { + FilePaths.push_back(pair.first->first()); + } + return pair.first->second; +} + +RecordIndex Aggregator::getRecordIndex(StringRef recordFile) { + auto pair = RecordIndices.insert(std::make_pair(recordFile, Records.size())); + bool wasInserted = pair.second; + if (wasInserted) { + Records.push_back(processRecord(recordFile)); + } + return pair.first->second; +} + +std::unique_ptr<RecordInfo> Aggregator::processRecord(StringRef recordFile) { + std::string error; + auto recordReader = IndexRecordReader(Store, recordFile, error); + if (!recordReader) { + errs() << "failed reading record file: " << recordFile << '\n'; + ::exit(1); + } + auto record = llvm::make_unique<RecordInfo>(); + recordReader.foreachOccurrence([&](IndexRecordOccurrence idxOccur) -> bool { + SymbolIndex symIdx = getSymbolIndex(idxOccur.getSymbol()); + SymbolInfo &symInfo = Symbols[symIdx]; + symInfo.Roles |= getSymbolRoles(idxOccur.getRoles()); + SymbolOccurrenceInfo occurInfo; + occurInfo.Symbol = symIdx; + idxOccur.foreachRelation([&](IndexSymbolRelation rel) -> bool { + SymbolIndex relsymIdx = getSymbolIndex(rel.getSymbol()); + SymbolInfo &relsymInfo = Symbols[relsymIdx]; + relsymInfo.RelatedRoles |= getSymbolRoles(rel.getRoles()); + occurInfo.Relations.emplace_back(relsymIdx, + getSymbolRoles(rel.getRoles())); + return true; + }); + occurInfo.Roles = getSymbolRoles(idxOccur.getRoles()); + std::tie(occurInfo.Line, occurInfo.Column) = idxOccur.getLineCol(); + record->Occurrences.push_back(std::move(occurInfo)); + return true; + }); + return record; +} + +SymbolIndex Aggregator::getSymbolIndex(IndexRecordSymbol sym) { + auto pair = + SymbolIndices.insert(std::make_pair(sym.getUSR(), Symbols.size())); + bool wasInserted = pair.second; + if (wasInserted) { + SymbolInfo symInfo; + symInfo.Kind = getSymbolKind(sym.getKind()); + symInfo.Lang = getSymbolLanguage(sym.getLanguage()); + symInfo.USR = pair.first->first(); + symInfo.Name = copyStr(sym.getName()); + symInfo.CodegenName = copyStr(sym.getCodegenName()); + Symbols.push_back(std::move(symInfo)); + } + return pair.first->second; +} + +void Aggregator::dumpJSON(raw_ostream &OS) { + OS << "{\n"; + OS.indent(2) << "\"files\": [\n"; + for (unsigned i = 0, e = FilePaths.size(); i != e; ++i) { + OS.indent(4) << '\"' << FilePaths[i] << '\"'; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + OS.indent(2) << "\"symbols\": [\n"; + for (unsigned i = 0, e = Symbols.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + SymbolInfo &symInfo = Symbols[i]; + OS.indent(6) << "\"kind\": \"" << getSymbolKindString(symInfo.Kind) + << "\",\n"; + OS.indent(6) << "\"lang\": \"" << getSymbolLanguageString(symInfo.Lang) + << "\",\n"; + OS.indent(6) << "\"usr\": \"" << symInfo.USR << "\",\n"; + OS.indent(6) << "\"name\": \"" << symInfo.Name << "\",\n"; + if (!symInfo.CodegenName.empty()) + OS.indent(6) << "\"codegen\": \"" << symInfo.CodegenName << "\",\n"; + OS.indent(6) << "\"roles\": \""; + printSymbolRoles(symInfo.Roles, OS); + OS << '\"'; + if (symInfo.RelatedRoles != 0) { + OS << ",\n"; + OS.indent(6) << "\"rel-roles\": \""; + printSymbolRoles(symInfo.RelatedRoles, OS); + OS << '\"'; + } + OS << '\n'; + OS.indent(4) << "}"; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + OS.indent(2) << "\"records\": [\n"; + for (unsigned i = 0, e = Records.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + RecordInfo &recInfo = *Records[i]; + OS.indent(6) << "\"occurrences\": [\n"; + for (unsigned oi = 0, oe = recInfo.Occurrences.size(); oi != oe; ++oi) { + OS.indent(8) << "{\n"; + SymbolOccurrenceInfo &occurInfo = recInfo.Occurrences[oi]; + OS.indent(10) << "\"symbol\": " << occurInfo.Symbol << ",\n"; + OS.indent(10) << "\"line\": " << occurInfo.Line << ",\n"; + OS.indent(10) << "\"col\": " << occurInfo.Column << ",\n"; + OS.indent(10) << "\"roles\": \""; + printSymbolRoles(occurInfo.Roles, OS); + OS << '\"'; + if (!occurInfo.Relations.empty()) { + OS << ",\n"; + OS.indent(10) << "\"relations\": [\n"; + for (unsigned ri = 0, re = occurInfo.Relations.size(); ri != re; ++ri) { + OS.indent(12) << "{\n"; + SymbolRelationInfo &relInfo = occurInfo.Relations[ri]; + OS.indent(14) << "\"symbol\": " << relInfo.RelatedSymbol << ",\n"; + OS.indent(14) << "\"rel-roles\": \""; + printSymbolRoles(relInfo.Roles, OS); + OS << "\"\n"; + OS.indent(12) << "}"; + if (ri < re - 1) + OS << ','; + OS << '\n'; + } + OS.indent(10) << "]\n"; + } + OS << '\n'; + OS.indent(8) << "}"; + if (oi < oe - 1) + OS << ','; + OS << '\n'; + } + OS.indent(6) << "]\n"; + OS.indent(4) << "}"; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + StringMap<size_t> UnitIndicesByName; + for (unsigned i = 0, e = Units.size(); i != e; ++i) { + UnitInfo &unit = *Units[i]; + UnitIndicesByName[unit.Name] = i; + } + + OS.indent(2) << "\"units\": [\n"; + for (unsigned i = 0, e = Units.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + UnitInfo &unit = *Units[i]; + OS.indent(6) << "\"triple\": \"" << unit.Triple << "\",\n"; + OS.indent(6) << "\"out-file\": " << unit.OutFile << ",\n"; + if (!unit.UnitDepends.empty()) { + OS.indent(6) << "\"unit-dependencies\": ["; + for (unsigned ui = 0, ue = unit.UnitDepends.size(); ui != ue; ++ui) { + OS << UnitIndicesByName[unit.UnitDepends[ui]]; + if (ui < ue - 1) + OS << ", "; + } + OS << "],\n"; + } + OS.indent(6) << "\"sources\": [\n"; + for (unsigned si = 0, se = unit.Sources.size(); si != se; ++si) { + OS.indent(8) << "{\n"; + UnitSourceInfo &source = unit.Sources[si]; + OS.indent(10) << "\"file\": " << source.FilePath; + if (!source.AssociatedRecords.empty()) { + OS << ",\n"; + OS.indent(10) << "\"records\": ["; + for (unsigned ri = 0, re = source.AssociatedRecords.size(); ri != re; + ++ri) { + OS << source.AssociatedRecords[ri]; + if (ri < re - 1) + OS << ", "; + } + OS << ']'; + } + OS << '\n'; + OS.indent(8) << "}"; + if (si < se - 1) + OS << ','; + OS << '\n'; + } + OS.indent(6) << "]\n"; + OS.indent(4) << "}"; + if (i < e - 1) + OS << ','; + OS << '\n'; + } + OS.indent(2) << "]\n"; + OS << "}\n"; +} + +bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { + std::string error; + auto dataStore = IndexStore(StorePath, error); + if (!dataStore) { + errs() << "error opening store path '" << StorePath << "': " << error + << '\n'; + return true; + } + + // Explicitely avoid doing any memory cleanup for aggregator since the process + // is going to exit when we are done. + Aggregator *aggregator = new Aggregator(std::move(dataStore)); + bool err = aggregator->process(); + if (err) + return true; + aggregator->dumpJSON(OS); + llvm::BuryPointer(aggregator); + return false; +} Index: clang/tools/c-index-test/core_main.cpp =================================================================== --- clang/tools/c-index-test/core_main.cpp +++ clang/tools/c-index-test/core_main.cpp @@ -6,23 +6,31 @@ // //===----------------------------------------------------------------------===// +#include "JSONAggregation.h" +#include "indexstore/Conversions.h" +#include "indexstore/IndexStoreCXX.h" #include "clang/Basic/LangOptions.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" -#include "clang/Index/IndexingAction.h" +#include "clang/Index/CodegenNameGenerator.h" +#include "clang/Index/GenerateIndexAction.h" #include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexWhileBuilding/IndexRecordReader.h" +#include "clang/Index/IndexWhileBuilding/IndexUnitReader.h" #include "clang/Index/USRGeneration.h" -#include "clang/Index/CodegenNameGenerator.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/PrettyStackTrace.h" +#include <thread> using namespace clang; using namespace clang::index; @@ -35,18 +43,38 @@ enum class ActionType { None, PrintSourceSymbols, + PrintRecord, + PrintUnit, + PrintStoreFormatVersion, + AggregateAsJSON, + WatchDir, }; namespace options { static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); -static cl::opt<ActionType> -Action(cl::desc("Action:"), cl::init(ActionType::None), - cl::values( - clEnumValN(ActionType::PrintSourceSymbols, - "print-source-symbols", "Print symbols from source")), - cl::cat(IndexTestCoreCategory)); +static cl::opt<ActionType> Action( + cl::desc("Action:"), cl::init(ActionType::None), + cl::values( + clEnumValN(ActionType::PrintSourceSymbols, "print-source-symbols", + "Print symbols from source"), + clEnumValN(ActionType::PrintRecord, "print-record", + "Print record info"), + clEnumValN(ActionType::PrintUnit, "print-unit", "Print unit info"), + clEnumValN(ActionType::PrintStoreFormatVersion, + "print-store-format-version", "Print store format version"), + clEnumValN(ActionType::AggregateAsJSON, "aggregate-json", + "Aggregate index data in JSON format"), + clEnumValN(ActionType::WatchDir, "watch-dir", + "Watch directory for file events")), + cl::cat(IndexTestCoreCategory)); + +static cl::opt<std::string> OutputFile("o", cl::desc("output file"), + cl::cat(IndexTestCoreCategory)); + +static cl::list<std::string> InputFiles(cl::Positional, + cl::desc("<filename>...")); static cl::extrahelp MoreHelp( "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler " @@ -67,6 +95,8 @@ ModuleFormat("fmodule-format", cl::init("raw"), cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'")); +static cl::opt<std::string> FilePathAndRange( + "filepath", cl::desc("File path that can optionally include a line range")); } } // anonymous namespace @@ -221,9 +251,8 @@ auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS); IndexingOptions IndexOpts; IndexOpts.IndexFunctionLocals = indexLocals; - std::unique_ptr<FrontendAction> IndexAction; - IndexAction = createIndexingAction(DataConsumer, IndexOpts, - /*WrappedAction=*/nullptr); + std::unique_ptr<FrontendAction> IndexAction = + llvm::make_unique<GenerateIndexAction>(DataConsumer, IndexOpts); auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( @@ -279,6 +308,312 @@ return false; } +//===----------------------------------------------------------------------===// +// Print Record +//===----------------------------------------------------------------------===// + +static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS); +static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS); + +static int printRecord(StringRef Filename, raw_ostream &OS) { + std::string Error; + auto Reader = IndexRecordReader::createWithFilePath(Filename, Error); + if (!Reader) { + errs() << Error << '\n'; + return true; + } + + Reader->foreachDecl(/*noCache=*/true, + [&](const IndexRecordDecl *Rec) -> bool { + printSymbol(*Rec, OS); + return true; + }); + OS << "------------\n"; + Reader->foreachOccurrence([&](const IndexRecordOccurrence &Rec) -> bool { + printSymbol(Rec, OS); + return true; + }); + + return false; +}; + +//===----------------------------------------------------------------------===// +// Print Store Records +//===----------------------------------------------------------------------===// + +static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS); +static void printSymbol(indexstore::IndexRecordOccurrence Occur, + raw_ostream &OS); + +static bool printStoreRecord(indexstore::IndexStore &Store, StringRef RecName, + StringRef FilePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexRecordReader Reader(Store, RecName, Error); + if (!Reader) { + errs() << "error loading record: " << Error << "\n"; + return true; + } + + StringRef Filename = sys::path::filename(FilePath); + OS << Filename << '\n'; + OS << "------------\n"; + Reader.foreachSymbol(/*noCache=*/true, + [&](indexstore::IndexRecordSymbol Sym) -> bool { + printSymbol(Sym, OS); + return true; + }); + OS << "------------\n"; + Reader.foreachOccurrence( + [&](indexstore::IndexRecordOccurrence Occur) -> bool { + printSymbol(Occur, OS); + return true; + }); + + return false; +} + +static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexStore Store(StorePath, Error); + if (!Store) { + errs() << "error loading store: " << Error << "\n"; + return 1; + } + + bool Success = + Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { + indexstore::IndexUnitReader Reader(Store, UnitName, Error); + if (!Reader) { + errs() << "error loading unit: " << Error << "\n"; + return false; + } + return Reader.foreachDependency( + [&](indexstore::IndexUnitDependency Dep) -> bool { + if (Dep.getKind() == + indexstore::IndexUnitDependency::DependencyKind::Record) { + bool Err = printStoreRecord(Store, Dep.getName(), + Dep.getFilePath(), OS); + OS << '\n'; + return !Err; + } + return true; + }); + }); + + return !Success; +} + +static std::string findRecordNameForFile(indexstore::IndexStore &store, + StringRef filePath) { + std::string recName; + store.foreachUnit(/*sorted=*/false, [&](StringRef unitName) -> bool { + std::string error; + indexstore::IndexUnitReader Reader(store, unitName, error); + if (!Reader) { + errs() << "error loading unit: " << error << "\n"; + return false; + } + Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { + if (Dep.getKind() == + indexstore::IndexUnitDependency::DependencyKind::Record) { + if (Dep.getFilePath() == filePath) { + recName = Dep.getName(); + return false; + } + return true; + } + return true; + }); + return true; + }); + return recName; +} + +static int printStoreFileRecord(StringRef storePath, StringRef filePath, + Optional<unsigned> lineStart, + unsigned lineCount, raw_ostream &OS) { + std::string error; + indexstore::IndexStore store(storePath, error); + if (!store) { + errs() << "error loading store: " << error << "\n"; + return 1; + } + + std::string recName = findRecordNameForFile(store, filePath); + if (recName.empty()) { + errs() << "could not find record for '" << filePath << "'\n"; + return 1; + } + + if (!lineStart.hasValue()) + return printStoreRecord(store, recName, filePath, OS); + + indexstore::IndexRecordReader Reader(store, recName, error); + if (!Reader) { + errs() << "error loading record: " << error << "\n"; + return 1; + } + + Reader.foreachOccurrenceInLineRange( + *lineStart, lineCount, + [&](indexstore::IndexRecordOccurrence Occur) -> bool { + printSymbol(Occur, OS); + return true; + }); + + return 0; +} + +//===----------------------------------------------------------------------===// +// Print Unit +//===----------------------------------------------------------------------===// + +static int printUnit(StringRef Filename, raw_ostream &OS) { + std::string Error; + auto Reader = IndexUnitReader::createWithFilePath(Filename, Error); + if (!Reader) { + errs() << Error << '\n'; + return true; + } + + OS << "provider: " << Reader->getProviderIdentifier() << '-' + << Reader->getProviderVersion() << '\n'; + OS << "is-system: " << Reader->isSystemUnit() << '\n'; + OS << "is-module: " << Reader->isModuleUnit() << '\n'; + OS << "module-name: " + << (Reader->getModuleName().empty() ? "<none>" : Reader->getModuleName()) + << '\n'; + OS << "has-main: " << Reader->hasMainFile() << '\n'; + OS << "main-path: " << Reader->getMainFilePath() << '\n'; + OS << "work-dir: " << Reader->getWorkingDirectory() << '\n'; + OS << "out-file: " << Reader->getOutputFile() << '\n'; + OS << "target: " << Reader->getTarget() << '\n'; + OS << "is-debug: " << Reader->isDebugCompilation() << '\n'; + OS << "DEPEND START\n"; + unsigned NumDepends = 0; + Reader->foreachDependency( + [&](const IndexUnitReader::DependencyInfo &Dep) -> bool { + switch (Dep.Kind) { + case IndexUnitReader::DependencyKind::Unit: + OS << "Unit | "; + break; + case IndexUnitReader::DependencyKind::Record: + OS << "Record | "; + break; + case IndexUnitReader::DependencyKind::File: + OS << "File | "; + break; + } + OS << (Dep.IsSystem ? "system" : "user"); + OS << " | "; + if (!Dep.ModuleName.empty()) + OS << Dep.ModuleName << " | "; + OS << Dep.FilePath; + if (!Dep.UnitOrRecordName.empty()) + OS << " | " << Dep.UnitOrRecordName; + OS << '\n'; + ++NumDepends; + return true; + }); + OS << "DEPEND END (" << NumDepends << ")\n"; + OS << "INCLUDE START\n"; + unsigned NumIncludes = 0; + Reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &Inc) -> bool { + OS << Inc.SourcePath << ":" << Inc.SourceLine << " | "; + OS << Inc.TargetPath << '\n'; + ++NumIncludes; + return true; + }); + OS << "INCLUDE END (" << NumIncludes << ")\n"; + + return false; +}; + +//===----------------------------------------------------------------------===// +// Print Store Units +//===----------------------------------------------------------------------===// + +static bool printStoreUnit(indexstore::IndexStore &Store, StringRef UnitName, + raw_ostream &OS) { + std::string Error; + indexstore::IndexUnitReader Reader(Store, UnitName, Error); + if (!Reader) { + errs() << "error loading unit: " << Error << "\n"; + return true; + } + + OS << "provider: " << Reader.getProviderIdentifier() << '-' + << Reader.getProviderVersion() << '\n'; + OS << "is-system: " << Reader.isSystemUnit() << '\n'; + OS << "is-module: " << Reader.isModuleUnit() << '\n'; + OS << "module-name: " + << (Reader.getModuleName().empty() ? "<none>" : Reader.getModuleName()) + << '\n'; + OS << "has-main: " << Reader.hasMainFile() << '\n'; + OS << "main-path: " << Reader.getMainFilePath() << '\n'; + OS << "work-dir: " << Reader.getWorkingDirectory() << '\n'; + OS << "out-file: " << Reader.getOutputFile() << '\n'; + OS << "target: " << Reader.getTarget() << '\n'; + OS << "is-debug: " << Reader.isDebugCompilation() << '\n'; + OS << "DEPEND START\n"; + unsigned NumDepends = 0; + Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { + switch (Dep.getKind()) { + case indexstore::IndexUnitDependency::DependencyKind::Unit: + OS << "Unit | "; + break; + case indexstore::IndexUnitDependency::DependencyKind::Record: + OS << "Record | "; + break; + case indexstore::IndexUnitDependency::DependencyKind::File: + OS << "File | "; + break; + } + OS << (Dep.isSystem() ? "system" : "user"); + OS << " | "; + if (!Dep.getModuleName().empty()) + OS << Dep.getModuleName() << " | "; + OS << Dep.getFilePath(); + if (!Dep.getName().empty()) + OS << " | " << Dep.getName(); + OS << '\n'; + ++NumDepends; + return true; + }); + OS << "DEPEND END (" << NumDepends << ")\n"; + OS << "INCLUDE START\n"; + unsigned NumIncludes = 0; + Reader.foreachInclude([&](indexstore::IndexUnitInclude Inc) -> bool { + OS << Inc.getSourcePath() << ":" << Inc.getSourceLine() << " | "; + OS << Inc.getTargetPath() << '\n'; + ++NumIncludes; + return true; + }); + OS << "INCLUDE END (" << NumIncludes << ")\n"; + + return false; +} + +static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexStore Store(StorePath, Error); + if (!Store) { + errs() << "error loading store: " << Error << "\n"; + return 1; + } + + bool Success = + Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { + OS << UnitName << '\n'; + OS << "--------\n"; + bool err = printStoreUnit(Store, UnitName, OS); + OS << '\n'; + return !err; + }); + + return !Success; +} + //===----------------------------------------------------------------------===// // Helper Utils //===----------------------------------------------------------------------===// @@ -316,10 +651,200 @@ generateFullUSRForModule(Mod, OS); } +static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS) { + printSymbolInfo(Rec.SymInfo, OS); + OS << " | "; + + if (Rec.Name.empty()) + OS << "<no-name>"; + else + OS << Rec.Name; + OS << " | "; + + if (Rec.USR.empty()) + OS << "<no-usr>"; + else + OS << Rec.USR; + OS << " | "; + + if (Rec.CodeGenName.empty()) + OS << "<no-cgname>"; + else + OS << Rec.CodeGenName; + OS << " | "; + + printSymbolRoles(Rec.Roles, OS); + OS << " - "; + printSymbolRoles(Rec.RelatedRoles, OS); + OS << '\n'; +} + +static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS) { + OS << Rec.Line << ':' << Rec.Column << " | "; + printSymbolInfo(Rec.Dcl->SymInfo, OS); + OS << " | "; + + if (Rec.Dcl->USR.empty()) + OS << "<no-usr>"; + else + OS << Rec.Dcl->USR; + OS << " | "; + + printSymbolRoles(Rec.Roles, OS); + OS << " | "; + OS << "rel: " << Rec.Relations.size() << '\n'; + for (auto &Rel : Rec.Relations) { + OS << '\t'; + printSymbolRoles(Rel.Roles, OS); + OS << " | "; + if (Rel.Dcl->USR.empty()) + OS << "<no-usr>"; + else + OS << Rel.Dcl->USR; + OS << '\n'; + } +} + +static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS) { + SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), + getSymbolSubKind(Sym.getSubKind()), + getSymbolLanguage(Sym.getLanguage()), + SymbolPropertySet(Sym.getProperties())}; + + printSymbolInfo(SymInfo, OS); + OS << " | "; + + if (Sym.getName().empty()) + OS << "<no-name>"; + else + OS << Sym.getName(); + OS << " | "; + + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << " | "; + + if (Sym.getCodegenName().empty()) + OS << "<no-cgname>"; + else + OS << Sym.getCodegenName(); + OS << " | "; + + printSymbolRoles(getSymbolRoles(Sym.getRoles()), OS); + OS << " - "; + printSymbolRoles(getSymbolRoles(Sym.getRelatedRoles()), OS); + OS << '\n'; +} + +static void printSymbol(indexstore::IndexRecordOccurrence Occur, + raw_ostream &OS) { + OS << Occur.getLineCol().first << ':' << Occur.getLineCol().second << " | "; + auto Sym = Occur.getSymbol(); + SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), + getSymbolSubKind(Sym.getSubKind()), + getSymbolLanguage(Sym.getLanguage()), + SymbolPropertySet(Sym.getProperties())}; + + printSymbolInfo(SymInfo, OS); + OS << " | "; + + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << " | "; + + unsigned NumRelations = 0; + Occur.foreachRelation([&](indexstore::IndexSymbolRelation) { + ++NumRelations; + return true; + }); + + printSymbolRoles(getSymbolRoles(Occur.getRoles()), OS); + OS << " | "; + OS << "rel: " << NumRelations << '\n'; + Occur.foreachRelation([&](indexstore::IndexSymbolRelation Rel) { + OS << '\t'; + printSymbolRoles(getSymbolRoles(Rel.getRoles()), OS); + OS << " | "; + auto Sym = Rel.getSymbol(); + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << '\n'; + return true; + }); +} + +static int watchDirectory(StringRef dirPath) { + raw_ostream &OS = outs(); + auto receiver = [&](ArrayRef<DirectoryWatcher::Event> Events, + bool isInitial) { + OS << "-- " << Events.size() << " :\n"; + for (auto evt : Events) { + switch (evt.Kind) { + case DirectoryWatcher::Event::EventKind::Modified: + OS << "modified: "; + break; + case DirectoryWatcher::Event::EventKind::Removed: + OS << "removed: "; + break; + case DirectoryWatcher::Event::EventKind::WatchedDirRemoved: + OS << "dir deleted: "; + break; + case DirectoryWatcher::Event::EventKind::WatcherGotInvalidated: + OS << "watcher got invalidated: "; + break; + } + OS << evt.Filename << '\n'; + } + }; + auto watcher = DirectoryWatcher::create(dirPath, receiver, + /*waitInitialSync=*/true); + if (!watcher) { + errs() << "failed creating directory watcher" << '\n'; + return 1; + } + + while (1) { + std::this_thread::yield(); + } +} + //===----------------------------------------------------------------------===// // Command line processing. //===----------------------------------------------------------------------===// +bool deconstructPathAndRange(StringRef input, std::string &filepath, + Optional<unsigned> &lineStart, + unsigned &lineCount) { + StringRef path, range; + std::tie(path, range) = input.split(':'); + StringRef start, end; + std::tie(start, end) = range.split(':'); + filepath = path; + lineCount = 0; + if (start.empty()) + return false; + unsigned num; + if (start.getAsInteger(10, num)) { + errs() << "couldn't convert to integer: " << start << '\n'; + return true; + } + lineStart = num; + if (end.empty()) + return false; + if (end.getAsInteger(10, num)) { + errs() << "couldn't convert to integer: " << end << '\n'; + return true; + } + lineCount = num - lineStart.getValue(); + return false; +} + int indextest_core_main(int argc, const char **argv) { sys::PrintStackTraceOnErrorSignal(argv[0]); PrettyStackTraceProgram X(argc, argv); @@ -359,5 +884,74 @@ options::IncludeLocals); } + if (options::Action == ActionType::PrintRecord) { + if (!options::FilePathAndRange.empty()) { + std::string filepath; + Optional<unsigned> lineStart; + unsigned lineCount; + if (deconstructPathAndRange(options::FilePathAndRange, filepath, + lineStart, lineCount)) + return 1; + + if (options::InputFiles.empty()) { + errs() << "error: missing index store path\n"; + return 1; + } + return printStoreFileRecord(options::InputFiles[0], filepath, lineStart, + lineCount, outs()); + } + + if (options::InputFiles.empty()) { + errs() << "error: missing input file or directory\n"; + return 1; + } + + if (sys::fs::is_directory(options::InputFiles[0])) + return printStoreRecords(options::InputFiles[0], outs()); + else + return printRecord(options::InputFiles[0], outs()); + } + + if (options::Action == ActionType::PrintUnit) { + if (options::InputFiles.empty()) { + errs() << "error: missing input file or directory\n"; + return 1; + } + + if (sys::fs::is_directory(options::InputFiles[0])) + return printStoreUnits(options::InputFiles[0], outs()); + else + return printUnit(options::InputFiles[0], outs()); + } + + if (options::Action == ActionType::PrintStoreFormatVersion) { + outs() << indexstore::IndexStore::formatVersion() << '\n'; + } + + if (options::Action == ActionType::AggregateAsJSON) { + if (options::InputFiles.empty()) { + errs() << "error: missing input data store directory\n"; + return 1; + } + StringRef storePath = options::InputFiles[0]; + if (options::OutputFile.empty()) + return aggregateDataAsJSON(storePath, outs()); + std::error_code EC; + raw_fd_ostream OS(options::OutputFile, EC, llvm::sys::fs::F_None); + if (EC) { + errs() << "failed to open output file: " << EC.message() << '\n'; + return 1; + } + return aggregateDataAsJSON(storePath, OS); + } + + if (options::Action == ActionType::WatchDir) { + if (options::InputFiles.empty()) { + errs() << "error: missing directory path\n"; + return 1; + } + return watchDirectory(options::InputFiles[0]); + } + return 0; } Index: clang/tools/libclang/CXIndexDataConsumer.h =================================================================== --- clang/tools/libclang/CXIndexDataConsumer.h +++ clang/tools/libclang/CXIndexDataConsumer.h @@ -507,6 +507,14 @@ CXCursor getRefCursor(const NamedDecl *D, SourceLocation Loc); static bool shouldIgnoreIfImplicit(const Decl *D); + + void initialize(ASTContext &Ctx) override {} + + bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *MI, + index::SymbolRoleSet Roles, + SourceLocation Loc) override { + return true; + } }; inline ScratchAlloc::ScratchAlloc(CXIndexDataConsumer &idxCtx) : IdxCtx(idxCtx) { Index: clang/tools/libclang/Indexing.cpp =================================================================== --- clang/tools/libclang/Indexing.cpp +++ clang/tools/libclang/Indexing.cpp @@ -20,7 +20,8 @@ #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/Utils.h" -#include "clang/Index/IndexingAction.h" +#include "clang/Index/GenerateIndexAction.h" +#include "clang/Index/IndexAction.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/PPConditionalDirectiveRecord.h" @@ -548,12 +549,14 @@ auto DataConsumer = std::make_shared<CXIndexDataConsumer>(client_data, CB, index_options, CXTU->getTU()); - auto InterAction = llvm::make_unique<IndexingFrontendAction>(DataConsumer, - SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); - std::unique_ptr<FrontendAction> IndexAction; - IndexAction = createIndexingAction(DataConsumer, - getIndexingOptionsFromCXOptions(index_options), - std::move(InterAction)); + + std::unique_ptr<FrontendAction> IndexAction = + llvm::make_unique<IndexActionWrapper>( + llvm::make_unique<GenerateIndexAction>( + DataConsumer, getIndexingOptionsFromCXOptions(index_options)), + llvm::make_unique<IndexingFrontendAction>( + DataConsumer, + SkipBodies ? IdxSession->SkipBodyData.get() : nullptr)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<FrontendAction> Index: clang/unittests/Index/IndexTests.cpp =================================================================== --- clang/unittests/Index/IndexTests.cpp +++ clang/unittests/Index/IndexTests.cpp @@ -13,9 +13,9 @@ #include "clang/Basic/SourceManager.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" +#include "clang/Index/GenerateIndexAction.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/IndexSymbol.h" -#include "clang/Index/IndexingAction.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/StringRef.h" @@ -71,7 +71,6 @@ public: void initialize(ASTContext &Ctx) override { AST = &Ctx; - IndexDataConsumer::initialize(Ctx); } bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, @@ -104,6 +103,14 @@ return true; } + virtual void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {} + virtual bool handleModuleOccurence(const ImportDecl *ImportD, + const Module *Mod, SymbolRoleSet Roles, + SourceLocation Loc) override { + return true; + } + virtual void finish() override {} + std::vector<TestSymbol> Symbols; const ASTContext *AST = nullptr; }; Index: llvm/include/llvm/ADT/ArrayRef.h =================================================================== --- llvm/include/llvm/ADT/ArrayRef.h +++ llvm/include/llvm/ADT/ArrayRef.h @@ -481,6 +481,12 @@ return Vec; } + /// Construct an ArrayRef from a std::array. + template <typename T, std::size_t N> + ArrayRef<T> makeArrayRef(const std::array<T, N> &Arr) { + return Arr; + } + /// Construct an ArrayRef from an ArrayRef (no-op) (const) template <typename T> ArrayRef<T> makeArrayRef(const ArrayRef<T> &Vec) { return Vec; Index: llvm/include/llvm/Bitstream/BitCodes.h =================================================================== --- llvm/include/llvm/Bitstream/BitCodes.h +++ llvm/include/llvm/Bitstream/BitCodes.h @@ -168,6 +168,11 @@ SmallVector<BitCodeAbbrevOp, 32> OperandList; public: + BitCodeAbbrev() = default; + + explicit BitCodeAbbrev(std::initializer_list<BitCodeAbbrevOp> OperandList) + : OperandList(OperandList) {} + unsigned getNumOperandInfos() const { return static_cast<unsigned>(OperandList.size()); }