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 @@ -123,7 +124,8 @@ std::function RefsCallback, std::function RelationsCallback, std::function IncludeGraphCallback) - : WrapperFrontendAction(index::createIndexingAction(C, Opts, nullptr)), + : WrapperFrontendAction( + llvm::make_unique(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(C, Opts)), PragmaHandler(PragmaHandler) {} std::unique_ptr 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; + 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; +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, 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 OutputFiles; + /// \brief An optional callback function used to wrap all FrontendActions + /// produced to generate imported modules before they are executed. + std::function( + const FrontendOptions &opts, std::unique_ptr action)> + GenModuleActionWrapper; + /// Force an output buffer. std::unique_ptr OutputStream; @@ -790,6 +796,19 @@ bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override; + void setGenModuleActionWrapper( + std::function( + const FrontendOptions &Opts, std::unique_ptr Action)> + Wrapper) { + GenModuleActionWrapper = Wrapper; + } + + std::function( + const FrontendOptions &Opts, std::unique_ptr Action)> + getGenModuleActionWrapper() const { + return GenModuleActionWrapper; + } + void addDependencyCollector(std::shared_ptr 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 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 getAllManglings(const Decl *D); private: struct Implementation; - std::unique_ptr 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 Relations; - - DeclOccurrence(SymbolRoleSet R, unsigned Offset, const Decl *D, - ArrayRef 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 + +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 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 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 DataConsumer; + std::shared_ptr IndexCtx; + +public: + GenerateIndexAction(std::shared_ptr DataConsumer, + const IndexingOptions &Opts); + + std::unique_ptr 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 + +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 IndexingAction; + +public: + IndexActionWrapper(std::unique_ptr IndexingActionParam, + std::unique_ptr WrappedActionParam); + + /// \returns a consumer that first invokes consumer created by + /// WrappedActionParam and then consumer created by IndexingActionParam. + std::unique_ptr 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 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 +#include + +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 + +namespace llvm { +class BitstreamWriter; +} + +namespace clang { +class FileEntry; +class FileManager; + +namespace index { + +class IndexPPCallbacks : public PPCallbacks { + std::shared_ptr IndexCtx; + +public: + IndexPPCallbacks(std::shared_ptr 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 + +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 RecordByFile; + +public: + explicit DeclOccurrenceCollector(SourceManager &SM) : SM(SM) {} + void setIndexingContext(IndexingContext &IC) { IndexCtx = &IC; } + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) override; + const llvm::DenseMap &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 + +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 Context = nullptr; + +public: + EmitIndexAction(const IndexingOptions &IndexOpts, + const EmitIndexOptions &RecordOpts); + + std::unique_ptr 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 +#include + +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) +/// / +/// / +/// /AA +/// /(records) +/// /AB +/// /(records) +/// /AC +/// /(records) +/// ... +/// / +/// /(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 &Str); + +/// Appends index directory and unit subdirectory to \p Buffer without erasing +/// it. +void appendUnitSubDir(llvm::SmallVectorImpl &Buffer); + +/// Appends index directory and record subdirectory to \p Buffer without erasing +/// it. +void appendRecordSubDir(llvm::SmallVectorImpl &Buffer); + +/// This is currently a placeholder - just appends \p UnitName to \p Buffer. +void appendUnitBucketDir(llvm::StringRef UnitName, + llvm::SmallVectorImpl &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 &Buffer); + +/// Appends \p RecordName to \p Buffer. +void appendRecordFilename(llvm::StringRef RecordName, + llvm::SmallVectorImpl &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 + +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 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 + createWithRecordFilename(StringRef RecordFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr + createWithFilePath(StringRef FilePath, std::string &Error); + static std::unique_ptr + createWithBuffer(std::unique_ptr 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 Checker, + llvm::function_ref 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 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 Receiver, + ArrayRef WhitelistDecls = None, + ArrayRef 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 Receiver); + + // TODO order +private: + friend class IndexBitstreamVisitor; + llvm::BumpPtrAllocator Allocator; + std::unique_ptr Buffer; + llvm::BitstreamCursor DeclCursor; + llvm::BitstreamCursor OccurCursor; + llvm::ArrayRef DeclOffsets; + const IndexRecordDecl **Decls; + + void setDeclOffsets(ArrayRef 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 &Record, StringRef Blob, + ArrayRef WhitelistDecls, + ArrayRef 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 +#include + +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 IndexForDecl; + std::vector Decls; + std::multimap> 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 Relations); + + bool isRecordEmpty() const { return Decls.empty(); } + + /// \returns filename of the index record created or None in case of failure. + llvm::Optional + writeToDisk(StringRef SourceFileName, const EmitIndexOptions &RecOpts, + const PrintingPolicy &Policy, + std::shared_ptr CGNameGen, + DiagnosticsEngine &Diag) const; + +private: + void getBitcodeRepresentation( + llvm::SmallString<512> &Buffer, const clang::PrintingPolicy &Policy, + std::shared_ptr 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 +#include + +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 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 &&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 &&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 + create(std::unique_ptr 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 + createWithUnitFilename(StringRef IndexUnitFilename, StringRef IndexRootDir, + std::string &Error); + + /// Creates IndexUnitReader from a file at \p IndexUnitPath. + static std::unique_ptr + 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> + 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 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 Receiver); + +private: + const std::unique_ptr 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 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 Modules; + StringRef ModuleNamesBuffer; + const llvm::sys::TimePoint<> ModTime; + +private: + /// Appends the full path related to \p PathIndex to \p PathBuf. + void constructFilePath(SmallVectorImpl &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 +#include +#include + +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 &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 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> Modules; + // Maps module to one-based index in the Modules container. + llvm::DenseMap ModToIdx; + + // Source file dependencies - mapping from UID to data. + llvm::DenseMap> SourceFiles; + // Source files that has a corresponding IndexRecord aren't written into + // index. + llvm::DenseSet SourceFileHasARecord; + std::vector> IncludedFiles; + + std::vector, std::array>> + IndexUnitDependencies; + llvm::DenseSet ASTFilesOfSeenIndexUnits; + + std::vector>> + IndexRecordDependencies; + + std::function &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 &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 +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 +#include + +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 Dirs; + std::vector FileBitPaths; + llvm::DenseMap FileToIndex; + +public: + PathStorage(llvm::StringRef workDir, llvm::StringRef sysrootPath); + + llvm::StringRef getPathsBuffer() const { return PathsBuf.str(); } + + llvm::ArrayRef 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 - -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 -createIndexingAction(std::shared_ptr DataConsumer, - IndexingOptions Opts, - std::unique_ptr 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 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 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 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 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 +#include +#include + +namespace clang { +namespace index { + +class IndexDataStore { +public: + ~IndexDataStore(); + + static std::unique_ptr create(StringRef IndexStorePath, + std::string &Error); + + StringRef getFilePath() const; + bool foreachUnitName(bool sorted, + llvm::function_ref 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 Events; + }; + typedef std::function 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 +static inline Ret functionPtrFromFunctionRef(void *ctx, Params... params) { + auto fn = (llvm::function_ref *)ctx; + return (*fn)(std::forward(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 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 getLineCol() { + unsigned line, col; + indexstore_occurrence_get_line_col(obj, &line, &col); + return std::make_pair(line, col); + } +}; + +class IndexStore; +typedef std::shared_ptr 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(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 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 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 &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 filter, + llvm::function_ref 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 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 symbolsFilter, + ArrayRef relatedSymbolsFilter, + llvm::function_ref receiver) { + llvm::SmallVector c_symbolsFilter; + c_symbolsFilter.reserve(symbolsFilter.size()); + for (IndexRecordSymbol sym : symbolsFilter) { + c_symbolsFilter.push_back(sym.obj); + } + llvm::SmallVector 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 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 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 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 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 +#include +#include +#include + +/** + * \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(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 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 Action) { + return llvm::make_unique( + llvm::make_unique( + 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 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(D)) { + if (FD->isDependentContext()) + return true; + if (writeFuncOrVarName(FD, FrontendBufOS)) + return true; + } else if (auto *VD = dyn_cast(D)) { + if (writeFuncOrVarName(VD, FrontendBufOS)) + return true; + } else if (auto *MD = dyn_cast(D)) { + MC->mangleObjCMethodNameWithoutSize(MD, OS); + return false; + } else if (auto *ID = dyn_cast(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 getAllManglings(const ObjCContainerDecl *OCD) { + StringRef ClassName; + if (const auto *OID = dyn_cast(OCD)) + ClassName = OID->getObjCRuntimeNameAsString(); + else if (const auto *OID = dyn_cast(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 getAllManglings(const Decl *D) { + if (const auto *OCD = dyn_cast(D)) + return getAllManglings(OCD); + + if (!(isa(D) || isa(D))) + return {}; + + const NamedDecl *ND = cast(D); + + ASTContext &Ctx = ND->getASTContext(); + std::unique_ptr M(Ctx.createMangleContext()); + + std::vector Manglings; + + auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { + auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, + /*IsCSSMethod=*/true); + auto CC = MD->getType()->getAs()->getCallConv(); + return CC == DefaultCC; + }; + + if (const auto *CD = dyn_cast_or_null(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() && 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(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(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(D)) + MC->mangleCXXCtor(CtorD, Ctor_Complete, OS); + else if (const auto *DtorD = dyn_cast(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(ND)) + MC->mangleCXXCtor(CD, static_cast(StructorType), FOS); + else if (const auto *DD = dyn_cast_or_null(ND)) + MC->mangleCXXDtor(DD, static_cast(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 - -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 Decls; - -public: - FileIndexRecord(FileID FID, bool IsSystem) : FID(FID), IsSystem(IsSystem) {} - - ArrayRef 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 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 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(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(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(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 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 indexMacrosCallback(IndexDataConsumer &Consumer, + const IndexingOptions &Opts) { + return llvm::make_unique( + std::make_shared(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 Ctx; + +public: + IndexASTConsumer(std::shared_ptr Ctx, CompilerInstance &CI) + : Ctx(Ctx) { + CI.getPreprocessor().addPPCallbacks( + llvm::make_unique(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 DataConsumer, + const IndexingOptions &Opts) + : DataConsumer(DataConsumer), + IndexCtx(new IndexingContext(Opts, *DataConsumer)) {} + +std::unique_ptr +GenerateIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + + IndexCtx->getDataConsumer().setPreprocessor(CI.getPreprocessorPtr()); + return llvm::make_unique(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 + +namespace clang { +namespace index { + +// Using distinct names for parameters in order to avoid confusion with name +// resolution in assert. +IndexActionWrapper::IndexActionWrapper( + std::unique_ptr IndexingActionParam, + std::unique_ptr WrappedActionParam) + : WrapperFrontendAction(std::move(WrappedActionParam)), + IndexingAction(std::move(IndexingActionParam)) { + assert(IndexingAction); +} + +std::unique_ptr +IndexActionWrapper::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::vector> 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(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 &Record); + +void emitRecordID(unsigned ID, const char *Name, llvm::BitstreamWriter &Stream, + llvm::SmallVectorImpl &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 &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 &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 + +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 class BitstreamVisitor { + SmallVector BlockStack; + +protected: + llvm::BitstreamCursor &Stream; + Optional 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 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> 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(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 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(this)->visitRecord( + BlockID, RecID, Record, Blob); + switch (Ret) { + case StreamVisit::Continue: + break; + + case StreamVisit::Skip: + if (Expected 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 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 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 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( + 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 +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 &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 CGNameGen; + if (RecordOpts.RecordSymbolCodeGenName) + CGNameGen = std::make_shared(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 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 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 Context; + +public: + IndexRecordingASTConsumer(std::shared_ptr 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 +EmitIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef) { + Context = std::make_shared(IndexOpts, CI, RecordOpts); + return llvm::make_unique(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 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 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 &Buffer) { + sys::path::append(Buffer, getIndexDir()); + sys::path::append(Buffer, subdir); +} + +void appendUnitBucketDir(StringRef UnitName, SmallVectorImpl &Buffer) { + sys::path::append(Buffer, UnitName); +} + +void appendUnitSubDir(SmallVectorImpl &Buffer) { + return appendIndexSubDir(getIndexUnitSubdir(), Buffer); +} + +void appendRecordSubDir(SmallVectorImpl &Buffer) { + return appendIndexSubDir(getIndexRecordSubdir(), Buffer); +} + +void appendRecordBucketDir(StringRef RecordName, + SmallVectorImpl &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 &Buffer) { + sys::path::append(Buffer, RecordName); +} + +void getUnitNameForOutputFile(StringRef FilePath, SmallVectorImpl &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 { + 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 &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::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::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::createWithBuffer(std::unique_ptr 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 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 Reader = + llvm::make_unique(); + Reader->Buffer = std::move(Buffer); + + IndexBitstreamVisitor BitVisitor(Stream, *Reader); + if (!BitVisitor.visit(Error)) + return nullptr; + + return Reader; +} + +bool IndexRecordReader::searchDecls( + llvm::function_ref Checker, + llvm::function_ref 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(); + *D = LocalD; + Decls[I] = D; + Receiver(D); + } + if (!Ret.ContinueSearch) + return false; + } + return true; +} + +bool IndexRecordReader::foreachDecl( + bool NoCache, function_ref 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 Receiver, + ArrayRef WhitelistDecls, + ArrayRef WhitelistRelated) { + class OccurBitVisitor : public BitstreamVisitor { + IndexRecordReader &Reader; + ArrayRef WhitelistDecls; + ArrayRef WhitelistRelated; + function_ref Receiver; + + public: + OccurBitVisitor(llvm::BitstreamCursor &Stream, IndexRecordReader &Reader, + ArrayRef WhitelistDecls, + ArrayRef WhitelistRelated, + function_ref Receiver) + : BitstreamVisitor(Stream), Reader(Reader), + WhitelistDecls(WhitelistDecls), WhitelistRelated(WhitelistRelated), + Receiver(std::move(Receiver)) {} + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + SmallVectorImpl &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 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 Offs) { + DeclOffsets = Offs; + Decls = Allocator.Allocate(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(); + 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 &Record, StringRef Blob, + ArrayRef WhitelistDecls, + ArrayRef WhitelistRelated, + IndexRecordOccurrence &RecOccur) { + auto isDeclIDContained = [](unsigned DeclID, + ArrayRef 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 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 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 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 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 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 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 REC_VERSION_BLOCK_ID_Abbrev() { + static const std::shared_ptr 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 DECLS_BLOCK_ID_Abbrev() { + static const std::shared_ptr 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 DECLOFFSETS_BLOCK_ID_Abbrev() { + static const std::shared_ptr CachedResult(new BitCodeAbbrev({ + BitCodeAbbrevOp(REC_DECLOFFSETS), + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32), // Number of Decls + BitCodeAbbrevOp(BitCodeAbbrevOp::Blob) // Offsets array + })); + + return CachedResult; +} + +static std::shared_ptr DECLOCCURRENCES_BLOCK_ID_Abbrev() { + static const std::shared_ptr 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 CGNameGen) const { + BitstreamWriter Stream(Buffer); + writeHeader(Stream); + writeBlockInfo(Stream); + writeVersionInfo(Stream); + + SmallVector 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(Declaration.D); + auto Info = getSymbolInfo(D); + assert(Info.Kind != SymbolKind::Unknown); + + std::string Name; + std::string CGName; + if (auto *ND = dyn_cast(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(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 +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 +IndexRecordWriter::writeToDisk(StringRef SourceFileName, + const EmitIndexOptions &RecordOpts, + const PrintingPolicy &Policy, + std::shared_ptr 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 { +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 Paths; + StringRef PathsBuffer; + size_t WorkDirOffset; + size_t WorkDirSize; + size_t OutputFileOffset; + size_t OutputFileSize; + size_t SysrootOffset; + size_t SysrootSize; + + std::vector 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 &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 &Record, StringRef Blob)> + BlockVisitorCallback; + +class IndexUnitBlockBitstreamVisitor + : public BitstreamVisitor { + 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 &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::create(std::unique_ptr 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 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( + 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::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::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> 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 &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 Receiver) { + store::SavedStreamPosition SavedDepPosition(DependenciesBlockCursor); + IndexUnitBlockBitstreamVisitor Visitor( + UNIT_DEPENDENCY, DependenciesBlockCursor, + [&](SmallVectorImpl &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 Receiver) { + store::SavedStreamPosition SavedIncPosition(IncludedFilesCursor); + IndexUnitBlockBitstreamVisitor Visitor( + UNIT_INCLUDE, IncludedFilesCursor, + [&](SmallVectorImpl &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 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 &&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 &&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 &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{ + UNIT_DEPENDENCY, + UNIT_DEPEND_KIND_FILE, + IsSystem, + static_cast(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>( + FileName, + { + UNIT_DEPENDENCY, UNIT_DEPEND_KIND_RECORD, IsSystem, + static_cast(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{ + UNIT_DEPENDENCY, UNIT_DEPEND_KIND_UNIT, IsSystem, + static_cast(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(PathStore.getPathIndex(IncludingFile) + 1), LineNo, + static_cast(PathStore.getPathIndex(IncludedFile) + 1)}); +}; + +static void writeBlockInfo(BitstreamWriter &Stream) { + SmallVector 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 UNIT_VERSION_BLOCK_ID_Abbrev() { + static const std::shared_ptr 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 UNIT_INFO_BLOCK_ID_Abbrev() { + static const std::shared_ptr 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(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 UNIT_DEPENDENCIES_BLOCK_ID_Abbrev() { + static const std::shared_ptr 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 UNIT_INCLUDES_BLOCK_ID_Abbrev() { + static const std::shared_ptr 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 UNIT_PATHS_BLOCK_ID_Paths_Abbrev() { + static const std::shared_ptr 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 +UNIT_PATHS_BLOCK_ID_Path_Buffers_Abbrev() { + static const std::shared_ptr 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 UNIT_MODULES_BLOCK_ID_Module_Abbrev() { + static const std::shared_ptr 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 +UNIT_MODULES_BLOCK_ID_Module_Buffer_Abbrev() { + static const std::shared_ptr 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 &Scratch) { + assert(mod); + writer::ModuleInfo info; + const std::string fullName = + static_cast(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 ModuleData = + [UnitModule]() -> llvm::Optional { + if (UnitModule) { + return ModuleIndexData{UnitModule->getFullModuleName(), + static_cast(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 +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 - -using namespace clang; -using namespace clang::index; - -bool IndexDataConsumer::handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, - ArrayRef 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 PP; - std::shared_ptr IndexCtx; - -public: - IndexASTConsumer(std::shared_ptr PP, - std::shared_ptr 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 IndexCtx; - -public: - IndexPPCallbacks(std::shared_ptr 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 DataConsumer; - std::shared_ptr IndexCtx; - - IndexActionBase(std::shared_ptr dataConsumer, - IndexingOptions Opts) - : DataConsumer(std::move(dataConsumer)), - IndexCtx(new IndexingContext(Opts, *DataConsumer)) {} - - std::unique_ptr - createIndexASTConsumer(CompilerInstance &CI) { - return llvm::make_unique(CI.getPreprocessorPtr(), - IndexCtx); - } - - std::unique_ptr createIndexPPCallbacks() { - return llvm::make_unique(IndexCtx); - } - - void finish() { - DataConsumer->finish(); - } -}; - -class IndexAction : public ASTFrontendAction, IndexActionBase { -public: - IndexAction(std::shared_ptr DataConsumer, - IndexingOptions Opts) - : IndexActionBase(std::move(DataConsumer), Opts) {} - -protected: - std::unique_ptr 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 WrappedAction, - std::shared_ptr DataConsumer, - IndexingOptions Opts) - : WrapperFrontendAction(std::move(WrappedAction)), - IndexActionBase(std::move(DataConsumer), Opts) {} - -protected: - std::unique_ptr CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) override { - auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); - if (!OtherConsumer) { - IndexActionFailed = true; - return nullptr; - } - - std::vector> Consumers; - Consumers.push_back(std::move(OtherConsumer)); - Consumers.push_back(createIndexASTConsumer(CI)); - return llvm::make_unique(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 -index::createIndexingAction(std::shared_ptr DataConsumer, - IndexingOptions Opts, - std::unique_ptr WrappedAction) { - if (WrappedAction) - return llvm::make_unique(std::move(WrappedAction), - std::move(DataConsumer), - Opts); - return llvm::make_unique(std::move(DataConsumer), Opts); -} - -static bool topLevelDeclVisitor(void *context, const Decl *D) { - IndexingContext &IndexCtx = *static_cast(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(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 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 -index::indexMacrosCallback(IndexDataConsumer &Consumer, IndexingOptions Opts) { - return llvm::make_unique( - std::make_shared(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(D)) + if (D->isImplicit() && !(isa(D) || isa(D))) return true; if (!isa(D) || shouldSkipNamelessDecl(cast(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 TheUnitEventHandlerData; + std::unique_ptr DirWatcher; + +public: + explicit IndexDataStoreImpl(StringRef indexStorePath) + : FilePath(indexStorePath) { + TheUnitEventHandlerData = std::make_shared(); + } + + StringRef getFilePath() const { return FilePath; } + bool foreachUnitName(bool sorted, + llvm::function_ref 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 receiver) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + std::vector 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 Events, + bool isInitial) { + SmallVector 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::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( + new IndexDataStore(new IndexDataStoreImpl(IndexStorePath))); +} + +#define IMPL static_cast(Impl) + +IndexDataStore::~IndexDataStore() { delete IMPL; } + +StringRef IndexDataStore::getFilePath() const { return IMPL->getFilePath(); } + +bool IndexDataStore::foreachUnitName( + bool sorted, llvm::function_ref 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 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: +// 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 +class Foo; // canonical decl + +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Def,RelChild | rel: 1 +template +class Foo {}; + +// CHECK: [[@LINE+2]]:8 | struct(Gen)/C++ | c:@N@crash2@ST>1#t>1#pT@Wrapper | Def,RelChild | rel: 1 +template