| //===-- core_main.cpp - Core Index Tool testbed ---------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "JSONAggregation.h" |
| #include "indexstore/IndexStoreCXX.h" |
| #include "clang/DirectoryWatcher/DirectoryWatcher.h" |
| #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" |
| #include "clang/Frontend/ASTUnit.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Frontend/CompilerInvocation.h" |
| #include "clang/Frontend/FrontendAction.h" |
| #include "clang/Index/IndexingAction.h" |
| #include "clang/Index/IndexDataConsumer.h" |
| #include "clang/Index/IndexDataStoreSymbolUtils.h" |
| #include "clang/Index/IndexRecordReader.h" |
| #include "clang/Index/IndexUnitReader.h" |
| #include "clang/Index/USRGeneration.h" |
| #include "clang/Index/CodegenNameGenerator.h" |
| #include "clang/Serialization/ASTReader.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/Signals.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Support/PrettyStackTrace.h" |
| |
| #define HAVE_CORESERVICES 0 |
| |
| #if defined(__has_include) |
| #if __has_include(<CoreServices/CoreServices.h>) |
| |
| #include <CoreServices/CoreServices.h> |
| #undef HAVE_CORESERVICES |
| #define HAVE_CORESERVICES 1 |
| |
| #endif |
| #endif |
| |
| using namespace clang; |
| using namespace clang::index; |
| using namespace llvm; |
| |
| extern "C" int indextest_core_main(int argc, const char **argv); |
| |
| namespace { |
| |
| enum class ActionType { |
| None, |
| PrintSourceSymbols, |
| PrintRecord, |
| PrintUnit, |
| PrintStoreFormatVersion, |
| AggregateAsJSON, |
| WatchDir, |
| }; |
| |
| namespace options { |
| |
| static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); |
| |
| static cl::opt<ActionType> |
| Action(cl::desc("Action:"), cl::init(ActionType::None), |
| cl::values( |
| clEnumValN(ActionType::PrintSourceSymbols, |
| "print-source-symbols", "Print symbols from source"), |
| clEnumValN(ActionType::PrintRecord, |
| "print-record", "Print record info"), |
| clEnumValN(ActionType::PrintUnit, |
| "print-unit", "Print unit info"), |
| clEnumValN(ActionType::PrintStoreFormatVersion, |
| "print-store-format-version", "Print store format version"), |
| clEnumValN(ActionType::AggregateAsJSON, |
| "aggregate-json", "Aggregate index data in JSON format"), |
| clEnumValN(ActionType::WatchDir, |
| "watch-dir", "Watch directory for file events")), |
| cl::cat(IndexTestCoreCategory)); |
| |
| static cl::opt<std::string> |
| OutputFile("o", cl::desc("output file"), |
| cl::cat(IndexTestCoreCategory)); |
| |
| static cl::list<std::string> |
| InputFiles(cl::Positional, cl::desc("<filename>...")); |
| |
| static cl::extrahelp MoreHelp( |
| "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler " |
| "invocation\n" |
| ); |
| |
| static cl::opt<bool> |
| DumpModuleImports("dump-imported-module-files", |
| cl::desc("Print symbols and input files from imported modules")); |
| |
| static cl::opt<bool> |
| IncludeLocals("include-locals", cl::desc("Print local symbols")); |
| |
| static cl::opt<std::string> |
| ModuleFilePath("module-file", |
| cl::desc("Path to module file to print symbols from")); |
| static cl::opt<std::string> |
| ModuleFormat("fmodule-format", cl::init("raw"), |
| cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'")); |
| |
| static cl::opt<std::string> |
| FilePathAndRange("filepath", |
| cl::desc("File path that can optionally include a line range")); |
| |
| } |
| } // anonymous namespace |
| |
| static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS); |
| static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, |
| raw_ostream &OS); |
| |
| namespace { |
| |
| class PrintIndexDataConsumer : public IndexDataConsumer { |
| raw_ostream &OS; |
| std::unique_ptr<CodegenNameGenerator> CGNameGen; |
| |
| public: |
| PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { |
| } |
| |
| void initialize(ASTContext &Ctx) override { |
| CGNameGen.reset(new CodegenNameGenerator(Ctx)); |
| } |
| |
| bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, |
| ArrayRef<SymbolRelation> Relations, |
| FileID FID, unsigned Offset, |
| ASTNodeInfo ASTNode) override { |
| ASTContext &Ctx = D->getASTContext(); |
| SourceManager &SM = Ctx.getSourceManager(); |
| |
| unsigned Line = SM.getLineNumber(FID, Offset); |
| unsigned Col = SM.getColumnNumber(FID, Offset); |
| OS << Line << ':' << Col << " | "; |
| |
| printSymbolInfo(getSymbolInfo(D), OS); |
| OS << " | "; |
| |
| printSymbolNameAndUSR(D, Ctx, OS); |
| OS << " | "; |
| |
| if (CGNameGen->writeName(D, OS)) |
| OS << "<no-cgname>"; |
| OS << " | "; |
| |
| printSymbolRoles(Roles, OS); |
| OS << " | "; |
| |
| OS << "rel: " << Relations.size() << '\n'; |
| |
| for (auto &SymRel : Relations) { |
| OS << '\t'; |
| printSymbolRoles(SymRel.Roles, OS); |
| OS << " | "; |
| printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS); |
| OS << '\n'; |
| } |
| |
| return true; |
| } |
| |
| bool handleModuleOccurence(const ImportDecl *ImportD, SymbolRoleSet Roles, |
| FileID FID, unsigned Offset) override { |
| ASTContext &Ctx = ImportD->getASTContext(); |
| SourceManager &SM = Ctx.getSourceManager(); |
| |
| unsigned Line = SM.getLineNumber(FID, Offset); |
| unsigned Col = SM.getColumnNumber(FID, Offset); |
| OS << Line << ':' << Col << " | "; |
| |
| printSymbolInfo(getSymbolInfo(ImportD), OS); |
| OS << " | "; |
| |
| OS << ImportD->getImportedModule()->getFullModuleName() << " | "; |
| |
| printSymbolRoles(Roles, OS); |
| OS << " |\n"; |
| |
| return true; |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| //===----------------------------------------------------------------------===// |
| // Print Source Symbols |
| //===----------------------------------------------------------------------===// |
| |
| static void dumpModuleFileInputs(serialization::ModuleFile &Mod, |
| ASTReader &Reader, |
| raw_ostream &OS) { |
| OS << "---- Module Inputs ----\n"; |
| Reader.visitInputFiles(Mod, /*IncludeSystem=*/true, /*Complain=*/false, |
| [&](const serialization::InputFile &IF, bool isSystem) { |
| OS << (isSystem ? "system" : "user") << " | "; |
| OS << IF.getFile()->getName() << '\n'; |
| }); |
| } |
| |
| static bool printSourceSymbols(ArrayRef<const char *> Args, |
| bool dumpModuleImports, |
| bool indexLocals) { |
| SmallVector<const char *, 4> ArgsWithProgName; |
| ArgsWithProgName.push_back("clang"); |
| ArgsWithProgName.append(Args.begin(), Args.end()); |
| IntrusiveRefCntPtr<DiagnosticsEngine> |
| Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); |
| auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags); |
| if (!CInvok) |
| return true; |
| |
| raw_ostream &OS = outs(); |
| auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS); |
| IndexingOptions IndexOpts; |
| IndexOpts.IndexFunctionLocals = indexLocals; |
| std::unique_ptr<FrontendAction> IndexAction; |
| IndexAction = createIndexingAction(DataConsumer, IndexOpts, |
| /*WrappedAction=*/nullptr); |
| |
| auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); |
| std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( |
| std::move(CInvok), PCHContainerOps, Diags, IndexAction.get())); |
| |
| if (!Unit) |
| return true; |
| |
| if (dumpModuleImports) { |
| if (auto Reader = Unit->getASTReader()) { |
| Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool { |
| OS << "==== Module " << Mod.ModuleName << " ====\n"; |
| indexModuleFile(Mod, *Reader, DataConsumer, IndexOpts); |
| dumpModuleFileInputs(Mod, *Reader, OS); |
| return true; // skip module dependencies. |
| }); |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool printSourceSymbolsFromModule(StringRef modulePath, |
| StringRef format) { |
| FileSystemOptions FileSystemOpts; |
| auto pchContOps = std::make_shared<PCHContainerOperations>(); |
| // Register the support for object-file-wrapped Clang modules. |
| pchContOps->registerReader(llvm::make_unique<ObjectFilePCHContainerReader>()); |
| auto pchRdr = pchContOps->getReaderOrNull(format); |
| if (!pchRdr) { |
| errs() << "unknown module format: " << format << '\n'; |
| return true; |
| } |
| |
| IntrusiveRefCntPtr<DiagnosticsEngine> Diags = |
| CompilerInstance::createDiagnostics(new DiagnosticOptions()); |
| std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile( |
| modulePath, *pchRdr, Diags, |
| FileSystemOpts, /*UseDebugInfo=*/false, |
| /*OnlyLocalDecls=*/true, None, |
| /*CaptureDiagnostics=*/false, |
| /*AllowPCHWithCompilerErrors=*/true, |
| /*UserFilesAreVolatile=*/false); |
| if (!AU) { |
| errs() << "failed to create TU for: " << modulePath << '\n'; |
| return true; |
| } |
| |
| auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(outs()); |
| IndexingOptions IndexOpts; |
| indexASTUnit(*AU, DataConsumer, IndexOpts); |
| |
| return false; |
| } |
| |
| #if INDEXSTORE_HAS_BLOCKS |
| |
| //===----------------------------------------------------------------------===// |
| // Print Record |
| //===----------------------------------------------------------------------===// |
| |
| static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS); |
| static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS); |
| |
| static int printRecord(StringRef Filename, raw_ostream &OS) { |
| std::string Error; |
| auto Reader = IndexRecordReader::createWithFilePath(Filename, Error); |
| if (!Reader) { |
| errs() << Error << '\n'; |
| return true; |
| } |
| |
| Reader->foreachDecl(/*noCache=*/true, [&](const IndexRecordDecl *Rec)->bool { |
| printSymbol(*Rec, OS); |
| return true; |
| }); |
| OS << "------------\n"; |
| Reader->foreachOccurrence([&](const IndexRecordOccurrence &Rec)->bool { |
| printSymbol(Rec, OS); |
| return true; |
| }); |
| |
| return false; |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // Print Store Records |
| //===----------------------------------------------------------------------===// |
| |
| static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS); |
| static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS); |
| |
| static bool printStoreRecord(indexstore::IndexStore &Store, StringRef RecName, |
| StringRef FilePath, raw_ostream &OS) { |
| std::string Error; |
| indexstore::IndexRecordReader Reader(Store, RecName, Error); |
| if (!Reader) { |
| errs() << "error loading record: " << Error << "\n"; |
| return true; |
| } |
| |
| StringRef Filename = sys::path::filename(FilePath); |
| OS << Filename << '\n'; |
| OS << "------------\n"; |
| Reader.foreachSymbol(/*noCache=*/true, [&](indexstore::IndexRecordSymbol Sym) -> bool { |
| printSymbol(Sym, OS); |
| return true; |
| }); |
| OS << "------------\n"; |
| Reader.foreachOccurrence([&](indexstore::IndexRecordOccurrence Occur)->bool { |
| printSymbol(Occur, OS); |
| return true; |
| }); |
| |
| return false; |
| } |
| |
| static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { |
| std::string Error; |
| indexstore::IndexStore Store(StorePath, Error); |
| if (!Store) { |
| errs() << "error loading store: " << Error << "\n"; |
| return 1; |
| } |
| |
| bool Success = Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { |
| indexstore::IndexUnitReader Reader(Store, UnitName, Error); |
| if (!Reader) { |
| errs() << "error loading unit: " << Error << "\n"; |
| return false; |
| } |
| return Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { |
| if (Dep.getKind() == indexstore::IndexUnitDependency::DependencyKind::Record) { |
| bool Err = printStoreRecord(Store, Dep.getName(), Dep.getFilePath(), OS); |
| OS << '\n'; |
| return !Err; |
| } |
| return true; |
| }); |
| }); |
| |
| return !Success; |
| } |
| |
| static std::string findRecordNameForFile(indexstore::IndexStore &store, |
| StringRef filePath) { |
| std::string recName; |
| store.foreachUnit(/*sorted=*/false, [&](StringRef unitName) -> bool { |
| std::string error; |
| indexstore::IndexUnitReader Reader(store, unitName, error); |
| if (!Reader) { |
| errs() << "error loading unit: " << error << "\n"; |
| return false; |
| } |
| Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { |
| if (Dep.getKind() == indexstore::IndexUnitDependency::DependencyKind::Record) { |
| if (Dep.getFilePath() == filePath) { |
| recName = Dep.getName(); |
| return false; |
| } |
| return true; |
| } |
| return true; |
| }); |
| return true; |
| }); |
| return recName; |
| } |
| |
| static int printStoreFileRecord(StringRef storePath, StringRef filePath, |
| Optional<unsigned> lineStart, unsigned lineCount, |
| raw_ostream &OS) { |
| std::string error; |
| indexstore::IndexStore store(storePath, error); |
| if (!store) { |
| errs() << "error loading store: " << error << "\n"; |
| return 1; |
| } |
| |
| std::string recName = findRecordNameForFile(store, filePath); |
| if (recName.empty()) { |
| errs() << "could not find record for '" << filePath << "'\n"; |
| return 1; |
| } |
| |
| if (!lineStart.hasValue()) |
| return printStoreRecord(store, recName, filePath, OS); |
| |
| indexstore::IndexRecordReader Reader(store, recName, error); |
| if (!Reader) { |
| errs() << "error loading record: " << error << "\n"; |
| return 1; |
| } |
| |
| Reader.foreachOccurrenceInLineRange(*lineStart, lineCount, [&](indexstore::IndexRecordOccurrence Occur)->bool { |
| printSymbol(Occur, OS); |
| return true; |
| }); |
| |
| return 0; |
| } |
| |
| |
| //===----------------------------------------------------------------------===// |
| // Print Unit |
| //===----------------------------------------------------------------------===// |
| |
| static int printUnit(StringRef Filename, raw_ostream &OS) { |
| std::string Error; |
| auto Reader = IndexUnitReader::createWithFilePath(Filename, Error); |
| if (!Reader) { |
| errs() << Error << '\n'; |
| return true; |
| } |
| |
| OS << "provider: " << Reader->getProviderIdentifier() << '-' << Reader->getProviderVersion() << '\n'; |
| OS << "is-system: " << Reader->isSystemUnit() << '\n'; |
| OS << "is-module: " << Reader->isModuleUnit() << '\n'; |
| OS << "module-name: " << (Reader->getModuleName().empty() ? "<none>" : Reader->getModuleName()) << '\n'; |
| OS << "has-main: " << Reader->hasMainFile() << '\n'; |
| OS << "main-path: " << Reader->getMainFilePath() << '\n'; |
| OS << "work-dir: " << Reader->getWorkingDirectory() << '\n'; |
| OS << "out-file: " << Reader->getOutputFile() << '\n'; |
| OS << "target: " << Reader->getTarget() << '\n'; |
| OS << "is-debug: " << Reader->isDebugCompilation() << '\n'; |
| OS << "DEPEND START\n"; |
| unsigned NumDepends = 0; |
| Reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &Dep) -> bool { |
| switch (Dep.Kind) { |
| case IndexUnitReader::DependencyKind::Unit: |
| OS << "Unit | "; break; |
| case IndexUnitReader::DependencyKind::Record: |
| OS << "Record | "; break; |
| case IndexUnitReader::DependencyKind::File: |
| OS << "File | "; break; |
| } |
| OS << (Dep.IsSystem ? "system" : "user"); |
| OS << " | "; |
| if (!Dep.ModuleName.empty()) |
| OS << Dep.ModuleName << " | "; |
| OS << Dep.FilePath << " | " << Dep.UnitOrRecordName << " | "; |
| OS << Dep.ModTime << " | " << Dep.FileSize << '\n'; |
| ++NumDepends; |
| return true; |
| }); |
| OS << "DEPEND END (" << NumDepends << ")\n"; |
| OS << "INCLUDE START\n"; |
| unsigned NumIncludes = 0; |
| Reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &Inc) -> bool { |
| OS << Inc.SourcePath << ":" << Inc.SourceLine << " | "; |
| OS << Inc.TargetPath << '\n'; |
| ++NumIncludes; |
| return true; |
| }); |
| OS << "INCLUDE END (" << NumIncludes << ")\n"; |
| |
| return false; |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // Print Store Units |
| //===----------------------------------------------------------------------===// |
| |
| static bool printStoreUnit(indexstore::IndexStore &Store, StringRef UnitName, |
| raw_ostream &OS) { |
| std::string Error; |
| indexstore::IndexUnitReader Reader(Store, UnitName, Error); |
| if (!Reader) { |
| errs() << "error loading unit: " << Error << "\n"; |
| return true; |
| } |
| |
| OS << "provider: " << Reader.getProviderIdentifier() << '-' << Reader.getProviderVersion() << '\n'; |
| OS << "is-system: " << Reader.isSystemUnit() << '\n'; |
| OS << "is-module: " << Reader.isModuleUnit() << '\n'; |
| OS << "module-name: " << (Reader.getModuleName().empty() ? "<none>" : Reader.getModuleName()) << '\n'; |
| OS << "has-main: " << Reader.hasMainFile() << '\n'; |
| OS << "main-path: " << Reader.getMainFilePath() << '\n'; |
| OS << "work-dir: " << Reader.getWorkingDirectory() << '\n'; |
| OS << "out-file: " << Reader.getOutputFile() << '\n'; |
| OS << "target: " << Reader.getTarget() << '\n'; |
| OS << "is-debug: " << Reader.isDebugCompilation() << '\n'; |
| OS << "DEPEND START\n"; |
| unsigned NumDepends = 0; |
| Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { |
| switch (Dep.getKind()) { |
| case indexstore::IndexUnitDependency::DependencyKind::Unit: |
| OS << "Unit | "; break; |
| case indexstore::IndexUnitDependency::DependencyKind::Record: |
| OS << "Record | "; break; |
| case indexstore::IndexUnitDependency::DependencyKind::File: |
| OS << "File | "; break; |
| } |
| OS << (Dep.isSystem() ? "system" : "user"); |
| OS << " | "; |
| if (!Dep.getModuleName().empty()) |
| OS << Dep.getModuleName() << " | "; |
| OS << Dep.getFilePath() << " | " << Dep.getName() << " | "; |
| OS << Dep.getModificationTime() << '\n'; |
| ++NumDepends; |
| return true; |
| }); |
| OS << "DEPEND END (" << NumDepends << ")\n"; |
| OS << "INCLUDE START\n"; |
| unsigned NumIncludes = 0; |
| Reader.foreachInclude([&](indexstore::IndexUnitInclude Inc) -> bool { |
| OS << Inc.getSourcePath() << ":" << Inc.getSourceLine() << " | "; |
| OS << Inc.getTargetPath() << '\n'; |
| ++NumIncludes; |
| return true; |
| }); |
| OS << "INCLUDE END (" << NumIncludes << ")\n"; |
| |
| return false; |
| } |
| |
| static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { |
| std::string Error; |
| indexstore::IndexStore Store(StorePath, Error); |
| if (!Store) { |
| errs() << "error loading store: " << Error << "\n"; |
| return 1; |
| } |
| |
| bool Success = Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { |
| OS << UnitName << '\n'; |
| OS << "--------\n"; |
| bool err = printStoreUnit(Store, UnitName, OS); |
| OS << '\n'; |
| return !err; |
| }); |
| |
| return !Success; |
| } |
| |
| |
| #else |
| |
| static int printUnit(StringRef Filename, raw_ostream &OS) { |
| return 1; |
| } |
| |
| static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { |
| return 1; |
| } |
| |
| static int printStoreFileRecord(StringRef storePath, StringRef filePath, |
| Optional<unsigned> lineStart, unsigned lineCount, |
| raw_ostream &OS) { |
| return 1; |
| } |
| |
| #endif |
| |
| //===----------------------------------------------------------------------===// |
| // Helper Utils |
| //===----------------------------------------------------------------------===// |
| |
| static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { |
| OS << getSymbolKindString(SymInfo.Kind); |
| if (SymInfo.SubKind != SymbolSubKind::None) |
| OS << '/' << getSymbolSubKindString(SymInfo.SubKind); |
| if (SymInfo.Properties) { |
| OS << '('; |
| printSymbolProperties(SymInfo.Properties, OS); |
| OS << ')'; |
| } |
| OS << '/' << getSymbolLanguageString(SymInfo.Lang); |
| } |
| |
| static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, |
| raw_ostream &OS) { |
| if (printSymbolName(D, Ctx.getLangOpts(), OS)) { |
| OS << "<no-name>"; |
| } |
| OS << " | "; |
| |
| SmallString<256> USRBuf; |
| if (generateUSRForDecl(D, USRBuf)) { |
| OS << "<no-usr>"; |
| } else { |
| OS << USRBuf; |
| } |
| } |
| |
| #if INDEXSTORE_HAS_BLOCKS |
| |
| static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS) { |
| printSymbolInfo(Rec.SymInfo, OS); |
| OS << " | "; |
| |
| if (Rec.Name.empty()) |
| OS << "<no-name>"; |
| else |
| OS << Rec.Name; |
| OS << " | "; |
| |
| if (Rec.USR.empty()) |
| OS << "<no-usr>"; |
| else |
| OS << Rec.USR; |
| OS << " | "; |
| |
| if (Rec.CodeGenName.empty()) |
| OS << "<no-cgname>"; |
| else |
| OS << Rec.CodeGenName; |
| OS << " | "; |
| |
| printSymbolRoles(Rec.Roles, OS); |
| OS << " - "; |
| printSymbolRoles(Rec.RelatedRoles, OS); |
| OS << '\n'; |
| } |
| |
| static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS) { |
| OS << Rec.Line << ':' << Rec.Column << " | "; |
| printSymbolInfo(Rec.Dcl->SymInfo, OS); |
| OS << " | "; |
| |
| if (Rec.Dcl->USR.empty()) |
| OS << "<no-usr>"; |
| else |
| OS << Rec.Dcl->USR; |
| OS << " | "; |
| |
| printSymbolRoles(Rec.Roles, OS); |
| OS << " | "; |
| OS << "rel: " << Rec.Relations.size() << '\n'; |
| for (auto &Rel : Rec.Relations) { |
| OS << '\t'; |
| printSymbolRoles(Rel.Roles, OS); |
| OS << " | "; |
| if (Rel.Dcl->USR.empty()) |
| OS << "<no-usr>"; |
| else |
| OS << Rel.Dcl->USR; |
| OS << '\n'; |
| } |
| } |
| |
| static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS) { |
| SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), |
| getSymbolSubKind(Sym.getSubKind()), |
| SymbolPropertySet(Sym.getProperties()), |
| getSymbolLanguage(Sym.getLanguage())}; |
| |
| printSymbolInfo(SymInfo, OS); |
| OS << " | "; |
| |
| if (Sym.getName().empty()) |
| OS << "<no-name>"; |
| else |
| OS << Sym.getName(); |
| OS << " | "; |
| |
| if (Sym.getUSR().empty()) |
| OS << "<no-usr>"; |
| else |
| OS << Sym.getUSR(); |
| OS << " | "; |
| |
| if (Sym.getCodegenName().empty()) |
| OS << "<no-cgname>"; |
| else |
| OS << Sym.getCodegenName(); |
| OS << " | "; |
| |
| printSymbolRoles(Sym.getRoles(), OS); |
| OS << " - "; |
| printSymbolRoles(Sym.getRelatedRoles(), OS); |
| OS << '\n'; |
| } |
| |
| static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS) { |
| OS << Occur.getLineCol().first << ':' << Occur.getLineCol().second << " | "; |
| auto Sym = Occur.getSymbol(); |
| SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), |
| getSymbolSubKind(Sym.getSubKind()), |
| SymbolPropertySet(Sym.getProperties()), |
| getSymbolLanguage(Sym.getLanguage())}; |
| |
| printSymbolInfo(SymInfo, OS); |
| OS << " | "; |
| |
| if (Sym.getUSR().empty()) |
| OS << "<no-usr>"; |
| else |
| OS << Sym.getUSR(); |
| OS << " | "; |
| |
| unsigned NumRelations = 0; |
| Occur.foreachRelation([&](indexstore::IndexSymbolRelation) { |
| ++NumRelations; |
| return true; |
| }); |
| |
| printSymbolRoles(Occur.getRoles(), OS); |
| OS << " | "; |
| OS << "rel: " << NumRelations << '\n'; |
| Occur.foreachRelation([&](indexstore::IndexSymbolRelation Rel) { |
| OS << '\t'; |
| printSymbolRoles(Rel.getRoles(), OS); |
| OS << " | "; |
| auto Sym = Rel.getSymbol(); |
| if (Sym.getUSR().empty()) |
| OS << "<no-usr>"; |
| else |
| OS << Sym.getUSR(); |
| OS << '\n'; |
| return true; |
| }); |
| } |
| |
| #else |
| |
| static int printRecord(StringRef Filename, raw_ostream &OS) { |
| return 1; |
| } |
| static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { |
| return 1; |
| } |
| |
| #endif |
| |
| static int watchDirectory(StringRef dirPath) { |
| raw_ostream &OS = outs(); |
| auto receiver = [&](ArrayRef<DirectoryWatcher::Event> Events, bool isInitial) { |
| for (auto evt : Events) { |
| switch (evt.Kind) { |
| case DirectoryWatcher::EventKind::Added: |
| OS << "added: "; break; |
| case DirectoryWatcher::EventKind::Modified: |
| OS << "modified: "; break; |
| case DirectoryWatcher::EventKind::Removed: |
| OS << "removed: "; break; |
| case DirectoryWatcher::EventKind::DirectoryDeleted: |
| OS << "dir deleted: "; break; |
| |
| } |
| OS << evt.Filename << '\n'; |
| } |
| }; |
| std::string Error; |
| auto watcher = DirectoryWatcher::create(dirPath, receiver, |
| /*waitInitialSync=*/true, Error); |
| if (!watcher) { |
| errs() << "failed creating directory watcher: " << Error << '\n'; |
| return 1; |
| } |
| #if HAVE_CORESERVICES |
| dispatch_main(); |
| #else |
| return 1; |
| #endif |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Command line processing. |
| //===----------------------------------------------------------------------===// |
| |
| bool deconstructPathAndRange(StringRef input, |
| std::string &filepath, |
| Optional<unsigned> &lineStart, |
| unsigned &lineCount) { |
| StringRef path, range; |
| std::tie(path, range) = input.split(':'); |
| StringRef start, end; |
| std::tie(start, end) = range.split(':'); |
| filepath = path; |
| lineCount = 0; |
| if (start.empty()) |
| return false; |
| unsigned num; |
| if (start.getAsInteger(10, num)) { |
| errs() << "couldn't convert to integer: " << start << '\n'; |
| return true; |
| } |
| lineStart = num; |
| if (end.empty()) |
| return false; |
| if (end.getAsInteger(10, num)) { |
| errs() << "couldn't convert to integer: " << end << '\n'; |
| return true; |
| } |
| lineCount = num-lineStart.getValue(); |
| return false; |
| } |
| |
| int indextest_core_main(int argc, const char **argv) { |
| sys::PrintStackTraceOnErrorSignal(argv[0]); |
| PrettyStackTraceProgram X(argc, argv); |
| |
| assert(argv[1] == StringRef("core")); |
| ++argv; |
| --argc; |
| |
| std::vector<const char *> CompArgs; |
| const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); |
| if (DoubleDash != argv + argc) { |
| CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc); |
| argc = DoubleDash - argv; |
| } |
| |
| cl::HideUnrelatedOptions(options::IndexTestCoreCategory); |
| cl::ParseCommandLineOptions(argc, argv, "index-test-core"); |
| |
| if (options::Action == ActionType::None) { |
| errs() << "error: action required; pass '-help' for options\n"; |
| return 1; |
| } |
| |
| if (options::Action == ActionType::PrintSourceSymbols) { |
| if (!options::ModuleFilePath.empty()) { |
| return printSourceSymbolsFromModule(options::ModuleFilePath, |
| options::ModuleFormat); |
| } |
| if (CompArgs.empty()) { |
| errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n"; |
| return 1; |
| } |
| return printSourceSymbols(CompArgs, options::DumpModuleImports, options::IncludeLocals); |
| } |
| |
| if (options::Action == ActionType::PrintRecord) { |
| if (!options::FilePathAndRange.empty()) { |
| std::string filepath; |
| Optional<unsigned> lineStart; |
| unsigned lineCount; |
| if (deconstructPathAndRange(options::FilePathAndRange, |
| filepath, lineStart, lineCount)) |
| return 1; |
| |
| if (options::InputFiles.empty()) { |
| errs() << "error: missing index store path\n"; |
| return 1; |
| } |
| return printStoreFileRecord(options::InputFiles[0], filepath, lineStart, lineCount, outs()); |
| } |
| |
| if (options::InputFiles.empty()) { |
| errs() << "error: missing input file or directory\n"; |
| return 1; |
| } |
| |
| if (sys::fs::is_directory(options::InputFiles[0])) |
| return printStoreRecords(options::InputFiles[0], outs()); |
| else |
| return printRecord(options::InputFiles[0], outs()); |
| } |
| |
| if (options::Action == ActionType::PrintUnit) { |
| if (options::InputFiles.empty()) { |
| errs() << "error: missing input file or directory\n"; |
| return 1; |
| } |
| |
| if (sys::fs::is_directory(options::InputFiles[0])) |
| return printStoreUnits(options::InputFiles[0], outs()); |
| else |
| return printUnit(options::InputFiles[0], outs()); |
| } |
| |
| #if INDEXSTORE_HAS_BLOCKS |
| if (options::Action == ActionType::PrintStoreFormatVersion) { |
| outs() << indexstore::IndexStore::formatVersion() << '\n'; |
| } |
| #endif |
| |
| if (options::Action == ActionType::AggregateAsJSON) { |
| if (options::InputFiles.empty()) { |
| errs() << "error: missing input data store directory\n"; |
| return 1; |
| } |
| StringRef storePath = options::InputFiles[0]; |
| if (options::OutputFile.empty()) |
| return aggregateDataAsJSON(storePath, outs()); |
| std::error_code EC; |
| raw_fd_ostream OS(options::OutputFile, EC, llvm::sys::fs::F_None); |
| if (EC) { |
| errs() << "failed to open output file: " << EC.message() << '\n'; |
| return 1; |
| } |
| return aggregateDataAsJSON(storePath, OS); |
| } |
| |
| if (options::Action == ActionType::WatchDir) { |
| if (options::InputFiles.empty()) { |
| errs() << "error: missing directory path\n"; |
| return 1; |
| } |
| return watchDirectory(options::InputFiles[0]); |
| } |
| |
| return 0; |
| } |