builtinindexingsupport.cpp 13.4 KB
Newer Older
Nikolai Kosjar's avatar
Nikolai Kosjar committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

30
#include "builtinindexingsupport.h"
31

32
#include "builtineditordocumentparser.h"
33
#include "cppchecksymbols.h"
34
#include "cppmodelmanager.h"
35
#include "cppprojectfile.h"
36
#include "cppsourceprocessor.h"
37
#include "cpptoolsconstants.h"
38
#include "cpptoolsplugin.h"
39
#include "searchsymbols.h"
40 41 42

#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
43

44 45
#include <cplusplus/LookupContext.h>
#include <utils/qtcassert.h>
46 47 48
#include <utils/runextensions.h>

#include <QCoreApplication>
49
#include <QElapsedTimer>
50 51 52 53

using namespace CppTools;
using namespace CppTools::Internal;

54
static const bool DumpFileNameWhileParsing = qgetenv("QTC_DUMP_FILENAME_WHILE_PARSING") == "1";
55
static const bool FindErrorsIndexing = qgetenv("QTC_FIND_ERRORS_INDEXING") == "1";
56

57 58
namespace {

59
class ParseParams
60
{
61 62 63 64 65 66 67 68 69
public:
    ParseParams()
        : dumpFileNameWhileParsing(DumpFileNameWhileParsing)
        , revision(0)
    {}

    int dumpFileNameWhileParsing;
    int revision;
    ProjectPart::HeaderPaths headerPaths;
70
    WorkingCopy workingCopy;
71
    QSet<QString> sourceFiles;
72 73
};

74
class WriteTaskFileForDiagnostics
75
{
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    Q_DISABLE_COPY(WriteTaskFileForDiagnostics)

public:
    WriteTaskFileForDiagnostics()
        : m_processedDiagnostics(0)
    {
        const QString fileName = QDir::tempPath()
                + QLatin1String("/qtc_findErrorsIndexing.diagnostics.")
                + QDateTime::currentDateTime().toString(QLatin1String("yyMMdd_HHmm"))
                + QLatin1String(".tasks");

        m_file.setFileName(fileName);
        Q_ASSERT(m_file.open(QIODevice::WriteOnly | QIODevice::Text));
        m_out.setDevice(&m_file);

        qDebug("FindErrorsIndexing: Task file for diagnostics is \"%s\".",
               qPrintable(m_file.fileName()));
    }

    ~WriteTaskFileForDiagnostics()
    {
        qDebug("FindErrorsIndexing: %d diagnostic messages written to \"%s\".",
               m_processedDiagnostics, qPrintable(m_file.fileName()));
    }

    int processedDiagnostics() const { return m_processedDiagnostics; }

    void process(const CPlusPlus::Document::Ptr document)
    {
        using namespace CPlusPlus;
        const QString fileName = document->fileName();

        foreach (const Document::DiagnosticMessage &message, document->diagnosticMessages()) {
            ++m_processedDiagnostics;

            QString type;
            switch (message.level()) {
            case Document::DiagnosticMessage::Warning:
                type = QLatin1String("warn"); break;
            case Document::DiagnosticMessage::Error:
            case Document::DiagnosticMessage::Fatal:
                type = QLatin1String("err"); break;
            default:
                break;
            }

            // format: file\tline\ttype\tdescription
            m_out << fileName << "\t"
                  << message.line() << "\t"
                  << type << "\t"
                  << message.text() << "\n";
        }
    }

private:
    QFile m_file;
    QTextStream m_out;
    int m_processedDiagnostics;
};

136
void classifyFiles(const QSet<QString> &files, QStringList *headers, QStringList *sources)
137 138 139 140 141 142 143 144 145 146 147 148
{
    foreach (const QString &file, files) {
        if (ProjectFile::isSource(ProjectFile::classify(file)))
            sources->append(file);
        else
            headers->append(file);
    }
}

void indexFindErrors(QFutureInterface<void> &future, const ParseParams params)
{
    QStringList sources, headers;
149 150 151 152
    classifyFiles(params.sourceFiles, &headers, &sources);
    sources.sort();
    headers.sort();
    QStringList files = sources + headers;
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

    WriteTaskFileForDiagnostics taskFileWriter;
    QElapsedTimer timer;
    timer.start();

    for (int i = 0, end = files.size(); i < end ; ++i) {
        if (future.isPaused())
            future.waitForResume();
        if (future.isCanceled())
            break;

        const QString file = files.at(i);
        qDebug("FindErrorsIndexing: \"%s\"", qPrintable(file));

        // Parse the file as precisely as possible
168 169 170 171
        BuiltinEditorDocumentParser parser(file);
        parser.setReleaseSourceAndAST(false);
        parser.update(params.workingCopy);
        CPlusPlus::Document::Ptr document = parser.document();
172
        QTC_ASSERT(document, return);
173

174 175 176 177
        // Write diagnostic messages
        taskFileWriter.process(document);

        // Look up symbols
178
        CPlusPlus::LookupContext context(document, parser.snapshot());
179 180 181 182 183 184 185 186 187 188 189 190 191 192
        CheckSymbols::go(document, context, QList<CheckSymbols::Result>()).waitForFinished();

        document->releaseSourceAndAST();

        future.setProgressValue(files.size() - (files.size() - (i + 1)));
    }

    const QTime format = QTime(0, 0, 0, 0).addMSecs(timer.elapsed() + 500);
    const QString time = format.toString(QLatin1String("hh:mm:ss"));
    qDebug("FindErrorsIndexing: Finished after %s.", qPrintable(time));
}

void index(QFutureInterface<void> &future, const ParseParams params)
{
193 194 195 196 197 198
    QScopedPointer<CppSourceProcessor> sourceProcessor(CppModelManager::createSourceProcessor());
    sourceProcessor->setDumpFileNameWhileParsing(params.dumpFileNameWhileParsing);
    sourceProcessor->setRevision(params.revision);
    sourceProcessor->setHeaderPaths(params.headerPaths);
    sourceProcessor->setWorkingCopy(params.workingCopy);

199

200 201
    QStringList sources;
    QStringList headers;
202
    classifyFiles(params.sourceFiles, &headers, &sources);
203

204
    foreach (const QString &file, params.sourceFiles)
205
        sourceProcessor->removeFromCache(file);
206 207

    const int sourceCount = sources.size();
208
    QStringList files = sources + headers;
209

210
    sourceProcessor->setTodo(files.toSet());
211

212
    const QString conf = CppModelManager::configurationFileName();
213 214
    bool processingHeaders = false;

215
    CppModelManager *cmm = CppModelManager::instance();
216
    const ProjectPart::HeaderPaths fallbackHeaderPaths = cmm->headerPaths();
217 218 219 220 221 222 223 224 225 226
    for (int i = 0; i < files.size(); ++i) {
        if (future.isPaused())
            future.waitForResume();

        if (future.isCanceled())
            break;

        const QString fileName = files.at(i);

        const bool isSourceFile = i < sourceCount;
227
        if (isSourceFile) {
228
            (void) sourceProcessor->run(conf);
229
        } else if (!processingHeaders) {
230
            (void) sourceProcessor->run(conf);
231 232 233 234

            processingHeaders = true;
        }

235
        QList<ProjectPart::Ptr> parts = cmm->projectPart(fileName);
236 237 238 239
        ProjectPart::HeaderPaths headerPaths = parts.isEmpty()
                ? fallbackHeaderPaths
                : parts.first()->headerPaths;
        sourceProcessor->setHeaderPaths(headerPaths);
240
        sourceProcessor->run(fileName);
241

242
        future.setProgressValue(files.size() - sourceProcessor->todo().size());
243 244

        if (isSourceFile)
245
            sourceProcessor->resetEnvironment();
246
    }
247 248 249 250
}

void parse(QFutureInterface<void> &future, const ParseParams params)
{
251
    const QSet<QString> &files = params.sourceFiles;
252 253 254 255 256 257 258 259 260
    if (files.isEmpty())
        return;

    future.setProgressRange(0, files.size());

    if (FindErrorsIndexing)
        indexFindErrors(future, params);
    else
        index(future, params);
261 262

    future.setProgressValue(files.size());
263
    CppModelManager::instance()->finishedRefreshingSourceFiles(files);
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
}

class BuiltinSymbolSearcher: public SymbolSearcher
{
public:
    BuiltinSymbolSearcher(const CPlusPlus::Snapshot &snapshot,
                          Parameters parameters, QSet<QString> fileNames)
        : m_snapshot(snapshot)
        , m_parameters(parameters)
        , m_fileNames(fileNames)
    {}

