cppmodelmanager.cpp 44.1 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

Roberto Raggi's avatar
Roberto Raggi committed
30
#include <cplusplus/pp.h>
Roberto Raggi's avatar
Roberto Raggi committed
31
#include <cplusplus/CppBindings.h>
32
#include <cplusplus/Overview.h>
33
#include <cplusplus/CheckUndefinedSymbols.h>
con's avatar
con committed
34
35

#include "cppmodelmanager.h"
Wolfgang Beck's avatar
Wolfgang Beck committed
36
37
38
39
40
#ifndef ICHECK_BUILD
#  include "cpptoolsconstants.h"
#  include "cpptoolseditorsupport.h"
#  include "cppfindreferences.h"
#endif
con's avatar
con committed
41

42
43
#include <functional>
#include <QtConcurrentRun>
Wolfgang Beck's avatar
Wolfgang Beck committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#ifndef ICHECK_BUILD
#  include <QFutureSynchronizer>
#  include <qtconcurrent/runextensions.h>
#  include <texteditor/itexteditor.h>
#  include <texteditor/basetexteditor.h>
#  include <projectexplorer/project.h>
#  include <projectexplorer/projectexplorer.h>
#  include <projectexplorer/projectexplorerconstants.h>
#  include <projectexplorer/session.h>
#  include <coreplugin/icore.h>
#  include <coreplugin/uniqueidmanager.h>
#  include <coreplugin/mimedatabase.h>
#  include <coreplugin/editormanager/editormanager.h>
#  include <coreplugin/progressmanager/progressmanager.h>
#  include <extensionsystem/pluginmanager.h>
#else
#  include <QDir>
#endif
62

hjk's avatar
hjk committed
63
64
#include <utils/qtcassert.h>

con's avatar
con committed
65
66
67
68
69
70
71
72
73
#include <TranslationUnit.h>
#include <Semantic.h>
#include <AST.h>
#include <Scope.h>
#include <Literals.h>
#include <Symbols.h>
#include <Names.h>
#include <NameVisitor.h>
#include <TypeVisitor.h>
74
#include <ASTVisitor.h>
con's avatar
con committed
75
76
#include <Lexer.h>
#include <Token.h>
77
78
#include <Parser.h>
#include <Control.h>
con's avatar
con committed
79

80
81
#include <cplusplus/LookupContext.h>

82
#include <QtCore/QCoreApplication>
hjk's avatar
hjk committed
83
84
85
#include <QtCore/QDebug>
#include <QtCore/QMutexLocker>
#include <QtCore/QTime>
Roberto Raggi's avatar
Roberto Raggi committed
86
#include <QtCore/QTimer>
87
#include <QtConcurrentMap>
88
89
#include <iostream>
#include <sstream>
hjk's avatar
hjk committed
90

91
92
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
93
94
using namespace CPlusPlus;

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
#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)

#include <cxxabi.h>

class DumpAST: protected ASTVisitor
{
public:
    int depth;

    DumpAST(Control *control)
        : ASTVisitor(control), depth(0)
    { }

    void operator()(AST *ast)
    { accept(ast); }

protected:
    virtual bool preVisit(AST *ast)
    {
        std::ostringstream s;
        PrettyPrinter pp(control(), s);
        pp(ast);
        QString code = QString::fromStdString(s.str());
        code.replace('\n', ' ');
        code.replace(QRegExp("\\s+"), " ");

        const char *name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11;

        QByteArray ind(depth, ' ');
        ind += name;

        printf("%-40s %s\n", ind.constData(), qPrintable(code));
        ++depth;
        return true;
    }

    virtual void postVisit(AST *)
    { --depth; }
};

Roberto Raggi's avatar
Roberto Raggi committed
135
#endif // QTCREATOR_WITH_DUMP_AST
136

con's avatar
con committed
137
138
139
140
141
142
143
144
145
146
static const char pp_configuration_file[] = "<configuration>";

static const char pp_configuration[] =
    "# 1 \"<configuration>\"\n"
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
147
    "#define __restrict__\n"
con's avatar
con committed
148

149
150
151
152
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

153
154
    "#define __builtin_va_arg(a,b) ((b)0)\n"

