Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -232,6 +232,10 @@ def err_module_header_file_not_found : Error<"module header file '%0' not found">, DefaultFatal; +def remark_index_producing_module_file_data : Remark<"producing index data for " + "module file '%0'">, + InGroup; + def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " "(%3.%4)">; Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -428,6 +428,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 @@ -32,9 +32,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) {} }; // Encodes the kind of response file supported for a command invocation. Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -357,6 +357,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_mangled_name : Flag<["-"], "index-record-mangled-name">, Flags<[CC1Option]>, + HelpText<"Record the mangled name for symbols">; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<["-"], "ccc-">, Group, Flags<[Unsupported]>; Index: clang/include/clang/Frontend/FrontendAction.h =================================================================== --- clang/include/clang/Frontend/FrontendAction.h +++ clang/include/clang/Frontend/FrontendAction.h @@ -327,6 +327,28 @@ bool hasCodeCompletionSupport() const override; }; +/// Class for adding custom ASTConsumer to a FrontendAction. +/// Wraps the base action and forwards all method call except CreateASTConsumer. +class ASTConsumerInjector : public WrapperFrontendAction { + std::function(CompilerInstance &CI, + StringRef InFile)> + ASTConsumerFactory; + +public: + /// \p BaseActionInput is the base action to which all method calls are + /// forwarded. \p ASTConsumerFactory is factory to produce the custom + /// ASTConsumer in CreateASTConsumer method. + ASTConsumerInjector(std::unique_ptr BaseActionInput, + std::function( + CompilerInstance &CI, StringRef InFile)> + ASTConsumerFactory); + + /// Returns MultiplexConsumer consumer with first element produced by + /// ASTConsumerFactory, followed by the ASTConsumer from the base action. + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; +}; + } // end namespace clang #endif Index: clang/include/clang/Frontend/FrontendOptions.h =================================================================== --- clang/include/clang/Frontend/FrontendOptions.h +++ clang/include/clang/Frontend/FrontendOptions.h @@ -374,6 +374,10 @@ std::string MTMigrateDir; std::string ARCMTMigrateReportOut; + std::string IndexStorePath; + unsigned IndexIgnoreSystemSymbols : 1; + unsigned IndexRecordMangledName : 1; + /// The input files and their types. SmallVector Inputs; @@ -457,7 +461,9 @@ UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), ModulesEmbedAllFiles(false), - IncludeTimestamps(true), UseTemporary(true), TimeTraceGranularity(500) {} + IncludeTimestamps(true), UseTemporary(true), + IndexIgnoreSystemSymbols(false), IndexRecordMangledName(false), + TimeTraceGranularity(500) {} /// getInputKindForExtension - Return the appropriate input kind for a file /// extension. For example, "c" would return Language::C. 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/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/IndexingOptions.h =================================================================== --- clang/include/clang/Index/IndexingOptions.h +++ clang/include/clang/Index/IndexingOptions.h @@ -43,6 +43,8 @@ std::function ShouldTraverseDecl; }; +IndexingOptions parseIndexGenerationOptions(const FrontendOptions &FEOpts); + } // namespace index } // namespace clang 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/clang/IndexSerialization/DeclOccurrenceCollector.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/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/IndexingContext.h" +#include "clang/IndexSerialization/IndexRecordWriter.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 handleDeclOccurrence(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/IndexSerialization/EmitIndex.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/EmitIndex.h @@ -0,0 +1,103 @@ +//===--- EmitIndex.h - Index emitting AST Consumer & functions --*- 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_EMITINDEXASTCONSUMER_H +#define LLVM_CLANG_INDEX_EMITINDEXASTCONSUMER_H + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexingContext.h" +#include "clang/IndexSerialization/DeclOccurrenceCollector.h" +#include "clang/IndexSerialization/IndexRecordWriter.h" +#include "clang/IndexSerialization/IndexUnitWriter.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(); +}; + +class IndexRecordingASTConsumer final : public ASTConsumer { + ActionContext Context; + +public: + IndexRecordingASTConsumer(const IndexingOptions &IndexOpts, + CompilerInstance &CI, + const EmitIndexOptions &RecordOpts); + +protected: + void Initialize(ASTContext &ASTContextInst) override; + + bool HandleTopLevelDecl(DeclGroupRef DG) override; + + void HandleInterestingDecl(DeclGroupRef DG) override; + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override; + + void HandleTranslationUnit(ASTContext &Ctx) 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/IndexSerialization/EmitIndexOptions.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/EmitIndexOptions.h @@ -0,0 +1,48 @@ +//===--- EmitIndexOptions.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/IndexSerialization/IndexDataFormat.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/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/IndexSerialization/IndexRecordReader.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/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/IndexSerialization/IndexRecordWriter.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/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/AST/Mangle.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Index/IndexSymbol.h" +#include "clang/IndexSerialization/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 MangledNameGen, + DiagnosticsEngine &Diag) const; + +private: + void getBitcodeRepresentation( + llvm::SmallString<512> &Buffer, const clang::PrintingPolicy &Policy, + std::shared_ptr MangledNameGen) const; +}; + +} // end namespace index +} // end namespace clang + +#endif Index: clang/include/clang/IndexSerialization/IndexUnitReader.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/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/IndexSerialization/SerializablePathCollection.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/IndexSerialization/IndexUnitWriter.h =================================================================== --- /dev/null +++ clang/include/clang/IndexSerialization/IndexUnitWriter.h @@ -0,0 +1,176 @@ +//===--- 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/IndexSerialization/EmitIndexOptions.h" +#include "clang/IndexSerialization/SerializablePathCollection.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 SerializablePathCollection 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); + + /// \returns true in case writing failed. + 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/indexstore/Conversions.h =================================================================== --- /dev/null +++ clang/include/indexstore/Conversions.h @@ -0,0 +1,408 @@ +//===--- 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; + if (Roles & INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::NameReference; + + return SymbolRoles; +} + +inline indexstore_symbol_kind_t getIndexStoreKind(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: + // FIXME: Tweak SymbolKind/SymbolSubKind to separate + // generic-ish/language-specific-ish concepts. + // + // We use SymbolKind/SymbolSubKind + // downstream for Swift and this would help us avoid intrusions here or + // upstream. + // + // Separation of concepts for C++ and Objective-C could also be + // improved. Once that is done on SymbolKind/SymbolSubKind level we should + // update indexstore_symbol_kind_t too. + // + // Initial discussion here: + // https://github.com/apple/llvm-project/pull/1099 + case SymbolKind::TemplateTypeParm: + case SymbolKind::TemplateTemplateParm: + case SymbolKind::NonTypeTemplateParm: + 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; + case SymbolRole::NameReference: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE; + 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,515 @@ +//===--- 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,582 @@ +/*===-- 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 + +#if __has_attribute(flag_enum) +# define INDEXSTORE_FLAG_ENUM_ATTR __attribute__((flag_enum)) +#else +# define INDEXSTORE_FLAG_ENUM_ATTR +#endif + +#if __has_attribute(enum_extensibility) +# define INDEXSTORE_OPEN_ENUM_ATTR __attribute__((enum_extensibility(open))) +#else +# define INDEXSTORE_OPEN_ENUM_ATTR +#endif + +#define INDEXSTORE_OPTIONS_ATTRS INDEXSTORE_OPEN_ENUM_ATTR INDEXSTORE_FLAG_ENUM_ATTR + +#if __has_feature(objc_fixed_enum) +#ifdef __cplusplus +# define INDEXSTORE_OPTIONS(_type, _name) enum INDEXSTORE_OPTIONS_ATTRS _name : _type +#else +# define INDEXSTORE_OPTIONS(_type, _name) enum _name : _type; typedef enum _name _name; enum INDEXSTORE_OPTIONS_ATTRS _name : _type +#endif +#endif + +#ifndef INDEXSTORE_OPTIONS +# define INDEXSTORE_OPTIONS(_type, _name) typedef _type _name; enum INDEXSTORE_OPTIONS_ATTRS +#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 = 2, + INDEXSTORE_UNIT_EVENT_MODIFIED = 3, + INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 4, + INDEXSTORE_UNIT_EVENT_FAILURE = 5, +} 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; + +INDEXSTORE_OPTIONS(uint64_t, indexstore_symbol_property_t) { + 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, +}; + +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; + +INDEXSTORE_OPTIONS(uint64_t, indexstore_symbol_role_t) { + 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, + INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE = 1 << 20, + + // 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_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 indexstore_symbol_property_t +indexstore_symbol_get_properties(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_role_t +indexstore_symbol_get_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_role_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 indexstore_symbol_role_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 indexstore_symbol_role_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) add_subdirectory(IndexSerialization) if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -1421,7 +1421,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 @@ -74,6 +74,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. @@ -209,6 +211,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]; @@ -272,6 +275,23 @@ llvm::sys::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 << ' '; + llvm::sys::printArg(OS, "-index-store-path", Quote); + OS << ' '; + llvm::sys::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 @@ -4903,6 +4903,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_mangled_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-mangled-name"); + } + // Add preprocessing options like -I, -D, etc. if we are using the // preprocessor. // Index: clang/lib/Frontend/CMakeLists.txt =================================================================== --- clang/lib/Frontend/CMakeLists.txt +++ clang/lib/Frontend/CMakeLists.txt @@ -56,6 +56,7 @@ clangBasic clangDriver clangEdit + clangIndexSerialization clangLex clangParse clangSema Index: clang/lib/Frontend/CompilerInstance.cpp =================================================================== --- clang/lib/Frontend/CompilerInstance.cpp +++ clang/lib/Frontend/CompilerInstance.cpp @@ -28,6 +28,8 @@ #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Index/IndexingOptions.h" +#include "clang/IndexSerialization/EmitIndex.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" @@ -1164,8 +1166,20 @@ llvm::CrashRecoveryContext CRC; CRC.RunSafelyOnThread( [&]() { - GenerateModuleFromModuleMapAction Action; - Instance.ExecuteAction(Action); + std::unique_ptr Action( + new GenerateModuleFromModuleMapAction); + + const FrontendOptions &FEOpts = ImportingInstance.getFrontendOpts(); + if (!FEOpts.IndexStorePath.empty()) { + Action = std::make_unique( + std::move(Action), [](CompilerInstance &CI, StringRef) { + const FrontendOptions &FEOpts = CI.getFrontendOpts(); + return std::make_unique( + index::parseIndexGenerationOptions(FEOpts), CI, + index::parseEmitIndexOptions(FEOpts)); + }); + } + Instance.ExecuteAction(*Action); }, DesiredStackSize); Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -2015,6 +2015,10 @@ << "ARC migration" << "ObjC migration"; } + Opts.IndexStorePath = Args.getLastArgValue(OPT_index_store_path).str(); + Opts.IndexIgnoreSystemSymbols = Args.hasArg(OPT_index_ignore_system_symbols); + Opts.IndexRecordMangledName = Args.hasArg(OPT_index_record_mangled_name); + InputKind DashX(Language::Unknown); if (const Arg *A = Args.getLastArg(OPT_x)) { StringRef XValue = A->getValue(); Index: clang/lib/Frontend/FrontendAction.cpp =================================================================== --- clang/lib/Frontend/FrontendAction.cpp +++ clang/lib/Frontend/FrontendAction.cpp @@ -1118,3 +1118,18 @@ std::unique_ptr WrappedAction) : WrappedAction(std::move(WrappedAction)) {} +ASTConsumerInjector::ASTConsumerInjector( + std::unique_ptr BaseActionInput, + std::function(CompilerInstance &CI, + StringRef InFile)> + ASTConsumerFactory) + : WrapperFrontendAction(std::move(BaseActionInput)), + ASTConsumerFactory(ASTConsumerFactory) {} + +std::unique_ptr +ASTConsumerInjector::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::vector> Consumers; + Consumers.emplace_back(ASTConsumerFactory(CI, InFile)); + Consumers.emplace_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile)); + return std::make_unique(std::move(Consumers)); +} Index: clang/lib/FrontendTool/CMakeLists.txt =================================================================== --- clang/lib/FrontendTool/CMakeLists.txt +++ clang/lib/FrontendTool/CMakeLists.txt @@ -8,6 +8,8 @@ clangCodeGen clangDriver clangFrontend + clangIndex + clangIndexSerialization clangRewriteFrontend ) Index: clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp =================================================================== --- clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -22,6 +22,9 @@ #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/Utils.h" #include "clang/FrontendTool/Utils.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexingOptions.h" +#include "clang/IndexSerialization/EmitIndex.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/AnalyzerHelpFlags.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" @@ -175,6 +178,16 @@ } #endif + if (!FEOpts.IndexStorePath.empty()) { + Act = std::make_unique( + std::move(Act), [](CompilerInstance &CI, StringRef) { + const FrontendOptions &FEOpts = CI.getFrontendOpts(); + return std::make_unique( + index::parseIndexGenerationOptions(FEOpts), CI, + index::parseEmitIndexOptions(FEOpts)); + }); + } + // 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,21 +1,21 @@ set(LLVM_LINK_COMPONENTS + BitReader Core Support ) add_clang_library(clangIndex CommentToXML.cpp - FileIndexRecord.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 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,61 +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 "clang/Basic/SourceManager.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/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,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" #include "clang/AST/Attr.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,25 @@ +//===--- 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/IndexingOptions.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/IndexingAction.cpp =================================================================== --- clang/lib/Index/IndexingAction.cpp +++ clang/lib/Index/IndexingAction.cpp @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #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/Index/IndexingContext.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" Index: clang/lib/Index/IndexingContext.cpp =================================================================== --- clang/lib/Index/IndexingContext.cpp +++ clang/lib/Index/IndexingContext.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" +#include "clang/Index/IndexingContext.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" @@ -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 (SM.isInSystemHeader(Loc)) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; @@ -364,7 +359,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; @@ -374,12 +369,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 (SM.isInSystemHeader(Loc)) { 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 + clangIndexSerialization + ) Index: clang/lib/IndexDataStore/IndexDataStore.cpp =================================================================== --- /dev/null +++ clang/lib/IndexDataStore/IndexDataStore.cpp @@ -0,0 +1,228 @@ +//===--- 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/IndexSerialization/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.str()); + } + } + + 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); + } + }; + + auto DirWatcherOrError = + DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, waitInitialSync); + if (!DirWatcherOrError) { + Error = "failed to create directory watcher: " + + toString(DirWatcherOrError.takeError()); + return true; + } + DirWatcher = std::move(*DirWatcherOrError); + + 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/lib/IndexSerialization/BitstreamUtil.h =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/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/IndexSerialization/BitstreamUtil.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/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/IndexSerialization/BitstreamVisitor.h =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/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/IndexSerialization/CMakeLists.txt =================================================================== --- clang/lib/IndexSerialization/CMakeLists.txt +++ clang/lib/IndexSerialization/CMakeLists.txt @@ -1,6 +1,30 @@ +set(LLVM_LINK_COMPONENTS + BitReader + BitstreamReader + Core + Support + ) + add_clang_library(clangIndexSerialization + BitstreamUtil.cpp + DeclOccurrenceCollector.cpp + EmitIndex.cpp + EmitIndexOptions.cpp + IndexDataFormat.cpp + IndexRecordReader.cpp + IndexRecordWriter.cpp + IndexUnitReader.cpp + IndexUnitWriter.cpp SerializablePathCollection.cpp LINK_LIBS + clangAST clangBasic + clangFormat + clangFrontend + clangIndex + clangLex + clangRewrite + clangSerialization + clangToolingCore ) Index: clang/lib/IndexSerialization/DeclOccurrenceCollector.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/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/IndexSerialization/DeclOccurrenceCollector.h" + +namespace clang { +namespace index { + +bool DeclOccurrenceCollector::handleDeclOccurrence( + 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(SM.isInSystemHeader(Loc))}) + .first->second; + Rec.addDeclOccurence(Roles, FID, Offset, D, Relations); + return true; +} + +} // namespace index +} // namespace clang \ No newline at end of file Index: clang/lib/IndexSerialization/EmitIndex.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/EmitIndex.cpp @@ -0,0 +1,481 @@ +//===- EmitIndex.cpp - Index emitting AST consumer & functions ------------===// +// +// 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/IndexSerialization/EmitIndex.h" + +#include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Index/IndexPPCallbacks.h" +#include "clang/Index/IndexingOptions.h" +#include "clang/IndexSerialization/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 { + EmitIndexOptions RecordOpts; + IndexUnitWriter &UnitWriter; + SourceManager &SourceMgr; + +public: + IncludePPCallbacks(EmitIndexOptions recordOpts, IndexUnitWriter &UnitWriter, + SourceManager &SourceMgr) + : 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 && + SourceMgr.isInSystemHeader(InclusionDirectiveLoc)) + 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(std::make_unique( + 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) { + auto FEOrError = CI.getSourceManager().getFileManager().getFile(Filename); + if (FEOrError) { + Module *ModForHead = nullptr; + { + Module *Mod = getModBeingCompiled(CI); + if (Mod) { + ModForHead = findModuleForHeader(CI, *Mod, **FEOrError); + UnitWriter.addModule(ModForHead); + } + } + + UnitWriter.addSourceFile(*FEOrError, 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 MangledNameGen; + if (RecordOpts.RecordSymbolCodeGenName) + MangledNameGen = 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, MangledNameGen, CI.getDiagnostics()); + if (!RecordFilename) { + reportError(CI, + "failed to write record file for source file '%0' to disk", + FE->getName().str(), ""); + 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.getASTReader()) { + 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().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; + } + } +} + +IndexRecordingASTConsumer::IndexRecordingASTConsumer( + const IndexingOptions &IndexOpts, CompilerInstance &CI, + const EmitIndexOptions &RecordOpts) + : Context(IndexOpts, CI, RecordOpts) {} + +void IndexRecordingASTConsumer::Initialize(ASTContext &ASTContextInst) { + Context.Ctx.setASTContext(ASTContextInst); + Context.DeclsDataCollector.initialize(ASTContextInst); +} + +bool IndexRecordingASTConsumer::HandleTopLevelDecl(DeclGroupRef DG) { + return Context.Ctx.indexDeclGroupRef(DG); +} + +void IndexRecordingASTConsumer::HandleInterestingDecl(DeclGroupRef DG) { + // Ignore deserialized decls. +} + +void IndexRecordingASTConsumer::HandleTopLevelDeclInObjCContainer( + DeclGroupRef DG) { + Context.Ctx.indexDeclGroupRef(DG); +} + +void IndexRecordingASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { + 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.getASTReader()->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.getASTReader()->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.getASTReader()->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.getASTReader()->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.getASTReader(); + 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/IndexSerialization/EmitIndexOptions.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/EmitIndexOptions.cpp @@ -0,0 +1,41 @@ +//===--- EmitIndexOptions.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/IndexSerialization/EmitIndexOptions.h" +#include "clang/IndexSerialization/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.IndexRecordMangledName); +} + +} // namespace index +} // namespace clang Index: clang/lib/IndexSerialization/IndexDataFormat.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/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/IndexSerialization/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/IndexSerialization/IndexRecordReader.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/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/IndexSerialization/IndexRecordReader.h" +#include "BitstreamVisitor.h" +#include "indexstore/Conversions.h" +#include "clang/IndexSerialization/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 = + std::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/IndexSerialization/IndexRecordWriter.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/IndexRecordWriter.cpp @@ -0,0 +1,338 @@ +//===--- 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/IndexSerialization/IndexRecordWriter.h" +#include "BitstreamUtil.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/USRGeneration.h" +#include "clang/IndexSerialization/IndexDataFormat.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileUtilities.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 MangledNameGen) 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 (MangledNameGen) { + llvm::raw_string_ostream OS(CGName); + MangledNameGen->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 MangledNameGen, + DiagnosticsEngine &Diag) const { + + llvm::SmallString<512> IndexRecordBuffer; + getBitcodeRepresentation(IndexRecordBuffer, Policy, MangledNameGen); + + 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().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()); + + llvm::Error Err = + llvm::writeFileAtomically(TempPath, RecordPath, IndexRecordBuffer); + if (Err) { + // FIXME: This assumes getDiagnosticClient().BeginSourceFile() has been + // called. + reportError(Diag, "failed writing record '%0': %1", RecordFilename, + llvm::toString(std::move(Err))); + 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/IndexSerialization/IndexUnitReader.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/IndexUnitReader.cpp @@ -0,0 +1,475 @@ +//===--- 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/IndexSerialization/IndexUnitReader.h" +#include "BitstreamVisitor.h" +#include "clang/IndexSerialization/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/IndexSerialization/SerializablePathCollection.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; + PathPool::RootDirKind Kind = (PathPool::RootDirKind)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( + PathPool::DirPath(Kind, + StringPool::StringOffSz(DirOffset, DirSize)), + StringPool::StringOffSz(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 std::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(static_cast(PathIndex) < Paths.size()); + const PathPool::FilePath &Path = Paths[PathIndex]; + StringRef Prefix; + switch (Path.Dir.Root) { + case PathPool::RootDirKind::Regular: + break; + case PathPool::RootDirKind::CurrentWorkDir: + Prefix = getWorkingDirectory(); + break; + case PathPool::RootDirKind::SysRoot: + Prefix = getSysrootPath(); + break; + } + PathBuf.append(Prefix.begin(), Prefix.end()); + sys::path::append( + PathBuf, PathsBuffer.substr(Path.Dir.Path.Offset, Path.Dir.Path.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/IndexSerialization/IndexUnitWriter.cpp =================================================================== --- /dev/null +++ clang/lib/IndexSerialization/IndexUnitWriter.cpp @@ -0,0 +1,579 @@ +//===--- 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/IndexSerialization/IndexUnitWriter.h" +#include "BitstreamUtil.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/IndexSerialization/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/FileUtilities.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.str().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, OutputFile), + 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, + File ? static_cast(PathStore.tryStoreFilePath(*File) + 1) : 0, + 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.str(), + { + UNIT_DEPENDENCY, UNIT_DEPEND_KIND_RECORD, IsSystem, + SourceFile ? static_cast( + PathStore.tryStoreFilePath(*SourceFile) + 1) + : 0, + 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, + ASTFile ? static_cast( + PathStore.tryStoreFilePath(*ASTFile) + 1) + : 0, + 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. + IncludingFile ? static_cast( + PathStore.tryStoreFilePath(*IncludingFile) + 1) + : 0, + LineNo, + IncludedFile ? static_cast( + PathStore.tryStoreFilePath(*IncludedFile) + 1) + : 0}); +}; + +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()); + + llvm::Error Err = + llvm::writeFileAtomically(TempPath, UnitPath, BitcodeRepresentation); + if (Err) { + Error = llvm::toString(std::move(Err)); + return true; + } + return false; +} + +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.WorkDirPath.Offset, PathStore.WorkDirPath.Size, + PathStore.OutputFilePath.Offset, PathStore.OutputFilePath.Size, + PathStore.SysRootPath.Offset, PathStore.SysRootPath.Size, + MainFile + ? static_cast(PathStore.tryStoreFilePath(*MainFile) + 1) + : 0, + 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 &FilePath : PathStore.getFilePaths()) { + const uint64_t Record[6]{UNIT_PATH, + static_cast(FilePath.Dir.Root), + FilePath.Dir.Path.Offset, + FilePath.Dir.Path.Size, + FilePath.Filename.Offset, + FilePath.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/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