    ~BuiltinSymbolSearcher()
    {}

279
    void runSearch(QFutureInterface<Core::SearchResultItem> &future)
280 281 282 283 284
    {
        future.setProgressRange(0, m_snapshot.size());
        future.setProgressValue(0);
        int progress = 0;

285
        SearchSymbols search(CppToolsPlugin::stringTable());
286 287 288
        search.setSymbolsToSearchFor(m_parameters.types);
        CPlusPlus::Snapshot::const_iterator it = m_snapshot.begin();

289
        QString findString = (m_parameters.flags & Core::FindRegularExpression
290
                              ? m_parameters.text : QRegExp::escape(m_parameters.text));
291
        if (m_parameters.flags & Core::FindWholeWords)
292
            findString = QString::fromLatin1("\\b%1\\b").arg(findString);
293
        QRegExp matcher(findString, (m_parameters.flags & Core::FindCaseSensitively
294 295 296 297 298 299 300
                                     ? Qt::CaseSensitive : Qt::CaseInsensitive));
        while (it != m_snapshot.end()) {
            if (future.isPaused())
                future.waitForResume();
            if (future.isCanceled())
                break;
            if (m_fileNames.isEmpty() || m_fileNames.contains(it.value()->fileName())) {
301
                QVector<Core::SearchResultItem> resultItems;
302
                auto filter = [&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
303
                    if (matcher.indexIn(info->symbolName()) != -1) {
304 305
                        QString text = info->symbolName();
                        QString scope = info->symbolScope();
306
                        if (info->type() == IndexItem::Function) {
307
                            QString name;
308 309
                            info->unqualifiedNameAndScope(info->symbolName(), &name, &scope);
                            text = name + info->symbolType();
310
                        } else if (info->type() == IndexItem::Declaration){
311
                            text = info->representDeclaration();
312
                        }
313

314
                        Core::SearchResultItem item;
315
                        item.path = scope.split(QLatin1String("::"), QString::SkipEmptyParts);
316
                        item.text = text;
317 318
                        item.textMarkPos = -1;
                        item.textMarkLength = 0;
319
                        item.icon = info->icon();
320 321 322 323
                        item.lineNumber = -1;
                        item.userData = qVariantFromValue(info);
                        resultItems << item;
                    }
324 325 326 327

                    return IndexItem::Recurse;
                };
                search(it.value())->visitAllChildren(filter);
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
                if (!resultItems.isEmpty())
                    future.reportResults(resultItems);
            }
            ++it;
            ++progress;
            future.setProgressValue(progress);
        }
        if (future.isPaused())
            future.waitForResume();
    }

private:
    const CPlusPlus::Snapshot m_snapshot;
    const Parameters m_parameters;
    const QSet<QString> m_fileNames;
};

} // anonymous namespace