con's avatar
con committed
155
156
    // ### add macros for win32
    "#define __cdecl\n"
157
    "#define __stdcall\n"
con's avatar
con committed
158
159
160
161
162
163
164
165
166
167
    "#define QT_WA(x) x\n"
    "#define API\n"
    "#define WINAPI\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define APIENTRY\n"
    "#define __declspec(a)\n"
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n";

168
#ifndef ICHECK_BUILD
169
CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager)
170
171
    : snapshot(modelManager->snapshot()),
      m_modelManager(modelManager),
172
173
      preprocess(this, &env),
      m_revision(0)
174
{ }
Roberto Raggi's avatar
Roberto Raggi committed
175

176
177
178
179
180
181
182
183
184
#else

CppPreprocessor::CppPreprocessor(QPointer<CPlusPlus::ParseManager> modelManager)
    : preprocess(this, &env),
      m_revision(0)
{
}
#endif

Roberto Raggi's avatar
Roberto Raggi committed
185
CppPreprocessor::~CppPreprocessor()
186
{ }
con's avatar
con committed
187

188
189
190
void CppPreprocessor::setRevision(unsigned revision)
{ m_revision = revision; }

191
void CppPreprocessor::setWorkingCopy(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy)
192
{ m_workingCopy = workingCopy; }
con's avatar
con committed
193

194
void CppPreprocessor::setIncludePaths(const QStringList &includePaths)
195
196
197
198
{
    m_includePaths.clear();

    for (int i = 0; i < includePaths.size(); ++i) {
199
        const QString &path = includePaths.at(i);
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

#ifdef Q_OS_DARWIN
        if (i + 1 < includePaths.size() && path.endsWith(QLatin1String(".framework/Headers"))) {
            const QFileInfo pathInfo(path);
            const QFileInfo frameworkFileInfo(pathInfo.path());
            const QString frameworkName = frameworkFileInfo.baseName();

            const QFileInfo nextIncludePath = includePaths.at(i + 1);
            if (nextIncludePath.fileName() == frameworkName) {
                // We got a QtXXX.framework/Headers followed by $QTDIR/include/QtXXX.
                // In this case we prefer to include files from $QTDIR/include/QtXXX.
                continue;
            }
        }
        m_includePaths.append(path);
#else
        m_includePaths.append(path);
#endif
    }
}
220
221

void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths)
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
{
    m_frameworkPaths.clear();

    foreach (const QString &frameworkPath, frameworkPaths) {
        addFrameworkPath(frameworkPath);
    }
}

// Add the given framework path, and expand private frameworks.
//
// Example:
//  <framework-path>/ApplicationServices.framework
// has private frameworks in:
//  <framework-path>/ApplicationServices.framework/Frameworks
// if the "Frameworks" folder exists inside the top level framework.
void CppPreprocessor::addFrameworkPath(const QString &frameworkPath)
{
    // The algorithm below is a bit too eager, but that's because we're not getting
    // in the frameworks we're linking against. If we would have that, then we could
    // add only those private frameworks.
    if (!m_frameworkPaths.contains(frameworkPath)) {
        m_frameworkPaths.append(frameworkPath);
    }

    const QDir frameworkDir(frameworkPath);
    const QStringList filter = QStringList() << QLatin1String("*.framework");
    foreach (const QFileInfo &framework, frameworkDir.entryInfoList(filter)) {
        if (!framework.isDir())
            continue;
        const QFileInfo privateFrameworks(framework.absoluteFilePath(), QLatin1String("Frameworks"));
        if (privateFrameworks.exists() && privateFrameworks.isDir()) {
            addFrameworkPath(privateFrameworks.absoluteFilePath());
        }
    }
}
con's avatar
con committed
257

258
259
260
void CppPreprocessor::setProjectFiles(const QStringList &files)
{ m_projectFiles = files; }

261
262
263
void CppPreprocessor::setTodo(const QStringList &files)
{ m_todo = QSet<QString>::fromList(files); }

