diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -641,7 +641,7 @@ auto BuiltPreamble = PrecompiledPreamble::Build( CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, Stats ? TimedFS : StatCacheFS, std::make_shared(), - StoreInMemory, CapturedInfo); + StoreInMemory, /*StoragePath=*/StringRef(), CapturedInfo); PreambleTimer.stopTimer(); // When building the AST for the main file, we do want the function diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -291,6 +291,14 @@ which identifies whether a constructor or conversion function cursor was marked with the explicit identifier. +- Introduced the new ``CXIndex`` constructor function + ``clang_createIndexWithOptions``, which allows overriding precompiled preamble + storage path. + +- Deprecated two functions ``clang_CXIndex_setGlobalOptions`` and + ``clang_CXIndex_setInvocationEmissionPathOption`` in favor of the new + function ``clang_createIndexWithOptions`` in order to improve thread safety. + Static Analyzer --------------- - Fix incorrect alignment attribute on the this parameter of certain diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -34,7 +34,7 @@ * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable. */ #define CINDEX_VERSION_MAJOR 0 -#define CINDEX_VERSION_MINOR 63 +#define CINDEX_VERSION_MINOR 64 #define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1)) @@ -275,6 +275,22 @@ */ CINDEX_LINKAGE void clang_disposeIndex(CXIndex index); +typedef enum { + /** + * Use the default value of an option that may depend on the process + * environment. + */ + CXChoice_Default = 0, + /** + * Enable the option. + */ + CXChoice_Enabled = 1, + /** + * Disable the option. + */ + CXChoice_Disabled = 2 +} CXChoice; + typedef enum { /** * Used to indicate that no special CXIndex options are needed. @@ -309,9 +325,116 @@ } CXGlobalOptFlags; +/** + * Index initialization options. + * + * 0 is the default value of each member of this struct except for Size. + * Initialize the struct in one of the following two ways to avoid adapting code + * each time a new member is added to it: + * \code + * CXIndexOptions Opts; + * memset(&Opts, 0, sizeof(Opts)); + * Opts.Size = sizeof(CXIndexOptions); + * \endcode + * or explicitly initialize the first data member and zero-initialize the rest: + * \code + * CXIndexOptions Opts = { sizeof(CXIndexOptions) }; + * \endcode + */ +typedef struct CXIndexOptions { + /** + * The size of struct CXIndexOptions used for option versioning. + * + * Always initialize this member to sizeof(CXIndexOptions), or assign + * sizeof(CXIndexOptions) to it right after creating a CXIndexOptions object. + */ + unsigned Size; + /** + * A CXChoice enumerator that specifies the indexing priority policy. + * \sa CXGlobalOpt_ThreadBackgroundPriorityForIndexing + */ + unsigned char ThreadBackgroundPriorityForIndexing; + /** + * A CXChoice enumerator that specifies the editing priority policy. + * \sa CXGlobalOpt_ThreadBackgroundPriorityForEditing + */ + unsigned char ThreadBackgroundPriorityForEditing; + /** + * \see clang_createIndex() + */ + int ExcludeDeclarationsFromPCH : 1; + /** + * \see clang_createIndex() + */ + int DisplayDiagnostics : 1; + /** + * The path to a directory, in which to store temporary PCH files. If null or + * empty, the default system temporary directory is used. These PCH files are + * deleted on clean exit but stay on disk if the program crashes or is killed. + * + * Libclang does not create the directory at the specified path in the file + * system. Therefore it must exist, or storing PCH files will fail. + */ + const char *PreambleStoragePath; + /** + * Specifies a path which will contain log files for certain libclang + * invocations. A null value implies that libclang invocations are not logged. + */ + const char *InvocationEmissionPath; +} CXIndexOptions; + +/** + * Provides a shared context for creating translation units. + * + * Call this function instead of clang_createIndex() if you need to configure + * the additional options in CXIndexOptions. + * + * \returns The created index or null in case of error, such as an unsupported + * value of options->Size. + * + * For example: + * \code + * CXIndex createIndex(const char *ApplicationTemporaryPath) { + * const int ExcludeDeclarationsFromPCH = 1; + * const int DisplayDiagnostics = 1; + * CXIndex Idx; + * #if CINDEX_VERSION_MINOR >= 64 + * CXIndexOptions Opts; + * memset(&Opts, 0, sizeof(Opts)); + * Opts.Size = sizeof(CXIndexOptions); + * Opts.ThreadBackgroundPriorityForIndexing = 1; + * Opts.ExcludeDeclarationsFromPCH = ExcludeDeclarationsFromPCH; + * Opts.DisplayDiagnostics = DisplayDiagnostics; + * Opts.PreambleStoragePath = ApplicationTemporaryPath; + * Idx = clang_createIndexWithOptions(&Opts); + * if (Idx) + * return Idx; + * fprintf(stderr, + * "clang_createIndexWithOptions() failed. " + * "CINDEX_VERSION_MINOR = %d, sizeof(CXIndexOptions) = %u\n", + * CINDEX_VERSION_MINOR, Opts.Size); + * #else + * (void)ApplicationTemporaryPath; + * #endif + * Idx = clang_createIndex(ExcludeDeclarationsFromPCH, DisplayDiagnostics); + * clang_CXIndex_setGlobalOptions( + * Idx, clang_CXIndex_getGlobalOptions(Idx) | + * CXGlobalOpt_ThreadBackgroundPriorityForIndexing); + * return Idx; + * } + * \endcode + * + * \sa clang_createIndex() + */ +CINDEX_LINKAGE CXIndex +clang_createIndexWithOptions(const CXIndexOptions *options); + /** * Sets general options associated with a CXIndex. * + * This function is DEPRECATED. Set CXIndexOptions::GlobalOptions and call + * clang_createIndexWithOptions() instead. + * * For example: * \code * CXIndex idx = ...; @@ -327,6 +450,9 @@ /** * Gets the general options associated with a CXIndex. * + * This function allows to obtain the final option values used by libclang after + * specifying the option policies via CXChoice enumerators. + * * \returns A bitmask of options, a bitwise OR of CXGlobalOpt_XXX flags that * are associated with the given CXIndex object. */ @@ -335,6 +461,9 @@ /** * Sets the invocation emission path option in a CXIndex. * + * This function is DEPRECATED. Set CXIndexOptions::InvocationEmissionPath and + * call clang_createIndexWithOptions() instead. + * * The invocation emission path specifies a path which will contain log * files for certain libclang invocations. A null value (default) implies that * libclang invocations are not logged.. diff --git a/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/include/clang/Frontend/ASTUnit.h @@ -124,6 +124,7 @@ std::unique_ptr WriterData; FileSystemOptions FileSystemOpts; + std::string PreambleStoragePath; /// The AST consumer that received information about the translation /// unit as it was parsed or loaded. @@ -802,6 +803,10 @@ /// /// \param ResourceFilesPath - The path to the compiler resource files. /// + /// \param PreambleStoragePath - The path to a directory, in which to create + /// temporary PCH files. If empty, the default system temporary directory is + /// used. + /// /// \param ModuleFormat - If provided, uses the specific module format. /// /// \param ErrAST - If non-null and parsing failed without any AST to return @@ -820,7 +825,7 @@ const char **ArgBegin, const char **ArgEnd, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, StringRef ResourceFilesPath, - bool OnlyLocalDecls = false, + StringRef PreambleStoragePath = StringRef(), bool OnlyLocalDecls = false, CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::None, ArrayRef RemappedFiles = std::nullopt, bool RemappedFilesKeepOriginalName = true, diff --git a/clang/include/clang/Frontend/PrecompiledPreamble.h b/clang/include/clang/Frontend/PrecompiledPreamble.h --- a/clang/include/clang/Frontend/PrecompiledPreamble.h +++ b/clang/include/clang/Frontend/PrecompiledPreamble.h @@ -75,6 +75,10 @@ /// \param StoreInMemory Store PCH in memory. If false, PCH will be stored in /// a temporary file. /// + /// \param StoragePath The path to a directory, in which to create a temporary + /// file to store PCH in. If empty, the default system temporary directory is + /// used. This parameter is ignored if \p StoreInMemory is true. + /// /// \param Callbacks A set of callbacks to be executed when building /// the preamble. static llvm::ErrorOr @@ -83,7 +87,8 @@ DiagnosticsEngine &Diagnostics, IntrusiveRefCntPtr VFS, std::shared_ptr PCHContainerOps, - bool StoreInMemory, PreambleCallbacks &Callbacks); + bool StoreInMemory, StringRef StoragePath, + PreambleCallbacks &Callbacks); PrecompiledPreamble(PrecompiledPreamble &&); PrecompiledPreamble &operator=(PrecompiledPreamble &&); 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 @@ -1397,7 +1397,8 @@ llvm::ErrorOr NewPreamble = PrecompiledPreamble::Build( PreambleInvocationIn, MainFileBuffer.get(), Bounds, *Diagnostics, VFS, - PCHContainerOps, /*StoreInMemory=*/false, Callbacks); + PCHContainerOps, /*StoreInMemory=*/false, PreambleStoragePath, + Callbacks); PreambleInvocationIn.getFrontendOpts().SkipFunctionBodies = PreviousSkipFunctionBodies; @@ -1741,12 +1742,13 @@ const char **ArgBegin, const char **ArgEnd, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, StringRef ResourceFilesPath, - bool OnlyLocalDecls, CaptureDiagsKind CaptureDiagnostics, - ArrayRef RemappedFiles, bool RemappedFilesKeepOriginalName, - unsigned PrecompilePreambleAfterNParses, TranslationUnitKind TUKind, - bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, - bool AllowPCHWithCompilerErrors, SkipFunctionBodiesScope SkipFunctionBodies, - bool SingleFileParse, bool UserFilesAreVolatile, bool ForSerialization, + StringRef PreambleStoragePath, bool OnlyLocalDecls, + CaptureDiagsKind CaptureDiagnostics, ArrayRef RemappedFiles, + bool RemappedFilesKeepOriginalName, unsigned PrecompilePreambleAfterNParses, + TranslationUnitKind TUKind, bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, bool AllowPCHWithCompilerErrors, + SkipFunctionBodiesScope SkipFunctionBodies, bool SingleFileParse, + bool UserFilesAreVolatile, bool ForSerialization, bool RetainExcludedConditionalBlocks, std::optional ModuleFormat, std::unique_ptr *ErrAST, IntrusiveRefCntPtr VFS) { @@ -1801,6 +1803,7 @@ VFS = llvm::vfs::getRealFileSystem(); VFS = createVFSFromCompilerInvocation(*CI, *Diags, VFS); AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); + AST->PreambleStoragePath = PreambleStoragePath; AST->ModuleCache = new InMemoryModuleCache; AST->OnlyLocalDecls = OnlyLocalDecls; AST->CaptureDiagnostics = CaptureDiagnostics; 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 @@ -197,20 +197,32 @@ class TempPCHFile { public: // A main method used to construct TempPCHFile. - static std::unique_ptr create() { + static std::unique_ptr create(StringRef StoragePath) { // FIXME: This is a hack so that we can override the preamble file during // crash-recovery testing, which is the only case where the preamble files // are not necessarily cleaned up. if (const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE")) return std::unique_ptr(new TempPCHFile(TmpFile)); - llvm::SmallString<64> File; - // Using a version of createTemporaryFile with a file descriptor guarantees + llvm::SmallString<128> File; + // Using the versions of createTemporaryFile() and + // createUniqueFile() with a file descriptor guarantees // that we would never get a race condition in a multi-threaded setting // (i.e., multiple threads getting the same temporary path). int FD; - if (auto EC = - llvm::sys::fs::createTemporaryFile("preamble", "pch", FD, File)) + std::error_code EC; + if (StoragePath.empty()) + EC = llvm::sys::fs::createTemporaryFile("preamble", "pch", FD, File); + else { + llvm::SmallString<128> TempPath = StoragePath; + // Use the same filename model as fs::createTemporaryFile(). + llvm::sys::path::append(TempPath, "preamble-%%%%%%.pch"); + namespace fs = llvm::sys::fs; + // Use the same owner-only file permissions as fs::createTemporaryFile(). + EC = fs::createUniqueFile(TempPath, FD, File, fs::OF_None, + fs::owner_read | fs::owner_write); + } + if (EC) return nullptr; // We only needed to make sure the file exists, close the file right away. llvm::sys::Process::SafelyCloseFileDescriptor(FD); @@ -403,7 +415,7 @@ DiagnosticsEngine &Diagnostics, IntrusiveRefCntPtr VFS, std::shared_ptr PCHContainerOps, bool StoreInMemory, - PreambleCallbacks &Callbacks) { + StringRef StoragePath, PreambleCallbacks &Callbacks) { assert(VFS && "VFS is null"); auto PreambleInvocation = std::make_shared(Invocation); @@ -418,7 +430,8 @@ } else { // Create a temporary file for the precompiled preamble. In rare // circumstances, this can fail. - std::unique_ptr PreamblePCHFile = TempPCHFile::create(); + std::unique_ptr PreamblePCHFile = + TempPCHFile::create(StoragePath); if (!PreamblePCHFile) return BuildPreambleError::CouldntCreateTempFile; Storage = PCHStorage::file(std::move(PreamblePCHFile)); 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 @@ -67,6 +67,27 @@ extern char *dirname(char *); #endif +CXIndex createIndexWithInvocationEmissionPath(int ExcludeDeclarationsFromPCH, + int DisplayDiagnostics) { + CXIndex Idx; + + CXIndexOptions Opts; + memset(&Opts, 0, sizeof(Opts)); + Opts.Size = sizeof(CXIndexOptions); + Opts.ExcludeDeclarationsFromPCH = ExcludeDeclarationsFromPCH; + Opts.DisplayDiagnostics = DisplayDiagnostics; + Opts.InvocationEmissionPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); + + Idx = clang_createIndexWithOptions(&Opts); + if (!Idx) { + fprintf(stderr, + "clang_createIndexWithOptions() failed. " + "CINDEX_VERSION_MINOR = %d, sizeof(CXIndexOptions) = %u\n", + CINDEX_VERSION_MINOR, Opts.Size); + } + return Idx; +} + /** Return the default parsing options. */ static unsigned getDefaultParsingOptions(void) { unsigned options = CXTranslationUnit_DetailedPreprocessingRecord; @@ -2046,18 +2067,17 @@ int result; unsigned Repeats = 0; unsigned I; - const char *InvocationPath; - Idx = clang_createIndex(/* excludeDeclsFromPCH */ - (!strcmp(filter, "local") || - !strcmp(filter, "local-display") || - !strcmp(filter, "local-pretty")) - ? 1 - : 0, - /* displayDiagnostics=*/1); - InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); - if (InvocationPath) - clang_CXIndex_setInvocationEmissionPathOption(Idx, InvocationPath); + Idx = + createIndexWithInvocationEmissionPath(/* excludeDeclsFromPCH */ + (!strcmp(filter, "local") || + !strcmp(filter, "local-display") || + !strcmp(filter, "local-pretty")) + ? 1 + : 0, + /* displayDiagnostics=*/1); + if (!Idx) + return -1; if ((CommentSchemaFile = parse_comments_schema(argc, argv))) { argc--; @@ -2701,7 +2721,6 @@ CXTranslationUnit TU; unsigned I, Repeats = 1; unsigned completionOptions = clang_defaultCodeCompleteOptions(); - const char *InvocationPath; if (getenv("CINDEXTEST_CODE_COMPLETE_PATTERNS")) completionOptions |= CXCodeComplete_IncludeCodePatterns; @@ -2724,10 +2743,9 @@ if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) return -1; - CIdx = clang_createIndex(0, 0); - InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); - if (InvocationPath) - clang_CXIndex_setInvocationEmissionPathOption(CIdx, InvocationPath); + CIdx = createIndexWithInvocationEmissionPath(0, 0); + if (!CIdx) + return -1; if (getenv("CINDEXTEST_EDITING")) Repeats = 5; @@ -4816,17 +4834,15 @@ int num_unsaved_files = 0; enum CXErrorCode Err; int result = 0; - const char *InvocationPath; CXString SGF; const char *usr; usr = input + strlen("-single-symbol-sgf-for="); - Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, - /* displayDiagnostics=*/0); - InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); - if (InvocationPath) - clang_CXIndex_setInvocationEmissionPathOption(Idx, InvocationPath); + Idx = createIndexWithInvocationEmissionPath(/* excludeDeclsFromPCH */ 1, + /* displayDiagnostics=*/0); + if (!Idx) + return -1; if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { result = -1; diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -3648,8 +3648,10 @@ static llvm::ManagedStatic RegisterFatalErrorHandlerOnce; -CXIndex clang_createIndex(int excludeDeclarationsFromPCH, - int displayDiagnostics) { +static CIndexer *clang_createIndex_Impl( + int excludeDeclarationsFromPCH, int displayDiagnostics, + unsigned char threadBackgroundPriorityForIndexing = CXChoice_Default, + unsigned char threadBackgroundPriorityForEditing = CXChoice_Default) { // We use crash recovery to make some of our APIs more reliable, implicitly // enable it. if (!getenv("LIBCLANG_DISABLE_CRASH_RECOVERY")) @@ -3673,21 +3675,71 @@ if (displayDiagnostics) CIdxr->setDisplayDiagnostics(); - if (getenv("LIBCLANG_BGPRIO_INDEX")) - CIdxr->setCXGlobalOptFlags(CIdxr->getCXGlobalOptFlags() | - CXGlobalOpt_ThreadBackgroundPriorityForIndexing); - if (getenv("LIBCLANG_BGPRIO_EDIT")) - CIdxr->setCXGlobalOptFlags(CIdxr->getCXGlobalOptFlags() | - CXGlobalOpt_ThreadBackgroundPriorityForEditing); + unsigned GlobalOptions = CIdxr->getCXGlobalOptFlags(); + const auto updateGlobalOption = + [&GlobalOptions](unsigned char Policy, CXGlobalOptFlags Flag, + const char *EnvironmentVariableName) { + switch (Policy) { + case CXChoice_Enabled: + GlobalOptions |= Flag; + break; + case CXChoice_Disabled: + GlobalOptions &= ~Flag; + break; + case CXChoice_Default: + default: // Fall back to default behavior if Policy is unsupported. + if (getenv(EnvironmentVariableName)) + GlobalOptions |= Flag; + } + }; + updateGlobalOption(threadBackgroundPriorityForIndexing, + CXGlobalOpt_ThreadBackgroundPriorityForIndexing, + "LIBCLANG_BGPRIO_INDEX"); + updateGlobalOption(threadBackgroundPriorityForEditing, + CXGlobalOpt_ThreadBackgroundPriorityForEditing, + "LIBCLANG_BGPRIO_EDIT"); + CIdxr->setCXGlobalOptFlags(GlobalOptions); return CIdxr; } +CXIndex clang_createIndex(int excludeDeclarationsFromPCH, + int displayDiagnostics) { + return clang_createIndex_Impl(excludeDeclarationsFromPCH, displayDiagnostics); +} + void clang_disposeIndex(CXIndex CIdx) { if (CIdx) delete static_cast(CIdx); } +CXIndex clang_createIndexWithOptions(const CXIndexOptions *options) { + // Adding new options to struct CXIndexOptions: + // 1. If no other new option has been added in the same libclang version, + // sizeof(CXIndexOptions) must increase for versioning purposes. + // 2. Options should be added at the end of the struct in order to seamlessly + // support older struct versions. If options->Size < sizeof(CXIndexOptions), + // don't attempt to read the missing options and rely on the default values of + // recently added options being reasonable. For example: + // if (options->Size >= offsetof(CXIndexOptions, RecentlyAddedMember)) + // do_something(options->RecentlyAddedMember); + + // If options->Size > sizeof(CXIndexOptions), the user may have set an option + // we can't handle, in which case we return nullptr to report failure. + // Replace `!=` with `>` here to support older struct versions. `!=` has the + // advantage of catching more usage bugs and no disadvantages while there is a + // single supported struct version (the initial version). + if (options->Size != sizeof(CXIndexOptions)) + return nullptr; + CIndexer *const CIdxr = clang_createIndex_Impl( + options->ExcludeDeclarationsFromPCH, options->DisplayDiagnostics, + options->ThreadBackgroundPriorityForIndexing, + options->ThreadBackgroundPriorityForEditing); + CIdxr->setPreambleStoragePath(options->PreambleStoragePath); + CIdxr->setInvocationEmissionPath(options->InvocationEmissionPath); + return CIdxr; +} + void clang_CXIndex_setGlobalOptions(CXIndex CIdx, unsigned options) { if (CIdx) static_cast(CIdx)->setCXGlobalOptFlags(options); @@ -3897,8 +3949,8 @@ std::unique_ptr Unit(ASTUnit::LoadFromCommandLine( Args->data(), Args->data() + Args->size(), CXXIdx->getPCHContainerOperations(), Diags, - CXXIdx->getClangResourcesPath(), CXXIdx->getOnlyLocalDecls(), - CaptureDiagnostics, *RemappedFiles.get(), + CXXIdx->getClangResourcesPath(), CXXIdx->getPreambleStoragePath(), + CXXIdx->getOnlyLocalDecls(), CaptureDiagnostics, *RemappedFiles.get(), /*RemappedFilesKeepOriginalName=*/true, PrecompilePreambleAfterNParses, TUKind, CacheCodeCompletionResults, IncludeBriefCommentsInCodeCompletion, /*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodies, SingleFileParse, diff --git a/clang/tools/libclang/CIndexer.h b/clang/tools/libclang/CIndexer.h --- a/clang/tools/libclang/CIndexer.h +++ b/clang/tools/libclang/CIndexer.h @@ -41,6 +41,7 @@ std::string ToolchainPath; + std::string PreambleStoragePath; std::string InvocationEmissionPath; public: @@ -77,6 +78,12 @@ StringRef getClangToolchainPath(); + void setPreambleStoragePath(StringRef Str) { + PreambleStoragePath = Str.str(); + } + + StringRef getPreambleStoragePath() const { return PreambleStoragePath; } + void setInvocationEmissionPath(StringRef Str) { InvocationEmissionPath = std::string(Str); } diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -421,6 +421,7 @@ LLVM_17 { global: clang_CXXMethod_isExplicit; + clang_createIndexWithOptions; }; # Example of how to add a new symbol version entry. If you do add a new symbol diff --git a/clang/unittests/Frontend/ASTUnitTest.cpp b/clang/unittests/Frontend/ASTUnitTest.cpp --- a/clang/unittests/Frontend/ASTUnitTest.cpp +++ b/clang/unittests/Frontend/ASTUnitTest.cpp @@ -167,7 +167,7 @@ std::unique_ptr ErrUnit; ASTUnit *AST = ASTUnit::LoadFromCommandLine( - &Args[0], &Args[4], PCHContainerOps, Diags, "", false, + &Args[0], &Args[4], PCHContainerOps, Diags, "", "", false, CaptureDiagsKind::All, std::nullopt, true, 0, TU_Complete, false, false, false, SkipFunctionBodiesScope::None, false, true, false, false, std::nullopt, &ErrUnit, nullptr); diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp --- a/clang/unittests/libclang/LibclangTest.cpp +++ b/clang/unittests/libclang/LibclangTest.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "TestUtils.h" #include "clang-c/Index.h" #include "clang-c/Rewrite.h" #include "llvm/ADT/StringRef.h" @@ -14,7 +15,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" -#include "TestUtils.h" +#include #include #include #include @@ -355,6 +356,168 @@ clang_ModuleMapDescriptor_dispose(MMD); } +TEST_F(LibclangParseTest, GlobalOptions) { + EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None); +} + +class LibclangIndexOptionsTest : public LibclangParseTest { + virtual void AdjustOptions(CXIndexOptions &Opts) {} + +protected: + void CreateIndex() override { + CXIndexOptions Opts; + memset(&Opts, 0, sizeof(Opts)); + Opts.Size = sizeof(CXIndexOptions); + AdjustOptions(Opts); + Index = clang_createIndexWithOptions(&Opts); + ASSERT_TRUE(Index); + } +}; + +TEST_F(LibclangIndexOptionsTest, GlobalOptions) { + EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None); +} + +class LibclangIndexingEnabledIndexOptionsTest + : public LibclangIndexOptionsTest { + void AdjustOptions(CXIndexOptions &Opts) override { + Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled; + } +}; + +TEST_F(LibclangIndexingEnabledIndexOptionsTest, GlobalOptions) { + EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), + CXGlobalOpt_ThreadBackgroundPriorityForIndexing); +} + +class LibclangIndexingDisabledEditingEnabledIndexOptionsTest + : public LibclangIndexOptionsTest { + void AdjustOptions(CXIndexOptions &Opts) override { + Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Disabled; + Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled; + } +}; + +TEST_F(LibclangIndexingDisabledEditingEnabledIndexOptionsTest, GlobalOptions) { + EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), + CXGlobalOpt_ThreadBackgroundPriorityForEditing); +} + +class LibclangBothEnabledIndexOptionsTest : public LibclangIndexOptionsTest { + void AdjustOptions(CXIndexOptions &Opts) override { + Opts.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled; + Opts.ThreadBackgroundPriorityForEditing = CXChoice_Enabled; + } +}; + +TEST_F(LibclangBothEnabledIndexOptionsTest, GlobalOptions) { + EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), + CXGlobalOpt_ThreadBackgroundPriorityForAll); +} + +class LibclangPreambleStorageTest : public LibclangParseTest { + std::string Main = "main.cpp"; + +protected: + std::string PreambleDir; + void InitializePreambleDir() { + llvm::SmallString<128> PathBuffer(TestDir); + llvm::sys::path::append(PathBuffer, "preambles"); + namespace fs = llvm::sys::fs; + ASSERT_FALSE(fs::create_directory(PathBuffer, false, fs::perms::owner_all)); + + PreambleDir = static_cast(PathBuffer); + FilesAndDirsToRemove.insert(PreambleDir); + } + +public: + void CountPreamblesInPreambleDir(int PreambleCount) { + // For some reason, the preamble is not created without '\n' before `int`. + WriteFile(Main, "\nint main() {}"); + + TUFlags |= CXTranslationUnit_CreatePreambleOnFirstParse; + ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, + nullptr, 0, TUFlags); + + int FileCount = 0; + + namespace fs = llvm::sys::fs; + std::error_code EC; + for (fs::directory_iterator File(PreambleDir, EC), FileEnd; + File != FileEnd && !EC; File.increment(EC)) { + ++FileCount; + + EXPECT_EQ(File->type(), fs::file_type::regular_file); + + const auto Filename = llvm::sys::path::filename(File->path()); + EXPECT_EQ(Filename.size(), std::strlen("preamble-%%%%%%.pch")); + EXPECT_TRUE(Filename.startswith("preamble-")); + EXPECT_TRUE(Filename.endswith(".pch")); + + const auto Status = File->status(); + ASSERT_TRUE(Status); + if (false) { + // The permissions assertion below fails, because the .pch.tmp file is + // created with default permissions and replaces the .pch file along + // with its permissions. Therefore the permissions set in + // TempPCHFile::create() don't matter in the end. + EXPECT_EQ(Status->permissions(), fs::owner_read | fs::owner_write); + } + } + + EXPECT_EQ(FileCount, PreambleCount); + } +}; + +class LibclangNotOverriddenPreambleStoragePathTest + : public LibclangPreambleStorageTest { +protected: + void CreateIndex() override { + InitializePreambleDir(); + LibclangPreambleStorageTest::CreateIndex(); + } +}; + +class LibclangSetPreambleStoragePathTest : public LibclangPreambleStorageTest { + virtual const char *PreambleStoragePath() = 0; + +protected: + void CreateIndex() override { + InitializePreambleDir(); + + CXIndexOptions Opts = {sizeof(CXIndexOptions)}; + Opts.PreambleStoragePath = PreambleStoragePath(); + Index = clang_createIndexWithOptions(&Opts); + ASSERT_TRUE(Index); + } +}; + +class LibclangNullPreambleStoragePathTest + : public LibclangSetPreambleStoragePathTest { + const char *PreambleStoragePath() override { return nullptr; } +}; +class LibclangEmptyPreambleStoragePathTest + : public LibclangSetPreambleStoragePathTest { + const char *PreambleStoragePath() override { return ""; } +}; +class LibclangPreambleDirPreambleStoragePathTest + : public LibclangSetPreambleStoragePathTest { + const char *PreambleStoragePath() override { return PreambleDir.c_str(); } +}; + +TEST_F(LibclangNotOverriddenPreambleStoragePathTest, CountPreambles) { + CountPreamblesInPreambleDir(0); +} +TEST_F(LibclangNullPreambleStoragePathTest, CountPreambles) { + CountPreamblesInPreambleDir(0); +} +TEST_F(LibclangEmptyPreambleStoragePathTest, CountPreambles) { + CountPreamblesInPreambleDir(0); +} +TEST_F(LibclangPreambleDirPreambleStoragePathTest, CountPreambles) { + CountPreamblesInPreambleDir(1); +} + TEST_F(LibclangParseTest, AllSkippedRanges) { std::string Header = "header.h", Main = "main.cpp"; WriteFile(Header, diff --git a/clang/unittests/libclang/TestUtils.h b/clang/unittests/libclang/TestUtils.h --- a/clang/unittests/libclang/TestUtils.h +++ b/clang/unittests/libclang/TestUtils.h @@ -22,11 +22,11 @@ #include class LibclangParseTest : public ::testing::Test { - // std::greater<> to remove files before their parent dirs in TearDown(). - std::set> Files; typedef std::unique_ptr fixed_addr_string; std::map UnsavedFileContents; public: + // std::greater<> to remove files before their parent dirs in TearDown(). + std::set> FilesAndDirsToRemove; std::string TestDir; bool RemoveTestDirRecursivelyDuringTeardown = false; CXIndex Index; @@ -40,7 +40,7 @@ TestDir = std::string(Dir.str()); TUFlags = CXTranslationUnit_DetailedPreprocessingRecord | clang_defaultEditingTranslationUnitOptions(); - Index = clang_createIndex(0, 0); + CreateIndex(); ClangTU = nullptr; } void TearDown() override { @@ -48,7 +48,7 @@ clang_disposeIndex(Index); namespace fs = llvm::sys::fs; - for (const std::string &Path : Files) + for (const std::string &Path : FilesAndDirsToRemove) EXPECT_FALSE(fs::remove(Path, /*IgnoreNonExisting=*/false)); if (RemoveTestDirRecursivelyDuringTeardown) EXPECT_FALSE(fs::remove_directories(TestDir, /*IgnoreErrors=*/false)); @@ -63,7 +63,7 @@ FileI != FileEnd; ++FileI) { ASSERT_NE(*FileI, "."); path::append(Path, *FileI); - Files.emplace(Path.str()); + FilesAndDirsToRemove.emplace(Path.str()); } Filename = std::string(Path.str()); } @@ -101,6 +101,9 @@ return string; }; +protected: + virtual void CreateIndex() { Index = clang_createIndex(0, 0); } + private: template static CXChildVisitResult TraverseStateless(CXCursor cx, CXCursor parent,