347 348
BuiltinIndexingSupport::BuiltinIndexingSupport()
    : m_revision(0)
349 350 351 352 353 354 355
{
    m_synchronizer.setCancelOnWait(true);
}

BuiltinIndexingSupport::~BuiltinIndexingSupport()
{}

356
QFuture<void> BuiltinIndexingSupport::refreshSourceFiles(const QSet<QString> &sourceFiles,
357
    CppModelManager::ProgressNotificationMode mode)
358 359 360
{
    CppModelManager *mgr = CppModelManager::instance();

361 362 363 364 365
    ParseParams params;
    params.revision = ++m_revision;
    params.headerPaths = mgr->headerPaths();
    params.workingCopy = mgr->workingCopy();
    params.sourceFiles = sourceFiles;
366

367
    QFuture<void> result = QtConcurrent::run(&parse, params);
368 369 370 371 372 373 374

    if (m_synchronizer.futures().size() > 10) {
        QList<QFuture<void> > futures = m_synchronizer.futures();

        m_synchronizer.clearFutures();

        foreach (const QFuture<void> &future, futures) {
375
            if (!(future.isFinished() || future.isCanceled()))
376 377 378 379 380 381
                m_synchronizer.addFuture(future);
        }
    }

    m_synchronizer.addFuture(result);

382
    if (mode == CppModelManager::ForcedProgressNotification || sourceFiles.count() > 1) {
383
        Core::ProgressManager::addTask(result, QCoreApplication::translate("CppTools::Internal::BuiltinIndexingSupport", "Parsing C/C++ Files"),
384
                                                CppTools::Constants::TASK_INDEX);
385 386 387 388 389 390 391 392 393
    }

    return result;
}

SymbolSearcher *BuiltinIndexingSupport::createSymbolSearcher(SymbolSearcher::Parameters parameters, QSet<QString> fileNames)
{
    return new BuiltinSymbolSearcher(CppModelManager::instance()->snapshot(), parameters, fileNames);
}
394 395 396 397 398

bool BuiltinIndexingSupport::isFindErrorsIndexingActive()
{
    return FindErrorsIndexing;
}