264
#ifndef ICHECK_BUILD
Wolfgang Beck's avatar
Wolfgang Beck committed
265
namespace {
266
class Process: public std::unary_function<Document::Ptr, void>
267
268
{
    QPointer<CppModelManager> _modelManager;
269
    Snapshot _snapshot;
270
    CppModelManager::WorkingCopy _workingCopy;
271
    Document::Ptr _doc;
272
273

public:
274
275
    Process(QPointer<CppModelManager> modelManager,
            Snapshot snapshot,
276
            const CppModelManager::WorkingCopy &workingCopy)
277
278
279
        : _modelManager(modelManager),
          _snapshot(snapshot),
          _workingCopy(workingCopy)
280
281
    { }

282
283
284
285
    LookupContext lookupContext(unsigned line, unsigned column) const
    { return lookupContext(_doc->findSymbolAt(line, column)); }

    LookupContext lookupContext(Symbol *symbol) const
286
    {
Roberto Raggi's avatar
Roberto Raggi committed
287
        LookupContext context(symbol, Document::create(QLatin1String("<none>")), _doc, _snapshot);
288
289
290
291
292
293
        return context;
    }

    void operator()(Document::Ptr doc)
    {
        _doc = doc;
294
295
296
297
298
299

        Document::CheckMode mode = Document::FastCheck;

        if (_workingCopy.contains(doc->fileName()))
            mode = Document::FullCheck;

300
301
302
        if (doc->isParsed() && mode == Document::FastCheck) {
            TranslationUnit *unit = doc->translationUnit();
            MemoryPool *pool = unit->memoryPool();
303

304
305
            Parser parser(unit);
            Semantic semantic(unit);
Roberto Raggi's avatar
Roberto Raggi committed
306

307
308
            Namespace *globalNamespace = doc->control()->newNamespace(0);
            doc->setGlobalNamespace(globalNamespace);
309

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
            Scope *globals = globalNamespace->members();

            while (parser.LA()) {
                unsigned start_declaration = parser.cursor();
                DeclarationAST *declaration = 0;

                if (parser.parseDeclaration(declaration)) {
                    semantic.check(declaration, globals);

                } else {
                    doc->translationUnit()->error(start_declaration, "expected a declaration");
                    parser.rewind(start_declaration + 1);
                    parser.skipUntilDeclaration();
                }

                parser.clearTemplateArgumentList();
                pool->reset();
            }

        } else {
            doc->parse();
            doc->check(mode);

            if (mode == Document::FullCheck) {
                // run the binding pass
                NamespaceBindingPtr ns = bind(doc, _snapshot);

                // check for undefined symbols.
                CheckUndefinedSymbols checkUndefinedSymbols(doc);
                checkUndefinedSymbols.setGlobalNamespaceBinding(ns);

                checkUndefinedSymbols(doc->translationUnit()->ast()); // ### FIXME
            }
343
344
        }

345
346
347
348
349
350
351
        doc->releaseTranslationUnit();

        if (_modelManager)
            _modelManager->emitDocumentUpdated(doc); // ### TODO: compress
    }
};
} // end of anonymous namespace
Wolfgang Beck's avatar
Wolfgang Beck committed
352
#endif
353
354
355
356
357
358

void CppPreprocessor::run(const QString &fileName)
{
    QString absoluteFilePath = fileName;
    sourceNeeded(absoluteFilePath, IncludeGlobal, /*line = */ 0);
}
359

360
void CppPreprocessor::resetEnvironment()
361
362
363
364
{
    env.reset();
    m_processed.clear();
}
365

366
bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision)
367
{
368
    if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath))
369
370
371
372
        return true;

    if (m_workingCopy.contains(absoluteFilePath)) {
        m_included.insert(absoluteFilePath);
373
374
375
        const QPair<QString, unsigned> r = m_workingCopy.get(absoluteFilePath);
        *result = r.first;
        *revision = r.second;
376
377
378
379
380
        return true;
    }

    QFileInfo fileInfo(absoluteFilePath);
    if (! fileInfo.isFile())
con's avatar
con committed
381
        return false;
382
383
384
385
386
387
388
389
390

    QFile file(absoluteFilePath);
    if (file.open(QFile::ReadOnly)) {
        m_included.insert(absoluteFilePath);
        QTextStream stream(&file);
        const QString contents = stream.readAll();
        *result = contents.toUtf8();
        file.close();
        return true;
con's avatar
con committed
391
392
    }

