diff --git a/clang/include/clang-c/Dependencies.h b/clang/include/clang-c/Dependencies.h index fd041344c6fdc..3475df1f198b0 100644 --- a/clang/include/clang-c/Dependencies.h +++ b/clang/include/clang-c/Dependencies.h @@ -585,6 +585,11 @@ const char *clang_experimental_DepGraph_getTUContextHash(CXDepGraph); CINDEX_LINKAGE CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph); +CINDEX_LINKAGE +CXCStringArray + clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths( + CXDependencyScannerService); + /** * @} */ diff --git a/clang/test/ClangScanDeps/error-c-api.cpp b/clang/test/ClangScanDeps/error-c-api.cpp index b40319fcb6313..cfd1bcbd3a75c 100644 --- a/clang/test/ClangScanDeps/error-c-api.cpp +++ b/clang/test/ClangScanDeps/error-c-api.cpp @@ -4,3 +4,4 @@ // CHECK: error: failed to get dependencies // CHECK-NEXT: 'missing.h' file not found +// CHECK-NEXT: number of invalid negatively stat cached paths: 0 diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index 132e780301b02..7a7afe1c5066e 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -907,6 +907,14 @@ static int scanDeps(ArrayRef Args, std::string WorkingDirectory, clang_disposeString(Spelling); clang_disposeDiagnostic(Diag); } + + CXCStringArray InvalidNegativeStatCachedPaths = + clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths( + Service); + + llvm::errs() << "note: number of invalid negatively stat cached paths: " + << InvalidNegativeStatCachedPaths.Count << "\n"; + return 1; } diff --git a/clang/tools/libclang/CDependencies.cpp b/clang/tools/libclang/CDependencies.cpp index 1839f8a2d3127..bd22ddc25055f 100644 --- a/clang/tools/libclang/CDependencies.cpp +++ b/clang/tools/libclang/CDependencies.cpp @@ -41,12 +41,57 @@ struct DependencyScannerServiceOptions { ScanningOutputFormat getFormat() const; }; + +struct CStringsManager { + SmallVector>> OwnedCStr; + SmallVector>> OwnedStdStr; + + /// Doesn't own the string contents. + CXCStringArray createCStringsRef(ArrayRef Strings) { + OwnedCStr.push_back(std::make_unique>()); + std::vector &CStrings = *OwnedCStr.back(); + CStrings.reserve(Strings.size()); + for (const auto &String : Strings) + CStrings.push_back(String.c_str()); + return {CStrings.data(), CStrings.size()}; + } + + /// Doesn't own the string contents. + CXCStringArray createCStringsRef(const llvm::StringSet<> &StringsUnordered) { + std::vector Strings; + + for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end(); + SI != SE; ++SI) + Strings.push_back(SI->getKey()); + + llvm::sort(Strings); + + OwnedCStr.push_back(std::make_unique>()); + std::vector &CStrings = *OwnedCStr.back(); + CStrings.reserve(Strings.size()); + for (const auto &String : Strings) + CStrings.push_back(String.data()); + return {CStrings.data(), CStrings.size()}; + } + + /// Gets ownership of string contents. + CXCStringArray createCStringsOwned(std::vector &&Strings) { + OwnedStdStr.push_back( + std::make_unique>(std::move(Strings))); + return createCStringsRef(*OwnedStdStr.back()); + } +}; + +struct DependencyScannerService { + DependencyScanningService Service; + CStringsManager StrMgr{}; +}; } // end anonymous namespace DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerServiceOptions, CXDependencyScannerServiceOptions) -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningService, +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerService, CXDependencyScannerService) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningWorker, CXDependencyScannerWorker) @@ -127,9 +172,9 @@ clang_experimental_DependencyScannerService_create_v0(CXDependencyMode Format) { // FIXME: Pass default CASOpts and nullptr as CachingOnDiskFileSystem now. CASOptions CASOpts; IntrusiveRefCntPtr FS; - return wrap(new DependencyScanningService( + return wrap(new DependencyScannerService{DependencyScanningService( ScanningMode::DependencyDirectivesScan, unwrap(Format), CASOpts, - /*CAS=*/nullptr, /*ActionCache=*/nullptr, FS)); + /*CAS=*/nullptr, /*ActionCache=*/nullptr, FS)}); } ScanningOutputFormat DependencyScannerServiceOptions::getFormat() const { @@ -165,10 +210,10 @@ clang_experimental_DependencyScannerService_create_v1( FS = llvm::cantFail( llvm::cas::createCachingOnDiskFileSystem(CAS)); } - return wrap(new DependencyScanningService( + return wrap(new DependencyScannerService{DependencyScanningService( ScanningMode::DependencyDirectivesScan, Format, unwrap(Opts)->CASOpts, std::move(CAS), std::move(Cache), std::move(FS), - unwrap(Opts)->OptimizeArgs)); + unwrap(Opts)->OptimizeArgs)}); } void clang_experimental_DependencyScannerService_dispose_v0( @@ -177,17 +222,17 @@ void clang_experimental_DependencyScannerService_dispose_v0( } CXDependencyScannerWorker clang_experimental_DependencyScannerWorker_create_v0( - CXDependencyScannerService Service) { - ScanningOutputFormat Format = unwrap(Service)->getFormat(); + CXDependencyScannerService S) { + ScanningOutputFormat Format = unwrap(S)->Service.getFormat(); bool IsIncludeTreeOutput = Format == ScanningOutputFormat::IncludeTree || Format == ScanningOutputFormat::FullIncludeTree; llvm::IntrusiveRefCntPtr FS = llvm::vfs::createPhysicalFileSystem(); if (IsIncludeTreeOutput) - FS = llvm::cas::createCASProvidingFileSystem(unwrap(Service)->getCAS(), + FS = llvm::cas::createCASProvidingFileSystem(unwrap(S)->Service.getCAS(), std::move(FS)); - return wrap(new DependencyScanningWorker(*unwrap(Service), FS)); + return wrap(new DependencyScanningWorker(unwrap(S)->Service, FS)); } void clang_experimental_DependencyScannerWorker_dispose_v0( @@ -223,46 +268,6 @@ struct DependencyScannerWorkerScanSettings { MLO; }; -struct CStringsManager { - SmallVector>> OwnedCStr; - SmallVector>> OwnedStdStr; - - /// Doesn't own the string contents. - CXCStringArray createCStringsRef(ArrayRef Strings) { - OwnedCStr.push_back(std::make_unique>()); - std::vector &CStrings = *OwnedCStr.back(); - CStrings.reserve(Strings.size()); - for (const auto &String : Strings) - CStrings.push_back(String.c_str()); - return {CStrings.data(), CStrings.size()}; - } - - /// Doesn't own the string contents. - CXCStringArray createCStringsRef(const llvm::StringSet<> &StringsUnordered) { - std::vector Strings; - - for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end(); - SI != SE; ++SI) - Strings.push_back(SI->getKey()); - - llvm::sort(Strings); - - OwnedCStr.push_back(std::make_unique>()); - std::vector &CStrings = *OwnedCStr.back(); - CStrings.reserve(Strings.size()); - for (const auto &String : Strings) - CStrings.push_back(String.data()); - return {CStrings.data(), CStrings.size()}; - } - - /// Gets ownership of string contents. - CXCStringArray createCStringsOwned(std::vector &&Strings) { - OwnedStdStr.push_back( - std::make_unique>(std::move(Strings))); - return createCStringsRef(*OwnedStdStr.back()); - } -}; - struct DependencyGraph { TranslationUnitDeps TUDeps; SmallString<256> SerialDiagBuf; @@ -561,6 +566,38 @@ CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph Graph) { return unwrap(Graph)->getDiagnosticSet(); } +CXCStringArray +clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths( + CXDependencyScannerService S) { + DependencyScanningService &Service = unwrap(S)->Service; + CStringsManager &StrMgr = unwrap(S)->StrMgr; + + // FIXME: CAS currently does not use the shared cache, and cannot produce + // the same diagnostics. We should add such a diagnostics to CAS as well. + if (Service.useCASFS()) + return {nullptr, 0}; + + DependencyScanningFilesystemSharedCache &SharedCache = + Service.getSharedCache(); + + // Note that it is critical that this FS is the same as the default virtual + // file system we pass to the DependencyScanningWorkers. + llvm::IntrusiveRefCntPtr FS = + llvm::vfs::createPhysicalFileSystem(); + + auto InvaidNegStatCachedPaths = + SharedCache.getInvalidNegativeStatCachedPaths(*FS); + + // FIXME: This code here creates copies of strings from + // InvaidNegStatCachedPaths. It is acceptable because this C-API is expected + // to be called only at the end of a CXDependencyScannerService's lifetime. + // In other words, it is called very infrequently. We can change + // CStringsManager's interface to accommodate handling arbitrary StringRefs + // (which may not be null terminated) if we want to avoid copying. + return StrMgr.createCStringsOwned( + {InvaidNegStatCachedPaths.begin(), InvaidNegStatCachedPaths.end()}); +} + static std::string lookupModuleOutput(const ModuleDeps &MD, ModuleOutputKind MOK, void *MLOContext, std::variant &Strings) { CXStringSet *createSet(const llvm::StringSet<> &StringsUnordered) { std::vector Strings; - - for (auto SI = StringsUnordered.begin(), - SE = StringsUnordered.end(); SI != SE; ++SI) + + for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end(); + SI != SE; ++SI) Strings.push_back(SI->getKey()); - + llvm::sort(Strings); - + CXStringSet *Set = new CXStringSet; Set->Count = Strings.size(); Set->Strings = new CXString[Set->Count]; diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map index 389ce6e1f42f4..6d48a73654b26 100644 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -577,6 +577,7 @@ LLVM_21 { clang_experimental_DepGraphModule_isCWDIgnored; clang_experimental_DepGraphModule_isInStableDirs; clang_getFullyQualifiedName; + clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths; }; # Example of how to add a new symbol version entry. If you do add a new symbol