clangindex.cpp 4.85 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 *    This file is part of KDevelop
 *
 *    Copyright 2013 Olivier de Gaalon <olivier.jg@gmail.com>
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Library General Public
 *    License as published by the Free Software Foundation; either
 *    version 2 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Library General Public License for more details.
 *
 *    You should have received a copy of the GNU Library General Public License
 *    along with this library; see the file COPYING.LIB.  If not, write to
 *    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *    Boston, MA 02110-1301, USA.
 */

#include "clangindex.h"

#include "clangpch.h"
25
#include "clangparsingenvironment.h"
26
#include "documentfinderhelpers.h"
27 28

#include <util/path.h>
29 30
#include <util/clangtypes.h>
#include <util/clangdebug.h>
31
#include <language/backgroundparser/urlparselock.h>
32 33
#include <language/duchain/duchainlock.h>
#include <language/duchain/duchain.h>
34 35 36 37 38 39

#include <clang-c/Index.h>

using namespace KDevelop;

ClangIndex::ClangIndex()
40
    // NOTE: We don't exclude PCH declarations. That way we could retrieve imports manually, as clang_getInclusions returns nothing on reparse with CXTranslationUnit_PrecompiledPreamble flag.
41
    : m_index(clang_createIndex(0 /*Exclude PCH Decls*/, qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DIAGS") /*Display diags*/))
42
{
43 44 45 46 47
    // demote the priority of the clang parse threads to reduce potential UI lockups
    // but the code completion threads still retain their normal priority to return
    // the results as quickly as possible
    clang_CXIndex_setGlobalOptions(m_index, clang_CXIndex_getGlobalOptions(m_index)
        | CXGlobalOpt_ThreadBackgroundPriorityForIndexing);
48 49 50 51 52 53 54
}

CXIndex ClangIndex::index() const
{
    return m_index;
}

55
QSharedPointer<const ClangPCH> ClangIndex::pch(const ClangParsingEnvironment& environment)
56
{
57
    const auto& pchInclude = environment.pchInclude();
58 59 60 61 62 63
    if (!pchInclude.isValid()) {
        return {};
    }

    UrlParseLock pchLock(IndexedString(pchInclude.pathOrUrl()));

64
    if (QFile::exists(pchInclude.toLocalFile() + QLatin1String(".pch"))) {
65 66 67 68 69 70 71
        QReadLocker lock(&m_pchLock);
        auto pch = m_pch.constFind(pchInclude);
        if (pch != m_pch.constEnd()) {
            return pch.value();
        }
    }

72
    auto pch = QSharedPointer<ClangPCH>::create(environment, this);
73 74 75 76 77 78 79 80 81
    QWriteLocker lock(&m_pchLock);
    m_pch.insert(pchInclude, pch);
    return pch;
}

ClangIndex::~ClangIndex()
{
    clang_disposeIndex(m_index);
}
82 83 84

IndexedString ClangIndex::translationUnitForUrl(const IndexedString& url)
{
85 86 87 88 89 90 91 92 93 94 95 96
    { // try explicit pin data first
        QMutexLocker lock(&m_mappingMutex);
        auto tu = m_tuForUrl.find(url);
        if (tu != m_tuForUrl.end()) {
            if (!QFile::exists(tu.value().str())) {
                // TU doesn't exist, unpin
                m_tuForUrl.erase(tu);
                return url;
            }
            return tu.value();
        }
    }
97 98
    // if no explicit pin data is available, follow back the duchain import chain
    {
99
        DUChainReadLocker lock;
100 101 102 103 104 105
        TopDUContext* top = DUChain::self()->chainForDocument(url);
        if (top) {
            TopDUContext* tuTop = top;
            QSet<TopDUContext*> visited;
            while(true) {
                visited.insert(tuTop);
106
                TopDUContext* next = nullptr;
107 108
                const auto importers = tuTop->indexedImporters();
                for (IndexedDUContext ctx : importers) {
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
                    if (ctx.data()) {
                        next = ctx.data()->topContext();
                        break;
                    }
                }
                if (!next || visited.contains(next)) {
                    break;
                }
                tuTop = next;
            }
            if (tuTop != top) {
                return tuTop->url();
            }
        }
    }

125 126
    // otherwise, fallback to a simple buddy search for headers
    if (ClangHelpers::isHeader(url.str())) {
127 128
        const auto buddies = DocumentFinderHelpers::potentialBuddies(url.toUrl(), false);
        for (const QUrl& buddy : buddies) {
129 130 131 132
            const QString buddyPath = buddy.toLocalFile();
            if (QFile::exists(buddyPath)) {
                return IndexedString(buddyPath);
            }
133 134 135 136 137 138 139 140 141 142 143
        }
    }
    return url;
}

void ClangIndex::pinTranslationUnitForUrl(const IndexedString& tu, const IndexedString& url)
{
    QMutexLocker lock(&m_mappingMutex);
    m_tuForUrl.insert(url, tu);
}

144
void ClangIndex::unpinTranslationUnitForUrl(const IndexedString& url)
145 146
{
    QMutexLocker lock(&m_mappingMutex);
147
    m_tuForUrl.remove(url);
148
}