393
394
395
    return false;
}

396
QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
397
398
399
{
    QFileInfo fileInfo(fileName);
    if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) {
400
        QString contents;
401
        includeFile(fileName, &contents, revision);
402
403
404
405
406
407
408
409
410
        return contents;
    }

    if (type == IncludeLocal && m_currentDoc) {
        QFileInfo currentFileInfo(m_currentDoc->fileName());
        QString path = currentFileInfo.absolutePath();
        path += QLatin1Char('/');
        path += fileName;
        path = QDir::cleanPath(path);
411
        QString contents;
412
        if (includeFile(path, &contents, revision)) {
413
            fileName = path;
con's avatar
con committed
414
415
            return contents;
        }
416
    }
con's avatar
con committed
417

418
419
    foreach (const QString &includePath, m_includePaths) {
        QString path = includePath;
420
421
422
        path += QLatin1Char('/');
        path += fileName;
        path = QDir::cleanPath(path);
423
        QString contents;
424
        if (includeFile(path, &contents, revision)) {
425
426
            fileName = path;
            return contents;
con's avatar
con committed
427
        }
428
    }
con's avatar
con committed
429

430
431
432
433
434
435
    // look in the system include paths
    foreach (const QString &includePath, m_systemIncludePaths) {
        QString path = includePath;
        path += QLatin1Char('/');
        path += fileName;
        path = QDir::cleanPath(path);
436
        QString contents;
437
        if (includeFile(path, &contents, revision)) {
438
439
            fileName = path;
            return contents;
con's avatar
con committed
440
        }
441
442
443
444
445
446
    }

    int index = fileName.indexOf(QLatin1Char('/'));
    if (index != -1) {
        QString frameworkName = fileName.left(index);
        QString name = fileName.mid(index + 1);
con's avatar
con committed
447

448
449
        foreach (const QString &frameworkPath, m_frameworkPaths) {
            QString path = frameworkPath;
con's avatar
con committed
450
            path += QLatin1Char('/');
451
452
453
            path += frameworkName;
            path += QLatin1String(".framework/Headers/");
            path += name;
454
            path = QDir::cleanPath(path);
455
            QString contents;
456
            if (includeFile(path, &contents, revision)) {
con's avatar
con committed
457
458
459
460
                fileName = path;
                return contents;
            }
        }
461
    }
con's avatar
con committed
462

463
464
465
    QString path = fileName;
    if (path.at(0) != QLatin1Char('/'))
        path.prepend(QLatin1Char('/'));
con's avatar
con committed
466

467
468
469
    foreach (const QString &projectFile, m_projectFiles) {
        if (projectFile.endsWith(path)) {
            fileName = projectFile;
470
            QString contents;
471
            includeFile(fileName, &contents, revision);
472
            return contents;
con's avatar
con committed
473
474
475
        }
    }

476
    //qDebug() << "**** file" << fileName << "not found!";
477
    return QString();
478
}
con's avatar
con committed
479

Roberto Raggi's avatar
Roberto Raggi committed
480
void CppPreprocessor::macroAdded(const Macro &macro)
481
482
483
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
484

Roberto Raggi's avatar
Roberto Raggi committed
485
    m_currentDoc->appendMacro(macro);
486
}
487

Christian Kamm's avatar
Christian Kamm committed
488
489
490
491
492
void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro &macro)
{
    if (! m_currentDoc)
        return;

493
    m_currentDoc->addMacroUse(macro, offset, macro.name().length(), env.currentLine,
Christian Kamm's avatar
Christian Kamm committed
494
495
496
497
498
499
500
501
502
503
504
                              QVector<MacroArgumentReference>(), true);
}

void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name)
{
    if (! m_currentDoc)
        return;

    m_currentDoc->addUndefinedMacroUse(name, offset);
}

505
void CppPreprocessor::startExpandingMacro(unsigned offset,
Roberto Raggi's avatar
Roberto Raggi committed
506
                                          const Macro &macro,
507
                                          const QByteArray &originalText,
Christian Kamm's avatar
Christian Kamm committed
508
                                          bool inCondition,
509
                                          const QVector<MacroArgumentReference> &actuals)
