diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -5121,7 +5121,8 @@ MarshallingInfoFlag>; def fno_validate_pch : Flag<["-"], "fno-validate-pch">, HelpText<"Disable validation of precompiled headers">, - MarshallingInfoFlag>; + MarshallingInfoFlag, "DisableValidationForModuleKind::None">, + Normalizer<"makeFlagToValueNormalizer(DisableValidationForModuleKind::All)">; def fallow_pcm_with_errors : Flag<["-"], "fallow-pcm-with-compiler-errors">, HelpText<"Accept a PCM file that was created with compiler errors">, MarshallingInfoFlag>; diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -50,6 +50,7 @@ class Sema; class SourceManager; class TargetInfo; +enum class DisableValidationForModuleKind; /// CompilerInstance - Helper class for managing a single instance of the Clang /// compiler. @@ -659,16 +660,17 @@ /// Create an external AST source to read a PCH file and attach it to the AST /// context. - void createPCHExternalASTSource(StringRef Path, bool DisablePCHValidation, - bool AllowPCHWithCompilerErrors, - void *DeserializationListener, - bool OwnDeserializationListener); + void createPCHExternalASTSource( + StringRef Path, DisableValidationForModuleKind DisableValidation, + bool AllowPCHWithCompilerErrors, void *DeserializationListener, + bool OwnDeserializationListener); /// Create an external AST source to read a PCH file. /// /// \return - The new object on success, or null on failure. static IntrusiveRefCntPtr createPCHExternalASTSource( - StringRef Path, StringRef Sysroot, bool DisablePCHValidation, + StringRef Path, StringRef Sysroot, + DisableValidationForModuleKind DisableValidation, bool AllowPCHWithCompilerErrors, Preprocessor &PP, InMemoryModuleCache &ModuleCache, ASTContext &Context, const PCHContainerReader &PCHContainerRdr, diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_LEX_PREPROCESSOROPTIONS_H_ #define LLVM_CLANG_LEX_PREPROCESSOROPTIONS_H_ +#include "clang/Basic/BitmaskEnum.h" #include "clang/Basic/LLVM.h" #include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" #include "llvm/ADT/StringRef.h" @@ -40,6 +41,24 @@ ARCXX_libstdcxx }; +/// Whether to disable the normal validation performed on precompiled +/// headers and module files when they are loaded. +enum class DisableValidationForModuleKind { + /// Perform validation, don't disable it. + None = 0, + + /// Disable validation for a precompiled header and the modules it depends on. + PCH = 0x1, + + /// Disable validation for module files. + Module = 0x2, + + /// Disable validation for all kinds. + All = PCH | Module, + + LLVM_MARK_AS_BITMASK_ENUM(Module) +}; + /// PreprocessorOptions - This class is used for passing the various options /// used in preprocessor initialization to InitializePreprocessor(). class PreprocessorOptions { @@ -79,9 +98,10 @@ /// Headers that will be converted to chained PCHs in memory. std::vector ChainedIncludes; - /// When true, disables most of the normal validation performed on - /// precompiled headers. - bool DisablePCHValidation = false; + /// Whether to disable most of the normal validation performed on + /// precompiled headers and module files. + DisableValidationForModuleKind DisablePCHOrModuleValidation = + DisableValidationForModuleKind::None; /// When true, a PCH with compiler errors will not be rejected. bool AllowPCHWithCompilerErrors = false; diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -23,6 +23,7 @@ #include "clang/Lex/ExternalPreprocessorSource.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PreprocessingRecord.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/IdentifierResolver.h" #include "clang/Sema/Sema.h" @@ -441,6 +442,9 @@ /// imported from. For non-module AST types it should be invalid. SourceLocation CurrentImportLoc; + /// The module kind that is currently deserializing. + Optional CurrentDeserializingModuleKind; + /// The global module index, if loaded. std::unique_ptr GlobalIndex; @@ -931,8 +935,8 @@ std::string isysroot; /// Whether to disable the normal validation performed on precompiled - /// headers when they are loaded. - bool DisableValidation; + /// headers and module files when they are loaded. + DisableValidationForModuleKind DisableValidationKind; /// Whether to accept an AST file with compiler errors. bool AllowASTWithCompilerErrors; @@ -1215,6 +1219,8 @@ llvm::DenseMap DefinitionSource; + bool shouldDisableValidationForFile(const serialization::ModuleFile &M) const; + /// Reads a statement from the specified cursor. Stmt *ReadStmtFromStream(ModuleFile &F); @@ -1473,9 +1479,9 @@ /// user. This is only used with relocatable PCH files. If non-NULL, /// a relocatable PCH file will use the default path "/". /// - /// \param DisableValidation If true, the AST reader will suppress most + /// \param DisableValidationKind If set, the AST reader will suppress most /// of its regular consistency checking, allowing the use of precompiled - /// headers that cannot be determined to be compatible. + /// headers and module files that cannot be determined to be compatible. /// /// \param AllowASTWithCompilerErrors If true, the AST reader will accept an /// AST file the was created out of an AST with compiler errors, @@ -1496,7 +1502,9 @@ ASTReader(Preprocessor &PP, InMemoryModuleCache &ModuleCache, ASTContext *Context, const PCHContainerReader &PCHContainerRdr, ArrayRef> Extensions, - StringRef isysroot = "", bool DisableValidation = false, + StringRef isysroot = "", + DisableValidationForModuleKind DisableValidationKind = + DisableValidationForModuleKind::None, bool AllowASTWithCompilerErrors = false, bool AllowConfigurationMismatch = false, bool ValidateSystemInputs = false, diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -809,9 +809,10 @@ PP.getIdentifierTable(), PP.getSelectorTable(), PP.getBuiltinInfo()); - bool disableValid = false; + DisableValidationForModuleKind disableValid = + DisableValidationForModuleKind::None; if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) - disableValid = true; + disableValid = DisableValidationForModuleKind::All; AST->Reader = new ASTReader( PP, *AST->ModuleCache, AST->Ctx.get(), PCHContainerRdr, {}, /*isysroot=*/"", diff --git a/clang/lib/Frontend/ChainedIncludesSource.cpp b/clang/lib/Frontend/ChainedIncludesSource.cpp --- a/clang/lib/Frontend/ChainedIncludesSource.cpp +++ b/clang/lib/Frontend/ChainedIncludesSource.cpp @@ -83,10 +83,10 @@ ASTDeserializationListener *deserialListener = nullptr) { Preprocessor &PP = CI.getPreprocessor(); std::unique_ptr Reader; - Reader.reset(new ASTReader(PP, CI.getModuleCache(), &CI.getASTContext(), - CI.getPCHContainerReader(), - /*Extensions=*/{}, - /*isysroot=*/"", /*DisableValidation=*/true)); + Reader.reset(new ASTReader( + PP, CI.getModuleCache(), &CI.getASTContext(), CI.getPCHContainerReader(), + /*Extensions=*/{}, + /*isysroot=*/"", DisableValidationForModuleKind::PCH)); for (unsigned ti = 0; ti < bufNames.size(); ++ti) { StringRef sr(bufNames[ti]); Reader->addInMemoryBuffer(sr, std::move(MemBufs[ti])); @@ -129,7 +129,8 @@ CInvok->getPreprocessorOpts().ChainedIncludes.clear(); CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear(); - CInvok->getPreprocessorOpts().DisablePCHValidation = true; + CInvok->getPreprocessorOpts().DisablePCHOrModuleValidation = + DisableValidationForModuleKind::PCH; CInvok->getPreprocessorOpts().Includes.clear(); CInvok->getPreprocessorOpts().MacroIncludes.clear(); CInvok->getPreprocessorOpts().Macros.clear(); diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -503,11 +503,12 @@ // ExternalASTSource void CompilerInstance::createPCHExternalASTSource( - StringRef Path, bool DisablePCHValidation, bool AllowPCHWithCompilerErrors, - void *DeserializationListener, bool OwnDeserializationListener) { + StringRef Path, DisableValidationForModuleKind DisableValidation, + bool AllowPCHWithCompilerErrors, void *DeserializationListener, + bool OwnDeserializationListener) { bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; TheASTReader = createPCHExternalASTSource( - Path, getHeaderSearchOpts().Sysroot, DisablePCHValidation, + Path, getHeaderSearchOpts().Sysroot, DisableValidation, AllowPCHWithCompilerErrors, getPreprocessor(), getModuleCache(), getASTContext(), getPCHContainerReader(), getFrontendOpts().ModuleFileExtensions, DependencyCollectors, @@ -516,7 +517,8 @@ } IntrusiveRefCntPtr CompilerInstance::createPCHExternalASTSource( - StringRef Path, StringRef Sysroot, bool DisablePCHValidation, + StringRef Path, StringRef Sysroot, + DisableValidationForModuleKind DisableValidation, bool AllowPCHWithCompilerErrors, Preprocessor &PP, InMemoryModuleCache &ModuleCache, ASTContext &Context, const PCHContainerReader &PCHContainerRdr, @@ -528,7 +530,7 @@ IntrusiveRefCntPtr Reader(new ASTReader( PP, ModuleCache, &Context, PCHContainerRdr, Extensions, - Sysroot.empty() ? "" : Sysroot.data(), DisablePCHValidation, + Sysroot.empty() ? "" : Sysroot.data(), DisableValidation, AllowPCHWithCompilerErrors, /*AllowConfigurationMismatch*/ false, HSOpts.ModulesValidateSystemHeaders, HSOpts.ValidateASTInputFilesContent, UseGlobalModuleIndex)); @@ -1497,7 +1499,8 @@ TheASTReader = new ASTReader( getPreprocessor(), getModuleCache(), &getASTContext(), getPCHContainerReader(), getFrontendOpts().ModuleFileExtensions, - Sysroot.empty() ? "" : Sysroot.c_str(), PPOpts.DisablePCHValidation, + Sysroot.empty() ? "" : Sysroot.c_str(), + PPOpts.DisablePCHOrModuleValidation, /*AllowASTWithCompilerErrors=*/FEOpts.AllowPCMWithCompilerErrors, /*AllowConfigurationMismatch=*/false, HSOpts.ModulesValidateSystemHeaders, HSOpts.ValidateASTInputFilesContent, diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -873,9 +873,9 @@ if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { CI.createPCHExternalASTSource( CI.getPreprocessorOpts().ImplicitPCHInclude, - CI.getPreprocessorOpts().DisablePCHValidation, - CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, DeserialListener, - DeleteDeserialListener); + CI.getPreprocessorOpts().DisablePCHOrModuleValidation, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, + DeserialListener, DeleteDeserialListener); if (!CI.getASTContext().getExternalSource()) goto failure; } diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -344,7 +344,7 @@ CI.getPreprocessor(), CI.getModuleCache(), &CI.getASTContext(), CI.getPCHContainerReader(), CI.getFrontendOpts().ModuleFileExtensions, Sysroot.empty() ? "" : Sysroot.c_str(), - /*DisableValidation*/ false, + DisableValidationForModuleKind::None, /*AllowASTWithCompilerErrors*/ false, /*AllowConfigurationMismatch*/ true, /*ValidateSystemInputs*/ true)); diff --git a/clang/lib/Frontend/PrecompiledPreamble.cpp b/clang/lib/Frontend/PrecompiledPreamble.cpp --- a/clang/lib/Frontend/PrecompiledPreamble.cpp +++ b/clang/lib/Frontend/PrecompiledPreamble.cpp @@ -812,7 +812,8 @@ PreprocessorOpts.PrecompiledPreambleBytes.first = Bounds.Size; PreprocessorOpts.PrecompiledPreambleBytes.second = Bounds.PreambleEndsAtStartOfLine; - PreprocessorOpts.DisablePCHValidation = true; + PreprocessorOpts.DisablePCHOrModuleValidation = + DisableValidationForModuleKind::PCH; setupPreambleStorage(Storage, PreprocessorOpts, VFS); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -2211,6 +2211,29 @@ PP.setLoadedMacroDirective(II, Earliest, Latest); } +bool ASTReader::shouldDisableValidationForFile( + const serialization::ModuleFile &M) const { + if (DisableValidationKind == DisableValidationForModuleKind::None) + return false; + + // If a PCH is loaded and validation is disabled for PCH then disable + // validation for the PCH and the modules it loads. + ModuleKind K = CurrentDeserializingModuleKind.getValueOr(M.Kind); + + switch (K) { + case MK_MainFile: + case MK_Preamble: + case MK_PCH: + return bool(DisableValidationKind & DisableValidationForModuleKind::PCH); + case MK_ImplicitModule: + case MK_ExplicitModule: + case MK_PrebuiltModule: + return bool(DisableValidationKind & DisableValidationForModuleKind::Module); + } + + return false; +} + ASTReader::InputFileInfo ASTReader::readInputFileInfo(ModuleFile &F, unsigned ID) { // Go find this input file. @@ -2357,7 +2380,7 @@ auto HasInputFileChanged = [&]() { if (StoredSize != File->getSize()) return ModificationType::Size; - if (!DisableValidation && StoredTime && + if (!shouldDisableValidationForFile(F) && StoredTime && StoredTime != File->getModificationTime()) { // In case the modification time changes but not the content, // accept the cached file as legit. @@ -2573,6 +2596,8 @@ return Success; }; + bool DisableValidation = shouldDisableValidationForFile(F); + // Read all of the records and blocks in the control block. RecordData Record; unsigned NumInputs = 0; @@ -2871,7 +2896,8 @@ // If we're implicitly loading a module, the base directory can't // change between the build and use. // Don't emit module relocation error if we have -fno-validate-pch - if (!PP.getPreprocessorOpts().DisablePCHValidation && + if (!bool(PP.getPreprocessorOpts().DisablePCHOrModuleValidation & + DisableValidationForModuleKind::Module) && F.Kind != MK_ExplicitModule && F.Kind != MK_PrebuiltModule) { auto BuildDir = PP.getFileManager().getDirectory(Blob); if (!BuildDir || *BuildDir != M->Directory) { @@ -3903,7 +3929,9 @@ auto &Map = PP.getHeaderSearchInfo().getModuleMap(); const FileEntry *ModMap = M ? Map.getModuleMapFileForUniquing(M) : nullptr; // Don't emit module relocation error if we have -fno-validate-pch - if (!PP.getPreprocessorOpts().DisablePCHValidation && !ModMap) { + if (!bool(PP.getPreprocessorOpts().DisablePCHOrModuleValidation & + DisableValidationForModuleKind::Module) && + !ModMap) { if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) { if (auto ASTFE = M ? M->getASTFile() : None) { // This module was defined by an imported (explicit) module. @@ -4189,6 +4217,8 @@ SmallVectorImpl *Imported) { llvm::SaveAndRestore SetCurImportLocRAII(CurrentImportLoc, ImportLoc); + llvm::SaveAndRestore> SetCurModuleKindRAII( + CurrentDeserializingModuleKind, Type); // Defer any pending actions until we get to the end of reading the AST file. Deserializing AnASTFile(this); @@ -4623,6 +4653,7 @@ PP.getHeaderSearchInfo().getHeaderSearchOpts(); bool AllowCompatibleConfigurationMismatch = F.Kind == MK_ExplicitModule || F.Kind == MK_PrebuiltModule; + bool DisableValidation = shouldDisableValidationForFile(F); ASTReadResult Result = readUnhashedControlBlockImpl( &F, F.Data, ClientLoadCapabilities, AllowCompatibleConfigurationMismatch, @@ -5514,7 +5545,8 @@ if (!ParentModule) { if (const FileEntry *CurFile = CurrentModule->getASTFile()) { // Don't emit module relocation error if we have -fno-validate-pch - if (!PP.getPreprocessorOpts().DisablePCHValidation && + if (!bool(PP.getPreprocessorOpts().DisablePCHOrModuleValidation & + DisableValidationForModuleKind::Module) && CurFile != F.File) { Error(diag::err_module_file_conflict, CurrentModule->getTopLevelModuleName(), CurFile->getName(), @@ -11601,12 +11633,13 @@ ASTContext *Context, const PCHContainerReader &PCHContainerRdr, ArrayRef> Extensions, - StringRef isysroot, bool DisableValidation, + StringRef isysroot, + DisableValidationForModuleKind DisableValidationKind, bool AllowASTWithCompilerErrors, bool AllowConfigurationMismatch, bool ValidateSystemInputs, bool ValidateASTInputFilesContent, bool UseGlobalIndex, std::unique_ptr ReadTimer) - : Listener(DisableValidation + : Listener(bool(DisableValidationKind &DisableValidationForModuleKind::PCH) ? cast(new SimpleASTReaderListener(PP)) : cast(new PCHValidator(PP, *this))), SourceMgr(PP.getSourceManager()), FileMgr(PP.getFileManager()), @@ -11614,7 +11647,7 @@ ContextObj(Context), ModuleMgr(PP.getFileManager(), ModuleCache, PCHContainerRdr, PP.getHeaderSearchInfo()), DummyIdResolver(PP), ReadTimer(std::move(ReadTimer)), isysroot(isysroot), - DisableValidation(DisableValidation), + DisableValidationKind(DisableValidationKind), AllowASTWithCompilerErrors(AllowASTWithCompilerErrors), AllowConfigurationMismatch(AllowConfigurationMismatch), ValidateSystemInputs(ValidateSystemInputs), diff --git a/clang/test/Index/Inputs/preamble-reparse-changed-module/head.h b/clang/test/Index/Inputs/preamble-reparse-changed-module/head.h new file mode 100644 --- /dev/null +++ b/clang/test/Index/Inputs/preamble-reparse-changed-module/head.h @@ -0,0 +1,3 @@ +@interface I +- (void)call_me; +@end diff --git a/clang/test/Index/Inputs/preamble-reparse-changed-module/module.modulemap b/clang/test/Index/Inputs/preamble-reparse-changed-module/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/Index/Inputs/preamble-reparse-changed-module/module.modulemap @@ -0,0 +1,3 @@ +module mod { + header "head.h" +} diff --git a/clang/test/Index/Inputs/preamble-reparse-changed-module/new-head.h b/clang/test/Index/Inputs/preamble-reparse-changed-module/new-head.h new file mode 100644 --- /dev/null +++ b/clang/test/Index/Inputs/preamble-reparse-changed-module/new-head.h @@ -0,0 +1,3 @@ +@interface I +- (void)call_me_new; +@end diff --git a/clang/test/Index/preamble-reparse-changed-module.m b/clang/test/Index/preamble-reparse-changed-module.m new file mode 100644 --- /dev/null +++ b/clang/test/Index/preamble-reparse-changed-module.m @@ -0,0 +1,18 @@ +// REQUIRES: shell + +// RUN: mkdir -p %t/mod +// RUN: touch %t/empty.h +// RUN: cp %S/Inputs/preamble-reparse-changed-module/module.modulemap %t/mod +// RUN: cp %S/Inputs/preamble-reparse-changed-module/head.h %t/mod + +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_EXECUTE_COMMAND="cp %S/Inputs/preamble-reparse-changed-module/new-head.h %t/mod/head.h" CINDEXTEST_EXECUTE_AFTER_TRIAL=1 \ +// RUN: c-index-test -test-load-source-reparse 3 local %s -I %t -I %t/mod -fmodules -fmodules-cache-path=%t/mcp 2>&1 | FileCheck %s + +// CHECK-NOT: warning: + +#include "empty.h" +@import mod; + +void test(I *o) { + [o call_me_new]; +} diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -24,6 +24,7 @@ #endif extern int indextest_core_main(int argc, const char **argv); +extern int indextest_perform_shell_execution(const char *command_line); /******************************************************************************/ /* Utility functions. */ @@ -2095,6 +2096,8 @@ enum CXErrorCode Err; int result, i; int trial; + int execute_after_trial = 0; + const char *execute_command = NULL; int remap_after_trial = 0; char *endptr = 0; @@ -2133,12 +2136,26 @@ if (checkForErrors(TU) != 0) return -1; + if (getenv("CINDEXTEST_EXECUTE_COMMAND")) { + execute_command = getenv("CINDEXTEST_EXECUTE_COMMAND"); + } + if (getenv("CINDEXTEST_EXECUTE_AFTER_TRIAL")) { + execute_after_trial = + strtol(getenv("CINDEXTEST_EXECUTE_AFTER_TRIAL"), &endptr, 10); + } + if (getenv("CINDEXTEST_REMAP_AFTER_TRIAL")) { remap_after_trial = strtol(getenv("CINDEXTEST_REMAP_AFTER_TRIAL"), &endptr, 10); } for (trial = 0; trial < trials; ++trial) { + if (execute_command && trial == execute_after_trial) { + result = indextest_perform_shell_execution(execute_command); + if (result != 0) + return result; + } + free_remapped_files(unsaved_files, num_unsaved_files); if (parse_remapped_files_with_try(trial, argc, argv, 0, &unsaved_files, &num_unsaved_files)) { diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -13,22 +13,25 @@ #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/IndexingAction.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/PrettyStackTrace.h" using namespace clang; using namespace clang::index; using namespace llvm; extern "C" int indextest_core_main(int argc, const char **argv); +extern "C" int indextest_perform_shell_execution(const char *command_line); namespace { @@ -359,3 +362,21 @@ return 0; } + +//===----------------------------------------------------------------------===// +// Utility functions +//===----------------------------------------------------------------------===// + +int indextest_perform_shell_execution(const char *command_line) { + BumpPtrAllocator Alloc; + llvm::StringSaver Saver(Alloc); + SmallVector Args; + llvm::cl::TokenizeGNUCommandLine(command_line, Saver, Args); + auto Program = llvm::sys::findProgramByName(Args[0]); + if (std::error_code ec = Program.getError()) { + llvm::errs() << "command not found: " << Args[0] << "\n"; + return ec.value(); + } + SmallVector execArgs(Args.begin(), Args.end()); + return llvm::sys::ExecuteAndWait(*Program, execArgs); +}