File indexing completed on 2024-05-19 15:44:54
0001 /* 0002 SPDX-FileCopyrightText: 2013 Olivier de Gaalon <olivier.jg@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "clangindex.h" 0008 0009 #include "clangpch.h" 0010 #include "clangparsingenvironment.h" 0011 #include "documentfinderhelpers.h" 0012 0013 #include <interfaces/icompletionsettings.h> 0014 #include <interfaces/icore.h> 0015 #include <interfaces/ilanguagecontroller.h> 0016 0017 #include <util/path.h> 0018 #include <util/clangtypes.h> 0019 #include <util/clangdebug.h> 0020 #include <language/backgroundparser/urlparselock.h> 0021 #include <language/duchain/duchainlock.h> 0022 #include <language/duchain/duchain.h> 0023 0024 #include <clang-c/Index.h> 0025 0026 using namespace KDevelop; 0027 0028 namespace { 0029 0030 CXIndex createIndex() 0031 { 0032 // NOTE: We don't exclude PCH declarations. That way we could retrieve imports manually, 0033 // as clang_getInclusions returns nothing on reparse with CXTranslationUnit_PrecompiledPreamble flag. 0034 const auto excludeDeclarationsFromPCH = false; 0035 const auto displayDiagnostics = qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DIAGS"); 0036 0037 CXIndex index; 0038 #if CINDEX_VERSION_MINOR >= 64 0039 const bool storePreamblesInMemory = 0040 ICore::self()->languageController()->completionSettings()->precompiledPreambleStorage() 0041 == ICompletionSettings::PrecompiledPreambleStorage::Memory; 0042 // When KDevelop crashes, libclang leaves preamble-*.pch files in its temporary directory. 0043 // These files occupy from tens of megabytes to gigabytes and are not automatically removed 0044 // until system restart. Set the preamble storage path to the active session's temporary 0045 // directory in order to remove all PCH files for the active session on KDevelop start. 0046 // See also https://github.com/llvm/llvm-project/issues/51847 0047 const auto preambleStoragePath = ICore::self()->sessionTemporaryDirectoryPath().toUtf8(); 0048 0049 CXIndexOptions options = {sizeof(CXIndexOptions)}; 0050 // Demote the priority of the clang parse threads to reduce potential UI lockups. 0051 // The code completion threads still retain their normal priority to return the results as quickly as possible. 0052 options.ThreadBackgroundPriorityForIndexing = CXChoice_Enabled; 0053 options.ExcludeDeclarationsFromPCH = excludeDeclarationsFromPCH; 0054 options.DisplayDiagnostics = displayDiagnostics; 0055 options.StorePreamblesInMemory = storePreamblesInMemory; 0056 options.PreambleStoragePath = preambleStoragePath.constData(); 0057 0058 index = clang_createIndexWithOptions(&options); 0059 if (index) { 0060 return index; 0061 } 0062 qCWarning(KDEV_CLANG) << "clang_createIndexWithOptions() failed. CINDEX_VERSION_MINOR =" << CINDEX_VERSION_MINOR 0063 << ", sizeof(CXIndexOptions) =" << options.Size; 0064 // Fall back to using older API. Configuring the preamble storage is not essential. 0065 #endif 0066 index = clang_createIndex(excludeDeclarationsFromPCH, displayDiagnostics); 0067 // Demote the priority of the clang parse threads to reduce potential UI lockups. 0068 // The code completion threads still retain their normal priority to return the results as quickly as possible. 0069 clang_CXIndex_setGlobalOptions( 0070 index, clang_CXIndex_getGlobalOptions(index) | CXGlobalOpt_ThreadBackgroundPriorityForIndexing); 0071 return index; 0072 } 0073 0074 } // unnamed namespace 0075 0076 ClangIndex::ClangIndex() 0077 : m_index(createIndex()) 0078 { 0079 } 0080 0081 CXIndex ClangIndex::index() const 0082 { 0083 return m_index; 0084 } 0085 0086 QSharedPointer<const ClangPCH> ClangIndex::pch(const ClangParsingEnvironment& environment) 0087 { 0088 const auto& pchInclude = environment.pchInclude(); 0089 if (!pchInclude.isValid()) { 0090 return {}; 0091 } 0092 0093 UrlParseLock pchLock(IndexedString(pchInclude.pathOrUrl())); 0094 0095 if (QFile::exists(pchInclude.toLocalFile() + QLatin1String(".pch"))) { 0096 QReadLocker lock(&m_pchLock); 0097 auto pch = m_pch.constFind(pchInclude); 0098 if (pch != m_pch.constEnd()) { 0099 return pch.value(); 0100 } 0101 } 0102 0103 auto pch = QSharedPointer<ClangPCH>::create(environment, this); 0104 QWriteLocker lock(&m_pchLock); 0105 m_pch.insert(pchInclude, pch); 0106 return pch; 0107 } 0108 0109 ClangIndex::~ClangIndex() 0110 { 0111 clang_disposeIndex(m_index); 0112 } 0113 0114 IndexedString ClangIndex::translationUnitForUrl(const IndexedString& url) 0115 { 0116 { // try explicit pin data first 0117 QMutexLocker lock(&m_mappingMutex); 0118 auto tu = m_tuForUrl.find(url); 0119 if (tu != m_tuForUrl.end()) { 0120 if (!QFile::exists(tu.value().str())) { 0121 // TU doesn't exist, unpin 0122 m_tuForUrl.erase(tu); 0123 return url; 0124 } 0125 return tu.value(); 0126 } 0127 } 0128 // if no explicit pin data is available, follow back the duchain import chain 0129 { 0130 DUChainReadLocker lock; 0131 TopDUContext* top = DUChain::self()->chainForDocument(url); 0132 if (top) { 0133 TopDUContext* tuTop = top; 0134 QSet<TopDUContext*> visited; 0135 while(true) { 0136 visited.insert(tuTop); 0137 TopDUContext* next = nullptr; 0138 const auto importers = tuTop->indexedImporters(); 0139 for (IndexedDUContext ctx : importers) { 0140 if (ctx.data()) { 0141 next = ctx.data()->topContext(); 0142 break; 0143 } 0144 } 0145 if (!next || visited.contains(next)) { 0146 break; 0147 } 0148 tuTop = next; 0149 } 0150 if (tuTop != top) { 0151 return tuTop->url(); 0152 } 0153 } 0154 } 0155 0156 // otherwise, fallback to a simple buddy search for headers 0157 if (ClangHelpers::isHeader(url.str())) { 0158 const auto buddies = DocumentFinderHelpers::potentialBuddies(url.toUrl(), false); 0159 for (const QUrl& buddy : buddies) { 0160 const QString buddyPath = buddy.toLocalFile(); 0161 if (QFile::exists(buddyPath)) { 0162 return IndexedString(buddyPath); 0163 } 0164 } 0165 } 0166 return url; 0167 } 0168 0169 void ClangIndex::pinTranslationUnitForUrl(const IndexedString& tu, const IndexedString& url) 0170 { 0171 QMutexLocker lock(&m_mappingMutex); 0172 m_tuForUrl.insert(url, tu); 0173 } 0174 0175 void ClangIndex::unpinTranslationUnitForUrl(const IndexedString& url) 0176 { 0177 QMutexLocker lock(&m_mappingMutex); 0178 m_tuForUrl.remove(url); 0179 }