510
511
512
{
    if (! m_currentDoc)
        return;
513

Christian Kamm's avatar
Christian Kamm committed
514
    //qDebug() << "start expanding:" << macro.name() << "text:" << originalText;
515
516
    m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine,
                              actuals, inCondition);
517
}
518

519
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
520
521
522
{
    if (! m_currentDoc)
        return;
con's avatar
con committed
523

524
525
    //qDebug() << "stop expanding:" << macro.name;
}
con's avatar
con committed
526

527
528
529
530
void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
{
    if (! doc)
        return;
con's avatar
con committed
531

532
    const QString fn = doc->fileName();
con's avatar
con committed
533

534
    if (m_processed.contains(fn))
535
        return;
con's avatar
con committed
536

537
    m_processed.insert(fn);
con's avatar
con committed
538

539
    foreach (const Document::Include &incl, doc->includes()) {
540
541
        QString includedFile = incl.fileName();

542
        if (Document::Ptr includedDoc = snapshot.document(includedFile))
543
            mergeEnvironment(includedDoc);
544
545
        else
            run(includedFile);
Roberto Raggi's avatar
Roberto Raggi committed
546
    }
con's avatar
con committed
547

548
    env.addMacros(doc->definedMacros());
549
}
con's avatar
con committed
550

551
552
553
554
555
556
557
558
559
560
561
562
563
564
void CppPreprocessor::startSkippingBlocks(unsigned offset)
{
    //qDebug() << "start skipping blocks:" << offset;
    if (m_currentDoc)
        m_currentDoc->startSkippingBlocks(offset);
}

void CppPreprocessor::stopSkippingBlocks(unsigned offset)
{
    //qDebug() << "stop skipping blocks:" << offset;
    if (m_currentDoc)
        m_currentDoc->stopSkippingBlocks(offset);
}

565
void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned line)
566
567
568
569
{
    if (fileName.isEmpty())
        return;

570
571
    unsigned editorRevision = 0;
    QString contents = tryIncludeFile(fileName, type, &editorRevision);
572
    fileName = QDir::cleanPath(fileName);
573
    if (m_currentDoc) {
574
        m_currentDoc->addIncludeFile(fileName, line);
575

576
        if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
577
578
            QString msg = QCoreApplication::translate(
                    "CppPreprocessor", "%1: No such file or directory").arg(fileName);
579

580
581
582
583
            Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
                                          m_currentDoc->fileName(),
                                          env.currentLine, /*column = */ 0,
                                          msg);
584

585
            m_currentDoc->addDiagnosticMessage(d);
586

587
            //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
con's avatar
con committed
588
        }
589
    }
con's avatar
con committed
590

591
    //qDebug() << "parse file:" << fileName << "contents:" << contents.size();
con's avatar
con committed
592

593
    Document::Ptr doc = snapshot.document(fileName);
594
595
596
597
    if (doc) {
        mergeEnvironment(doc);
        return;
    }
con's avatar
con committed
598

599
    doc = Document::create(fileName);
600
    doc->setRevision(m_revision);
601
    doc->setEditorRevision(editorRevision);
con's avatar
con committed
602

603
604
605
606
    QFileInfo info(fileName);
    if (info.exists())
        doc->setLastModified(info.lastModified());

607
    Document::Ptr previousDoc = switchDocument(doc);
con's avatar
con committed
608

609
    const QByteArray preprocessedCode = preprocess(fileName, contents);
con's avatar
con committed
610

611
    doc->setSource(preprocessedCode);
612
613
    doc->tokenize();
    doc->releaseSource();
614

615
    snapshot.insert(doc);
616
    m_todo.remove(fileName);
con's avatar
con committed
617

618
#ifndef ICHECK_BUILD
619
    Process process(m_modelManager, snapshot, m_workingCopy);
620
621

    process(doc);
622
623

    (void) switchDocument(previousDoc);
624
625
626
627
628
629
630
#else
    (void) switchDocument(previousDoc);
    Document::CheckMode mode = Document::FastCheck;
    mode = Document::FullCheck;
    doc->parse();
    doc->check(mode);
#endif
631
}
con's avatar
con committed
632

633
634
635
636
637
638
Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
{
    Document::Ptr previousDoc = m_currentDoc;
    m_currentDoc = doc;
    return previousDoc;
}
con's avatar
con committed
639

640
#ifndef ICHECK_BUILD
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
void CppTools::CppModelManagerInterface::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QStringList sourceFiles;

    foreach (const Document::Ptr doc, snapshot) {
        const QDateTime lastModified = doc->lastModified();

        if (! lastModified.isNull()) {
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
                sourceFiles.append(doc->fileName());
        }
    }

    updateSourceFiles(sourceFiles);
}

660
661
662
663
664
665
666
CppTools::CppModelManagerInterface *CppTools::CppModelManagerInterface::instance()
{
    ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance();
    return pluginManager->getObject<CppTools::CppModelManagerInterface>();

}

con's avatar
con committed
667
668
669
670
671
672
673

/*!
    \class CppTools::CppModelManager
    \brief The CppModelManager keeps track of one CppCodeModel instance
           for each project and all related CppCodeModelPart instances.

    It also takes care of updating the code models when C++ files are
674
    modified within Qt Creator.
con's avatar
con committed
675
676
*/

677
CppModelManager::CppModelManager(QObject *parent)
678
    : CppModelManagerInterface(parent)
con's avatar
con committed
679
{
Roberto Raggi's avatar
Roberto Raggi committed
680
    m_findReferences = new CppFindReferences(this);
681
    m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
Roberto Raggi's avatar
Roberto Raggi committed
682

683
    m_revision = 0;
Roberto Raggi's avatar
Roberto Raggi committed
684
685
    m_synchronizer.setCancelOnWait(true);

686
    m_core = Core::ICore::instance(); // FIXME
687
688
    m_dirty = true;

689
    ProjectExplorer::ProjectExplorerPlugin *pe =
690
       ProjectExplorer::ProjectExplorerPlugin::instance();
con's avatar
con committed
691

692
    QTC_ASSERT(pe, return);
con's avatar
con committed
693

694
    ProjectExplorer::SessionManager *session = pe->session();
hjk's avatar
hjk committed
695
    QTC_ASSERT(session, return);
con's avatar
con committed
696

Roberto Raggi's avatar
Roberto Raggi committed
697
698
699
700
701
702
    m_updateEditorSelectionsTimer = new QTimer(this);
    m_updateEditorSelectionsTimer->setInterval(500);
    m_updateEditorSelectionsTimer->setSingleShot(true);
    connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
            this, SLOT(updateEditorSelections()));

703
704
705
    connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));

con's avatar
con committed
706
707
708
    connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)),
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project *)));

709
710
    connect(session, SIGNAL(aboutToUnloadSession()),
            this, SLOT(onAboutToUnloadSession()));
con's avatar
con committed
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728

    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");

    // thread connections
    connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));

    // Listen for editor closed and opened events so that we can keep track of changing files
    connect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)),
        this, SLOT(editorOpened(Core::IEditor *)));

    connect(m_core->editorManager(), SIGNAL(editorAboutToClose(Core::IEditor *)),
        this, SLOT(editorAboutToClose(Core::IEditor *)));
}

CppModelManager::~CppModelManager()
{ }

729
Snapshot CppModelManager::snapshot() const
730
{
731
    QMutexLocker locker(&protectSnapshot);
732
733
    return m_snapshot;
}
con's avatar
con committed
734

735
736
737
738
739
740
void CppModelManager::ensureUpdated()
{
    QMutexLocker locker(&mutex);
    if (! m_dirty)
        return;

741
742
743
744
    m_projectFiles = internalProjectFiles();
    m_includePaths = internalIncludePaths();
    m_frameworkPaths = internalFrameworkPaths();
    m_definedMacros = internalDefinedMacros();
745
746
747
    m_dirty = false;
}

748
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
749
750
751
752
753
754
755
756
{
    QStringList files;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
        files += pinfo.sourceFiles;
    }
757
    files.removeDuplicates();
con's avatar
con committed
758
759
760
    return files;
}

761
QStringList CppModelManager::internalIncludePaths() const
con's avatar
con committed
762
763
764
765
766
767
768
769
{
    QStringList includePaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
        includePaths += pinfo.includePaths;
    }
770
    includePaths.removeDuplicates();
con's avatar
con committed
771
772
773
    return includePaths;
}

774
QStringList CppModelManager::internalFrameworkPaths() const
con's avatar
con committed
775
776
777
778
779
780
781
782
{
    QStringList frameworkPaths;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
        frameworkPaths += pinfo.frameworkPaths;
    }
783
    frameworkPaths.removeDuplicates();
con's avatar
con committed
784
785
786
    return frameworkPaths;
}

787
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
788
789
790
791
792
793
794
795
796
797
798
{
    QByteArray macros;
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
    while (it.hasNext()) {
        it.next();
        ProjectInfo pinfo = it.value();
        macros += pinfo.defines;
    }
    return macros;
}

799
void CppModelManager::setIncludesInPaths(const QMap<QString, QStringList> &includesInPaths)
800
801
802
803
804
805
806
807
808
{
    QMutexLocker locker(&mutex);
    QMapIterator<QString, QStringList> i(includesInPaths);
    while (i.hasNext()) {
        i.next();
        m_includesInPaths.insert(i.key(), i.value());
    }
}

809
810
811
812
813
814
815
816
817
818
void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.insert(editorSupport);
}

void CppModelManager::removeEditorSupport(AbstractEditorSupport *editorSupport)
{
    m_addtionalEditorSupport.remove(editorSupport);
}

819
820
821
822
QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol,
                                       CPlusPlus::Document::Ptr doc,
                                       const CPlusPlus::Snapshot &snapshot)
{
823
824
    NamespaceBindingPtr glo = bind(doc, snapshot);
    return m_findReferences->references(LookupContext::canonicalSymbol(symbol, glo.data()), doc, snapshot);
825
826
}

Erik Verbruggen's avatar
Erik Verbruggen committed
827
void CppModelManager::findUsages(CPlusPlus::Document::Ptr symbolDocument, CPlusPlus::Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
828
{
829
    if (symbol->identifier())
Erik Verbruggen's avatar
Erik Verbruggen committed
830
        m_findReferences->findUsages(symbolDocument, symbol);
831
832
}

Erik Verbruggen's avatar
Erik Verbruggen committed
833
void CppModelManager::renameUsages(CPlusPlus::Document::Ptr symbolDocument, CPlusPlus::Symbol *symbol)
834
835
{
    if (symbol->identifier())
Erik Verbruggen's avatar
Erik Verbruggen committed
836
        m_findReferences->renameUsages(symbolDocument, symbol);
Roberto Raggi's avatar
Roberto Raggi committed
837
838
}

Christian Kamm's avatar
Christian Kamm committed
839
840
841
842
843
void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
{
    m_findReferences->findMacroUses(macro);
}

844
CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
845
{
846
    WorkingCopy workingCopy;
con's avatar
con committed
847
848
849
850
851
852
    QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
    while (it.hasNext()) {
        it.next();
        TextEditor::ITextEditor *textEditor = it.key();
        CppEditorSupport *editorSupport = it.value();
        QString fileName = textEditor->file()->fileName();
853
        workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
con's avatar
con committed
854
855
    }

856
857
858
    QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
    while (jt.hasNext()) {
        AbstractEditorSupport *es =  jt.next();
859
        workingCopy.insert(es->fileName(), es->contents());
860
861
    }

con's avatar
con committed
862
863
864
    // add the project configuration file
    QByteArray conf(pp_configuration);
    conf += definedMacros();
865
    workingCopy.insert(pp_configuration_file, conf);
con's avatar
con committed
866
867
868
869

    return workingCopy;
}

870
CppModelManager::WorkingCopy CppModelManager::workingCopy() const
871
872
873
874
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

dt's avatar
dt committed
875
876
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles)
{ return refreshSourceFiles(sourceFiles); }
con's avatar
con committed
877

878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const
{
    QMutexLocker locker(&mutex);

    return m_projects.values();
}

CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
{
    QMutexLocker locker(&mutex);

    return m_projects.value(project, ProjectInfo(project));
}

void CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
{
    QMutexLocker locker(&mutex);

    if (! pinfo.isValid())
        return;

    m_projects.insert(pinfo.project, pinfo);
900
    m_dirty = true;
901

902
    if (m_indexerEnabled) {
903
904
905
        QFuture<void> result = QtConcurrent::run(&CppModelManager::updateIncludesInPaths,
                                                 this,
                                                 pinfo.includePaths,
906
                                                 pinfo.frameworkPaths,
907
908
909
910
                                                 m_headerSuffixes);

        if (pinfo.includePaths.size() > 1) {
            m_core->progressManager()->addTask(result, tr("Scanning"),
911
                                               CppTools::Constants::TASK_INDEX);
912
        }
913
914
915
916
917
918
919
    }
}

QStringList CppModelManager::includesInPath(const QString &path) const
{
    QMutexLocker locker(&mutex);
    return m_includesInPaths.value(path);
920
}
con's avatar
con committed
921
922
923

QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles)
{
924
    if (! sourceFiles.isEmpty() && m_indexerEnabled) {
925
        const WorkingCopy workingCopy = buildWorkingCopyList();
con's avatar
con committed
926

927
        CppPreprocessor *preproc = new CppPreprocessor(this);
928
        preproc->setRevision(++m_revision);
929
930
931
932
933
934
935
        preproc->setProjectFiles(projectFiles());
        preproc->setIncludePaths(includePaths());
        preproc->setFrameworkPaths(frameworkPaths());
        preproc->setWorkingCopy(workingCopy);

        QFuture<void> result = QtConcurrent::run(&CppModelManager::parse,
                                                 preproc, sourceFiles);
con's avatar
con committed
936

Roberto Raggi's avatar
Roberto Raggi committed
937
938
939
940
941
        if (m_synchronizer.futures().size() > 10) {
            QList<QFuture<void> > futures = m_synchronizer.futures();

            m_synchronizer.clearFutures();

942
            foreach (const QFuture<void> &future, futures) {
Roberto Raggi's avatar
Roberto Raggi committed
943
944
945
946
947
                if (! (future.isFinished() || future.isCanceled()))
                    m_synchronizer.addFuture(future);
            }
        }

Roberto Raggi's avatar
Roberto Raggi committed
948
949
        m_synchronizer.addFuture(result);

950
        if (sourceFiles.count() > 1) {
con's avatar
con committed
951
            m_core->progressManager()->addTask(result, tr("Parsing"),
952
                            CppTools::Constants::TASK_INDEX);
con's avatar
con committed
953
        }
954

con's avatar
con committed
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
        return result;
    }
    return QFuture<void>();
}

/*!
    \fn    void CppModelManager::editorOpened(Core::IEditor *editor)
    \brief If a C++ editor is opened, the model manager listens to content changes
           in order to update the CppCodeModel accordingly. It also updates the
           CppCodeModel for the first time with this editor.

    \sa    void CppModelManager::editorContentsChanged()
 */
void CppModelManager::editorOpened(Core::IEditor *editor)
{
    if (isCppEditor(editor)) {
        TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
hjk's avatar
hjk committed
972
        QTC_ASSERT(textEditor, return);
con's avatar
con committed
973
974
975
976
977
978
979
980
981
982
983

        CppEditorSupport *editorSupport = new CppEditorSupport(this);
        editorSupport->setTextEditor(textEditor);
        m_editorSupport[textEditor] = editorSupport;
    }
}

void CppModelManager::editorAboutToClose(Core::IEditor *editor)
{
    if (isCppEditor(editor)) {
        TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
hjk's avatar
hjk committed
984
        QTC_ASSERT(textEditor, return);
con's avatar
con committed
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000

        CppEditorSupport *editorSupport = m_editorSupport.value(textEditor);
        m_editorSupport.remove(textEditor);
        delete editorSupport;
    }
}

bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
    Core::UniqueIDManager *uidm = m_core->uniqueIDManager();
    const int uid = uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
    return editor->context().contains(uid);
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{ emit documentUpdated(doc); }
For faster browsing, not all history is shown. View entire blame