tools/icheck/parser/src/plugins/cpptools/cppmodelmanager.cpp
changeset 0 876b1a06bc25
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <cplusplus/pp.h>
       
    43 #include <cplusplus/CppBindings.h>
       
    44 #include <cplusplus/Overview.h>
       
    45 #include <cplusplus/CheckUndefinedSymbols.h>
       
    46 
       
    47 #include "cppmodelmanager.h"
       
    48 #ifndef ICHECK_BUILD
       
    49 #  include "cpptoolsconstants.h"
       
    50 #  include "cpptoolseditorsupport.h"
       
    51 #  include "cppfindreferences.h"
       
    52 #endif
       
    53 
       
    54 #include <functional>
       
    55 #include <QtConcurrentRun>
       
    56 #ifndef ICHECK_BUILD
       
    57 #  include <QFutureSynchronizer>
       
    58 #  include <qtconcurrent/runextensions.h>
       
    59 #  include <texteditor/itexteditor.h>
       
    60 #  include <texteditor/basetexteditor.h>
       
    61 #  include <projectexplorer/project.h>
       
    62 #  include <projectexplorer/projectexplorer.h>
       
    63 #  include <projectexplorer/projectexplorerconstants.h>
       
    64 #  include <projectexplorer/session.h>
       
    65 #  include <coreplugin/icore.h>
       
    66 #  include <coreplugin/uniqueidmanager.h>
       
    67 #  include <coreplugin/mimedatabase.h>
       
    68 #  include <coreplugin/editormanager/editormanager.h>
       
    69 #  include <coreplugin/progressmanager/progressmanager.h>
       
    70 #  include <extensionsystem/pluginmanager.h>
       
    71 #else
       
    72 #  include <QDir>
       
    73 #endif
       
    74 
       
    75 #include <utils/qtcassert.h>
       
    76 
       
    77 #include <TranslationUnit.h>
       
    78 #include <Semantic.h>
       
    79 #include <AST.h>
       
    80 #include <Scope.h>
       
    81 #include <Literals.h>
       
    82 #include <Symbols.h>
       
    83 #include <Names.h>
       
    84 #include <NameVisitor.h>
       
    85 #include <TypeVisitor.h>
       
    86 #include <ASTVisitor.h>
       
    87 #include <Lexer.h>
       
    88 #include <Token.h>
       
    89 
       
    90 #include <cplusplus/LookupContext.h>
       
    91 
       
    92 #include <QtCore/QCoreApplication>
       
    93 #include <QtCore/QDebug>
       
    94 #include <QtCore/QMutexLocker>
       
    95 #include <QtCore/QTime>
       
    96 #include <QtCore/QTimer>
       
    97 #include <QtConcurrentMap>
       
    98 #include <iostream>
       
    99 #include <sstream>
       
   100 
       
   101 using namespace CppTools;
       
   102 using namespace CppTools::Internal;
       
   103 using namespace CPlusPlus;
       
   104 
       
   105 #if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)
       
   106 
       
   107 #include <cxxabi.h>
       
   108 
       
   109 class DumpAST: protected ASTVisitor
       
   110 {
       
   111 public:
       
   112     int depth;
       
   113 
       
   114     DumpAST(Control *control)
       
   115         : ASTVisitor(control), depth(0)
       
   116     { }
       
   117 
       
   118     void operator()(AST *ast)
       
   119     { accept(ast); }
       
   120 
       
   121 protected:
       
   122     virtual bool preVisit(AST *ast)
       
   123     {
       
   124         std::ostringstream s;
       
   125         PrettyPrinter pp(control(), s);
       
   126         pp(ast);
       
   127         QString code = QString::fromStdString(s.str());
       
   128         code.replace('\n', ' ');
       
   129         code.replace(QRegExp("\\s+"), " ");
       
   130 
       
   131         const char *name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11;
       
   132 
       
   133         QByteArray ind(depth, ' ');
       
   134         ind += name;
       
   135 
       
   136         printf("%-40s %s\n", ind.constData(), qPrintable(code));
       
   137         ++depth;
       
   138         return true;
       
   139     }
       
   140 
       
   141     virtual void postVisit(AST *)
       
   142     { --depth; }
       
   143 };
       
   144 
       
   145 #endif // QTCREATOR_WITH_DUMP_AST
       
   146 
       
   147 static const char pp_configuration_file[] = "<configuration>";
       
   148 
       
   149 static const char pp_configuration[] =
       
   150     "# 1 \"<configuration>\"\n"
       
   151     "#define __cplusplus 1\n"
       
   152     "#define __extension__\n"
       
   153     "#define __context__\n"
       
   154     "#define __range__\n"
       
   155     "#define   restrict\n"
       
   156     "#define __restrict\n"
       
   157     "#define __restrict__\n"
       
   158 
       
   159     "#define __complex__\n"
       
   160     "#define __imag__\n"
       
   161     "#define __real__\n"
       
   162 
       
   163     "#define __builtin_va_arg(a,b) ((b)0)\n"
       
   164 
       
   165     // ### add macros for win32
       
   166     "#define __cdecl\n"
       
   167     "#define __stdcall\n"
       
   168     "#define QT_WA(x) x\n"
       
   169     "#define API\n"
       
   170     "#define WINAPI\n"
       
   171     "#define CALLBACK\n"
       
   172     "#define STDMETHODCALLTYPE\n"
       
   173     "#define __RPC_FAR\n"
       
   174     "#define APIENTRY\n"
       
   175     "#define __declspec(a)\n"
       
   176     "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n";
       
   177 
       
   178 #ifndef ICHECK_BUILD
       
   179 CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager)
       
   180     : snapshot(modelManager->snapshot()),
       
   181       m_modelManager(modelManager),
       
   182       preprocess(this, &env),
       
   183       m_revision(0)
       
   184 { }
       
   185 
       
   186 #else
       
   187 
       
   188 CppPreprocessor::CppPreprocessor(QPointer<CPlusPlus::ParseManager> modelManager)
       
   189     : preprocess(this, &env),
       
   190       m_revision(0)
       
   191 {
       
   192 }
       
   193 #endif
       
   194 
       
   195 CppPreprocessor::~CppPreprocessor()
       
   196 { }
       
   197 
       
   198 void CppPreprocessor::setRevision(unsigned revision)
       
   199 { m_revision = revision; }
       
   200 
       
   201 void CppPreprocessor::setWorkingCopy(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy)
       
   202 { m_workingCopy = workingCopy; }
       
   203 
       
   204 void CppPreprocessor::setIncludePaths(const QStringList &includePaths)
       
   205 { m_includePaths = includePaths; }
       
   206 
       
   207 void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths)
       
   208 { m_frameworkPaths = frameworkPaths; }
       
   209 
       
   210 void CppPreprocessor::setProjectFiles(const QStringList &files)
       
   211 { m_projectFiles = files; }
       
   212 
       
   213 void CppPreprocessor::setTodo(const QStringList &files)
       
   214 { m_todo = QSet<QString>::fromList(files); }
       
   215 
       
   216 #ifndef ICHECK_BUILD
       
   217 namespace {
       
   218 class Process: public std::unary_function<Document::Ptr, void>
       
   219 {
       
   220     QPointer<CppModelManager> _modelManager;
       
   221     Snapshot _snapshot;
       
   222     CppModelManager::WorkingCopy _workingCopy;
       
   223     Document::Ptr _doc;
       
   224 
       
   225 public:
       
   226     Process(QPointer<CppModelManager> modelManager,
       
   227             Snapshot snapshot,
       
   228             const CppModelManager::WorkingCopy &workingCopy)
       
   229         : _modelManager(modelManager),
       
   230           _snapshot(snapshot),
       
   231           _workingCopy(workingCopy)
       
   232     { }
       
   233 
       
   234     LookupContext lookupContext(unsigned line, unsigned column) const
       
   235     { return lookupContext(_doc->findSymbolAt(line, column)); }
       
   236 
       
   237     LookupContext lookupContext(Symbol *symbol) const
       
   238     {
       
   239         LookupContext context(symbol, Document::create(QLatin1String("<none>")), _doc, _snapshot);
       
   240         return context;
       
   241     }
       
   242 
       
   243     void operator()(Document::Ptr doc)
       
   244     {
       
   245         _doc = doc;
       
   246 
       
   247         Document::CheckMode mode = Document::FastCheck;
       
   248 
       
   249         if (_workingCopy.contains(doc->fileName()))
       
   250             mode = Document::FullCheck;
       
   251 
       
   252         doc->parse();
       
   253         doc->check(mode);
       
   254 
       
   255         if (mode == Document::FullCheck) {
       
   256             // run the binding pass
       
   257             NamespaceBindingPtr ns = bind(doc, _snapshot);
       
   258 
       
   259             // check for undefined symbols.
       
   260             CheckUndefinedSymbols checkUndefinedSymbols(doc);
       
   261             checkUndefinedSymbols.setGlobalNamespaceBinding(ns);
       
   262 
       
   263             checkUndefinedSymbols(doc->translationUnit()->ast()); // ### FIXME
       
   264         }
       
   265 
       
   266         doc->releaseTranslationUnit();
       
   267 
       
   268         if (_modelManager)
       
   269             _modelManager->emitDocumentUpdated(doc); // ### TODO: compress
       
   270     }
       
   271 };
       
   272 } // end of anonymous namespace
       
   273 #endif
       
   274 
       
   275 void CppPreprocessor::run(const QString &fileName)
       
   276 {
       
   277     QString absoluteFilePath = fileName;
       
   278     sourceNeeded(absoluteFilePath, IncludeGlobal, /*line = */ 0);
       
   279 }
       
   280 
       
   281 void CppPreprocessor::resetEnvironment()
       
   282 {
       
   283     env.reset();
       
   284     m_processed.clear();
       
   285 }
       
   286 
       
   287 bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision)
       
   288 {
       
   289     if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath))
       
   290         return true;
       
   291 
       
   292     if (m_workingCopy.contains(absoluteFilePath)) {
       
   293         m_included.insert(absoluteFilePath);
       
   294         const QPair<QString, unsigned> r = m_workingCopy.get(absoluteFilePath);
       
   295         *result = r.first;
       
   296         *revision = r.second;
       
   297         return true;
       
   298     }
       
   299 
       
   300     QFileInfo fileInfo(absoluteFilePath);
       
   301     if (! fileInfo.isFile())
       
   302         return false;
       
   303 
       
   304     QFile file(absoluteFilePath);
       
   305     if (file.open(QFile::ReadOnly)) {
       
   306         m_included.insert(absoluteFilePath);
       
   307         QTextStream stream(&file);
       
   308         const QString contents = stream.readAll();
       
   309         *result = contents.toUtf8();
       
   310         file.close();
       
   311         return true;
       
   312     }
       
   313 
       
   314     return false;
       
   315 }
       
   316 
       
   317 QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
       
   318 {
       
   319     QFileInfo fileInfo(fileName);
       
   320     if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) {
       
   321         QString contents;
       
   322         includeFile(fileName, &contents, revision);
       
   323         return contents;
       
   324     }
       
   325 
       
   326     if (type == IncludeLocal && m_currentDoc) {
       
   327         QFileInfo currentFileInfo(m_currentDoc->fileName());
       
   328         QString path = currentFileInfo.absolutePath();
       
   329         path += QLatin1Char('/');
       
   330         path += fileName;
       
   331         path = QDir::cleanPath(path);
       
   332         QString contents;
       
   333         if (includeFile(path, &contents, revision)) {
       
   334             fileName = path;
       
   335             return contents;
       
   336         }
       
   337     }
       
   338 
       
   339     foreach (const QString &includePath, m_includePaths) {
       
   340         QString path = includePath;
       
   341         path += QLatin1Char('/');
       
   342         path += fileName;
       
   343         path = QDir::cleanPath(path);
       
   344         QString contents;
       
   345         if (includeFile(path, &contents, revision)) {
       
   346             fileName = path;
       
   347             return contents;
       
   348         }
       
   349     }
       
   350 
       
   351     // look in the system include paths
       
   352     foreach (const QString &includePath, m_systemIncludePaths) {
       
   353         QString path = includePath;
       
   354         path += QLatin1Char('/');
       
   355         path += fileName;
       
   356         path = QDir::cleanPath(path);
       
   357         QString contents;
       
   358         if (includeFile(path, &contents, revision)) {
       
   359             fileName = path;
       
   360             return contents;
       
   361         }
       
   362     }
       
   363 
       
   364     int index = fileName.indexOf(QLatin1Char('/'));
       
   365     if (index != -1) {
       
   366         QString frameworkName = fileName.left(index);
       
   367         QString name = fileName.mid(index + 1);
       
   368 
       
   369         foreach (const QString &frameworkPath, m_frameworkPaths) {
       
   370             QString path = frameworkPath;
       
   371             path += QLatin1Char('/');
       
   372             path += frameworkName;
       
   373             path += QLatin1String(".framework/Headers/");
       
   374             path += name;
       
   375             path = QDir::cleanPath(path);
       
   376             QString contents;
       
   377             if (includeFile(path, &contents, revision)) {
       
   378                 fileName = path;
       
   379                 return contents;
       
   380             }
       
   381         }
       
   382     }
       
   383 
       
   384     QString path = fileName;
       
   385     if (path.at(0) != QLatin1Char('/'))
       
   386         path.prepend(QLatin1Char('/'));
       
   387 
       
   388     foreach (const QString &projectFile, m_projectFiles) {
       
   389         if (projectFile.endsWith(path)) {
       
   390             fileName = projectFile;
       
   391             QString contents;
       
   392             includeFile(fileName, &contents, revision);
       
   393             return contents;
       
   394         }
       
   395     }
       
   396 
       
   397     //qDebug() << "**** file" << fileName << "not found!";
       
   398     return QString();
       
   399 }
       
   400 
       
   401 void CppPreprocessor::macroAdded(const Macro &macro)
       
   402 {
       
   403     if (! m_currentDoc)
       
   404         return;
       
   405 
       
   406     m_currentDoc->appendMacro(macro);
       
   407 }
       
   408 
       
   409 void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro &macro)
       
   410 {
       
   411     if (! m_currentDoc)
       
   412         return;
       
   413 
       
   414     m_currentDoc->addMacroUse(macro, offset, macro.name().length(), env.currentLine,
       
   415                               QVector<MacroArgumentReference>(), true);
       
   416 }
       
   417 
       
   418 void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name)
       
   419 {
       
   420     if (! m_currentDoc)
       
   421         return;
       
   422 
       
   423     m_currentDoc->addUndefinedMacroUse(name, offset);
       
   424 }
       
   425 
       
   426 void CppPreprocessor::startExpandingMacro(unsigned offset,
       
   427                                           const Macro &macro,
       
   428                                           const QByteArray &originalText,
       
   429                                           bool inCondition,
       
   430                                           const QVector<MacroArgumentReference> &actuals)
       
   431 {
       
   432     if (! m_currentDoc)
       
   433         return;
       
   434 
       
   435     //qDebug() << "start expanding:" << macro.name() << "text:" << originalText;
       
   436     m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine,
       
   437                               actuals, inCondition);
       
   438 }
       
   439 
       
   440 void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
       
   441 {
       
   442     if (! m_currentDoc)
       
   443         return;
       
   444 
       
   445     //qDebug() << "stop expanding:" << macro.name;
       
   446 }
       
   447 
       
   448 void CppPreprocessor::mergeEnvironment(Document::Ptr doc)
       
   449 {
       
   450     if (! doc)
       
   451         return;
       
   452 
       
   453     const QString fn = doc->fileName();
       
   454 
       
   455     if (m_processed.contains(fn))
       
   456         return;
       
   457 
       
   458     m_processed.insert(fn);
       
   459 
       
   460     foreach (const Document::Include &incl, doc->includes()) {
       
   461         QString includedFile = incl.fileName();
       
   462 
       
   463         if (Document::Ptr includedDoc = snapshot.document(includedFile))
       
   464             mergeEnvironment(includedDoc);
       
   465         else
       
   466             run(includedFile);
       
   467     }
       
   468 
       
   469     env.addMacros(doc->definedMacros());
       
   470 }
       
   471 
       
   472 void CppPreprocessor::startSkippingBlocks(unsigned offset)
       
   473 {
       
   474     //qDebug() << "start skipping blocks:" << offset;
       
   475     if (m_currentDoc)
       
   476         m_currentDoc->startSkippingBlocks(offset);
       
   477 }
       
   478 
       
   479 void CppPreprocessor::stopSkippingBlocks(unsigned offset)
       
   480 {
       
   481     //qDebug() << "stop skipping blocks:" << offset;
       
   482     if (m_currentDoc)
       
   483         m_currentDoc->stopSkippingBlocks(offset);
       
   484 }
       
   485 
       
   486 void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned line)
       
   487 {
       
   488     if (fileName.isEmpty())
       
   489         return;
       
   490 
       
   491     unsigned editorRevision = 0;
       
   492     QString contents = tryIncludeFile(fileName, type, &editorRevision);
       
   493     fileName = QDir::cleanPath(fileName);
       
   494     if (m_currentDoc) {
       
   495         m_currentDoc->addIncludeFile(fileName, line);
       
   496 
       
   497         if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) {
       
   498             QString msg = QCoreApplication::translate(
       
   499                     "CppPreprocessor", "%1: No such file or directory").arg(fileName);
       
   500 
       
   501             Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning,
       
   502                                           m_currentDoc->fileName(),
       
   503                                           env.currentLine, /*column = */ 0,
       
   504                                           msg);
       
   505 
       
   506             m_currentDoc->addDiagnosticMessage(d);
       
   507 
       
   508             //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line;
       
   509         }
       
   510     }
       
   511 
       
   512     //qDebug() << "parse file:" << fileName << "contents:" << contents.size();
       
   513 
       
   514     Document::Ptr doc = snapshot.document(fileName);
       
   515     if (doc) {
       
   516         mergeEnvironment(doc);
       
   517         return;
       
   518     }
       
   519 
       
   520     doc = Document::create(fileName);
       
   521     doc->setRevision(m_revision);
       
   522     doc->setEditorRevision(editorRevision);
       
   523 
       
   524     QFileInfo info(fileName);
       
   525     if (info.exists())
       
   526         doc->setLastModified(info.lastModified());
       
   527 
       
   528     Document::Ptr previousDoc = switchDocument(doc);
       
   529 
       
   530     const QByteArray preprocessedCode = preprocess(fileName, contents);
       
   531 
       
   532     doc->setSource(preprocessedCode);
       
   533     doc->tokenize();
       
   534     doc->releaseSource();
       
   535 
       
   536     snapshot.insert(doc);
       
   537     m_todo.remove(fileName);
       
   538 
       
   539 #ifndef ICHECK_BUILD
       
   540     Process process(m_modelManager, snapshot, m_workingCopy);
       
   541 
       
   542     process(doc);
       
   543 
       
   544     (void) switchDocument(previousDoc);
       
   545 #else
       
   546     (void) switchDocument(previousDoc);
       
   547     Document::CheckMode mode = Document::FastCheck;
       
   548     mode = Document::FullCheck;
       
   549     doc->parse();
       
   550     doc->check(mode);
       
   551 #endif
       
   552 }
       
   553 
       
   554 Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc)
       
   555 {
       
   556     Document::Ptr previousDoc = m_currentDoc;
       
   557     m_currentDoc = doc;
       
   558     return previousDoc;
       
   559 }
       
   560 
       
   561 #ifndef ICHECK_BUILD
       
   562 void CppTools::CppModelManagerInterface::updateModifiedSourceFiles()
       
   563 {
       
   564     const Snapshot snapshot = this->snapshot();
       
   565     QStringList sourceFiles;
       
   566 
       
   567     foreach (const Document::Ptr doc, snapshot) {
       
   568         const QDateTime lastModified = doc->lastModified();
       
   569 
       
   570         if (! lastModified.isNull()) {
       
   571             QFileInfo fileInfo(doc->fileName());
       
   572 
       
   573             if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
       
   574                 sourceFiles.append(doc->fileName());
       
   575         }
       
   576     }
       
   577 
       
   578     updateSourceFiles(sourceFiles);
       
   579 }
       
   580 
       
   581 CppTools::CppModelManagerInterface *CppTools::CppModelManagerInterface::instance()
       
   582 {
       
   583     ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance();
       
   584     return pluginManager->getObject<CppTools::CppModelManagerInterface>();
       
   585 
       
   586 }
       
   587 
       
   588 
       
   589 /*!
       
   590     \class CppTools::CppModelManager
       
   591     \brief The CppModelManager keeps track of one CppCodeModel instance
       
   592            for each project and all related CppCodeModelPart instances.
       
   593 
       
   594     It also takes care of updating the code models when C++ files are
       
   595     modified within Qt Creator.
       
   596 */
       
   597 
       
   598 CppModelManager::CppModelManager(QObject *parent)
       
   599     : CppModelManagerInterface(parent)
       
   600 {
       
   601     m_findReferences = new CppFindReferences(this);
       
   602     m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull();
       
   603 
       
   604     m_revision = 0;
       
   605     m_synchronizer.setCancelOnWait(true);
       
   606 
       
   607     m_core = Core::ICore::instance(); // FIXME
       
   608     m_dirty = true;
       
   609 
       
   610     ProjectExplorer::ProjectExplorerPlugin *pe =
       
   611        ProjectExplorer::ProjectExplorerPlugin::instance();
       
   612 
       
   613     QTC_ASSERT(pe, return);
       
   614 
       
   615     ProjectExplorer::SessionManager *session = pe->session();
       
   616     QTC_ASSERT(session, return);
       
   617 
       
   618     m_updateEditorSelectionsTimer = new QTimer(this);
       
   619     m_updateEditorSelectionsTimer->setInterval(500);
       
   620     m_updateEditorSelectionsTimer->setSingleShot(true);
       
   621     connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()),
       
   622             this, SLOT(updateEditorSelections()));
       
   623 
       
   624     connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)),
       
   625             this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
       
   626 
       
   627     connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)),
       
   628             this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project *)));
       
   629 
       
   630     connect(session, SIGNAL(aboutToUnloadSession()),
       
   631             this, SLOT(onAboutToUnloadSession()));
       
   632 
       
   633     qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
       
   634 
       
   635     // thread connections
       
   636     connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
       
   637             this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
       
   638 
       
   639     // Listen for editor closed and opened events so that we can keep track of changing files
       
   640     connect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)),
       
   641         this, SLOT(editorOpened(Core::IEditor *)));
       
   642 
       
   643     connect(m_core->editorManager(), SIGNAL(editorAboutToClose(Core::IEditor *)),
       
   644         this, SLOT(editorAboutToClose(Core::IEditor *)));
       
   645 }
       
   646 
       
   647 CppModelManager::~CppModelManager()
       
   648 { }
       
   649 
       
   650 Snapshot CppModelManager::snapshot() const
       
   651 {
       
   652     QMutexLocker locker(&protectSnapshot);
       
   653     return m_snapshot;
       
   654 }
       
   655 
       
   656 void CppModelManager::ensureUpdated()
       
   657 {
       
   658     QMutexLocker locker(&mutex);
       
   659     if (! m_dirty)
       
   660         return;
       
   661 
       
   662     m_projectFiles = internalProjectFiles();
       
   663     m_includePaths = internalIncludePaths();
       
   664     m_frameworkPaths = internalFrameworkPaths();
       
   665     m_definedMacros = internalDefinedMacros();
       
   666     m_dirty = false;
       
   667 }
       
   668 
       
   669 QStringList CppModelManager::internalProjectFiles() const
       
   670 {
       
   671     QStringList files;
       
   672     QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
       
   673     while (it.hasNext()) {
       
   674         it.next();
       
   675         ProjectInfo pinfo = it.value();
       
   676         files += pinfo.sourceFiles;
       
   677     }
       
   678     files.removeDuplicates();
       
   679     return files;
       
   680 }
       
   681 
       
   682 QStringList CppModelManager::internalIncludePaths() const
       
   683 {
       
   684     QStringList includePaths;
       
   685     QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
       
   686     while (it.hasNext()) {
       
   687         it.next();
       
   688         ProjectInfo pinfo = it.value();
       
   689         includePaths += pinfo.includePaths;
       
   690     }
       
   691     includePaths.removeDuplicates();
       
   692     return includePaths;
       
   693 }
       
   694 
       
   695 QStringList CppModelManager::internalFrameworkPaths() const
       
   696 {
       
   697     QStringList frameworkPaths;
       
   698     QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
       
   699     while (it.hasNext()) {
       
   700         it.next();
       
   701         ProjectInfo pinfo = it.value();
       
   702         frameworkPaths += pinfo.frameworkPaths;
       
   703     }
       
   704     frameworkPaths.removeDuplicates();
       
   705     return frameworkPaths;
       
   706 }
       
   707 
       
   708 QByteArray CppModelManager::internalDefinedMacros() const
       
   709 {
       
   710     QByteArray macros;
       
   711     QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
       
   712     while (it.hasNext()) {
       
   713         it.next();
       
   714         ProjectInfo pinfo = it.value();
       
   715         macros += pinfo.defines;
       
   716     }
       
   717     return macros;
       
   718 }
       
   719 
       
   720 void CppModelManager::setIncludesInPaths(const QMap<QString, QStringList> &includesInPaths)
       
   721 {
       
   722     QMutexLocker locker(&mutex);
       
   723     QMapIterator<QString, QStringList> i(includesInPaths);
       
   724     while (i.hasNext()) {
       
   725         i.next();
       
   726         m_includesInPaths.insert(i.key(), i.value());
       
   727     }
       
   728 }
       
   729 
       
   730 void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport)
       
   731 {
       
   732     m_addtionalEditorSupport.insert(editorSupport);
       
   733 }
       
   734 
       
   735 void CppModelManager::removeEditorSupport(AbstractEditorSupport *editorSupport)
       
   736 {
       
   737     m_addtionalEditorSupport.remove(editorSupport);
       
   738 }
       
   739 
       
   740 QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol,
       
   741                                        CPlusPlus::Document::Ptr doc,
       
   742                                        const CPlusPlus::Snapshot &snapshot)
       
   743 {
       
   744     NamespaceBindingPtr glo = bind(doc, snapshot);
       
   745     return m_findReferences->references(LookupContext::canonicalSymbol(symbol, glo.data()), doc, snapshot);
       
   746 }
       
   747 
       
   748 void CppModelManager::findUsages(CPlusPlus::Symbol *symbol)
       
   749 {
       
   750     if (symbol->identifier())
       
   751         m_findReferences->findUsages(symbol);
       
   752 }
       
   753 
       
   754 void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol)
       
   755 {
       
   756     if (symbol->identifier())
       
   757         m_findReferences->renameUsages(symbol);
       
   758 }
       
   759 
       
   760 void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
       
   761 {
       
   762     m_findReferences->findMacroUses(macro);
       
   763 }
       
   764 
       
   765 CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
       
   766 {
       
   767     WorkingCopy workingCopy;
       
   768     QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport);
       
   769     while (it.hasNext()) {
       
   770         it.next();
       
   771         TextEditor::ITextEditor *textEditor = it.key();
       
   772         CppEditorSupport *editorSupport = it.value();
       
   773         QString fileName = textEditor->file()->fileName();
       
   774         workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision());
       
   775     }
       
   776 
       
   777     QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport);
       
   778     while (jt.hasNext()) {
       
   779         AbstractEditorSupport *es =  jt.next();
       
   780         workingCopy.insert(es->fileName(), es->contents());
       
   781     }
       
   782 
       
   783     // add the project configuration file
       
   784     QByteArray conf(pp_configuration);
       
   785     conf += definedMacros();
       
   786     workingCopy.insert(pp_configuration_file, conf);
       
   787 
       
   788     return workingCopy;
       
   789 }
       
   790 
       
   791 CppModelManager::WorkingCopy CppModelManager::workingCopy() const
       
   792 {
       
   793     return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
       
   794 }
       
   795 
       
   796 void CppModelManager::updateSourceFiles(const QStringList &sourceFiles)
       
   797 { (void) refreshSourceFiles(sourceFiles); }
       
   798 
       
   799 QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const
       
   800 {
       
   801     QMutexLocker locker(&mutex);
       
   802 
       
   803     return m_projects.values();
       
   804 }
       
   805 
       
   806 CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
       
   807 {
       
   808     QMutexLocker locker(&mutex);
       
   809 
       
   810     return m_projects.value(project, ProjectInfo(project));
       
   811 }
       
   812 
       
   813 void CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
       
   814 {
       
   815     QMutexLocker locker(&mutex);
       
   816 
       
   817     if (! pinfo.isValid())
       
   818         return;
       
   819 
       
   820     m_projects.insert(pinfo.project, pinfo);
       
   821     m_dirty = true;
       
   822 
       
   823     if (m_indexerEnabled) {
       
   824         QFuture<void> result = QtConcurrent::run(&CppModelManager::updateIncludesInPaths,
       
   825                                                  this,
       
   826                                                  pinfo.includePaths,
       
   827                                                  pinfo.frameworkPaths,
       
   828                                                  m_headerSuffixes);
       
   829 
       
   830         if (pinfo.includePaths.size() > 1) {
       
   831             m_core->progressManager()->addTask(result, tr("Scanning"),
       
   832                                                CppTools::Constants::TASK_INDEX);
       
   833         }
       
   834     }
       
   835 }
       
   836 
       
   837 QStringList CppModelManager::includesInPath(const QString &path) const
       
   838 {
       
   839     QMutexLocker locker(&mutex);
       
   840     return m_includesInPaths.value(path);
       
   841 }
       
   842 
       
   843 QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles)
       
   844 {
       
   845     if (! sourceFiles.isEmpty() && m_indexerEnabled) {
       
   846         const WorkingCopy workingCopy = buildWorkingCopyList();
       
   847 
       
   848         CppPreprocessor *preproc = new CppPreprocessor(this);
       
   849         preproc->setRevision(++m_revision);
       
   850         preproc->setProjectFiles(projectFiles());
       
   851         preproc->setIncludePaths(includePaths());
       
   852         preproc->setFrameworkPaths(frameworkPaths());
       
   853         preproc->setWorkingCopy(workingCopy);
       
   854 
       
   855         QFuture<void> result = QtConcurrent::run(&CppModelManager::parse,
       
   856                                                  preproc, sourceFiles);
       
   857 
       
   858         if (m_synchronizer.futures().size() > 10) {
       
   859             QList<QFuture<void> > futures = m_synchronizer.futures();
       
   860 
       
   861             m_synchronizer.clearFutures();
       
   862 
       
   863             foreach (const QFuture<void> &future, futures) {
       
   864                 if (! (future.isFinished() || future.isCanceled()))
       
   865                     m_synchronizer.addFuture(future);
       
   866             }
       
   867         }
       
   868 
       
   869         m_synchronizer.addFuture(result);
       
   870 
       
   871         if (sourceFiles.count() > 1) {
       
   872             m_core->progressManager()->addTask(result, tr("Indexing"),
       
   873                             CppTools::Constants::TASK_INDEX);
       
   874         }
       
   875 
       
   876         return result;
       
   877     }
       
   878     return QFuture<void>();
       
   879 }
       
   880 
       
   881 /*!
       
   882     \fn    void CppModelManager::editorOpened(Core::IEditor *editor)
       
   883     \brief If a C++ editor is opened, the model manager listens to content changes
       
   884            in order to update the CppCodeModel accordingly. It also updates the
       
   885            CppCodeModel for the first time with this editor.
       
   886 
       
   887     \sa    void CppModelManager::editorContentsChanged()
       
   888  */
       
   889 void CppModelManager::editorOpened(Core::IEditor *editor)
       
   890 {
       
   891     if (isCppEditor(editor)) {
       
   892         TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
       
   893         QTC_ASSERT(textEditor, return);
       
   894 
       
   895         CppEditorSupport *editorSupport = new CppEditorSupport(this);
       
   896         editorSupport->setTextEditor(textEditor);
       
   897         m_editorSupport[textEditor] = editorSupport;
       
   898     }
       
   899 }
       
   900 
       
   901 void CppModelManager::editorAboutToClose(Core::IEditor *editor)
       
   902 {
       
   903     if (isCppEditor(editor)) {
       
   904         TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
       
   905         QTC_ASSERT(textEditor, return);
       
   906 
       
   907         CppEditorSupport *editorSupport = m_editorSupport.value(textEditor);
       
   908         m_editorSupport.remove(textEditor);
       
   909         delete editorSupport;
       
   910     }
       
   911 }
       
   912 
       
   913 bool CppModelManager::isCppEditor(Core::IEditor *editor) const
       
   914 {
       
   915     Core::UniqueIDManager *uidm = m_core->uniqueIDManager();
       
   916     const int uid = uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
       
   917     return editor->context().contains(uid);
       
   918 }
       
   919 
       
   920 void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
       
   921 { emit documentUpdated(doc); }
       
   922 
       
   923 void CppModelManager::onDocumentUpdated(Document::Ptr doc)
       
   924 {
       
   925     const QString fileName = doc->fileName();
       
   926 
       
   927     bool outdated = false;
       
   928 
       
   929     protectSnapshot.lock();
       
   930 
       
   931     Document::Ptr previous = m_snapshot.document(fileName);
       
   932 
       
   933     if (previous && (doc->revision() != 0 && doc->revision() < previous->revision()))
       
   934         outdated = true;
       
   935     else
       
   936         m_snapshot.insert(doc);
       
   937 
       
   938     protectSnapshot.unlock();
       
   939 
       
   940     if (outdated)
       
   941         return;
       
   942 
       
   943     QList<Core::IEditor *> openedEditors = m_core->editorManager()->openedEditors();
       
   944     foreach (Core::IEditor *editor, openedEditors) {
       
   945         if (editor->file()->fileName() == fileName) {
       
   946             TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
       
   947             if (! textEditor)
       
   948                 continue;
       
   949 
       
   950             TextEditor::BaseTextEditor *ed = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget());
       
   951             if (! ed)
       
   952                 continue;
       
   953 
       
   954             QList<TextEditor::BaseTextEditor::BlockRange> blockRanges;
       
   955 
       
   956             foreach (const Document::Block &block, doc->skippedBlocks()) {
       
   957                 blockRanges.append(TextEditor::BaseTextEditor::BlockRange(block.begin(), block.end()));
       
   958             }
       
   959 
       
   960             QList<QTextEdit::ExtraSelection> selections;
       
   961 
       
   962 #ifdef QTCREATOR_WITH_MACRO_HIGHLIGHTING
       
   963             // set up the format for the macros
       
   964             QTextCharFormat macroFormat;
       
   965             macroFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
       
   966 
       
   967             QTextCursor c = ed->textCursor();
       
   968             foreach (const Document::MacroUse &block, doc->macroUses()) {
       
   969                 QTextEdit::ExtraSelection sel;
       
   970                 sel.cursor = c;
       
   971                 sel.cursor.setPosition(block.begin());
       
   972                 sel.cursor.setPosition(block.end(), QTextCursor::KeepAnchor);
       
   973                 sel.format = macroFormat;
       
   974                 selections.append(sel);
       
   975             }
       
   976 #endif // QTCREATOR_WITH_MACRO_HIGHLIGHTING
       
   977 
       
   978             // set up the format for the errors
       
   979             QTextCharFormat errorFormat;
       
   980             errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
       
   981             errorFormat.setUnderlineColor(Qt::red);
       
   982 
       
   983             // set up the format for the warnings.
       
   984             QTextCharFormat warningFormat;
       
   985             warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
       
   986             warningFormat.setUnderlineColor(Qt::darkYellow);
       
   987 
       
   988 #ifdef QTCREATOR_WITH_ADVANCED_HIGHLIGHTER
       
   989             QSet<QPair<unsigned, unsigned> > lines;
       
   990             foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) {
       
   991                 if (m.fileName() != fileName)
       
   992                     continue;
       
   993 
       
   994                 const QPair<unsigned, unsigned> coordinates = qMakePair(m.line(), m.column());
       
   995 
       
   996                 if (lines.contains(coordinates))
       
   997                     continue;
       
   998 
       
   999                 lines.insert(coordinates);
       
  1000 
       
  1001                 QTextEdit::ExtraSelection sel;
       
  1002                 if (m.isWarning())
       
  1003                     sel.format = warningFormat;
       
  1004                 else
       
  1005                     sel.format = errorFormat;
       
  1006 
       
  1007                 QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
       
  1008 
       
  1009                 // ### check for generated tokens.
       
  1010 
       
  1011                 int column = m.column();
       
  1012 
       
  1013                 if (column > c.block().length()) {
       
  1014                     column = 0;
       
  1015 
       
  1016                     const QString text = c.block().text();
       
  1017                     for (int i = 0; i < text.size(); ++i) {
       
  1018                         if (! text.at(i).isSpace()) {
       
  1019                             ++column;
       
  1020                             break;
       
  1021                         }
       
  1022                     }
       
  1023                 }
       
  1024 
       
  1025                 if (column != 0)
       
  1026                     --column;
       
  1027 
       
  1028                 c.setPosition(c.position() + column);
       
  1029                 c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
       
  1030                 sel.cursor = c;
       
  1031                 selections.append(sel);
       
  1032             }
       
  1033 #else
       
  1034             QSet<int> lines;
       
  1035             foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) {
       
  1036                 if (m.fileName() != fileName)
       
  1037                     continue;
       
  1038                 else if (lines.contains(m.line()))
       
  1039                     continue;
       
  1040 
       
  1041                 lines.insert(m.line());
       
  1042 
       
  1043                 QTextEdit::ExtraSelection sel;
       
  1044                 if (m.isWarning())
       
  1045                     sel.format = warningFormat;
       
  1046                 else
       
  1047                     sel.format = errorFormat;
       
  1048 
       
  1049                 QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1));
       
  1050                 const QString text = c.block().text();
       
  1051                 for (int i = 0; i < text.size(); ++i) {
       
  1052                     if (! text.at(i).isSpace()) {
       
  1053                         c.setPosition(c.position() + i);
       
  1054                         break;
       
  1055                     }
       
  1056                 }
       
  1057                 c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
       
  1058                 sel.cursor = c;
       
  1059                 selections.append(sel);
       
  1060             }
       
  1061 #endif
       
  1062             QList<Editor> todo;
       
  1063             foreach (const Editor &e, todo) {
       
  1064                 if (e.textEditor != textEditor)
       
  1065                     todo.append(e);
       
  1066             }
       
  1067 
       
  1068             Editor e;
       
  1069             e.revision = ed->document()->revision();
       
  1070             e.textEditor = textEditor;
       
  1071             e.selections = selections;
       
  1072             e.ifdefedOutBlocks = blockRanges;
       
  1073             todo.append(e);
       
  1074             m_todo = todo;
       
  1075             postEditorUpdate();
       
  1076             break;
       
  1077         }
       
  1078     }
       
  1079 }
       
  1080 
       
  1081 void CppModelManager::postEditorUpdate()
       
  1082 {
       
  1083     m_updateEditorSelectionsTimer->start(500);
       
  1084 }
       
  1085 
       
  1086 void CppModelManager::updateEditorSelections()
       
  1087 {
       
  1088     foreach (const Editor &ed, m_todo) {
       
  1089         if (! ed.textEditor)
       
  1090             continue;
       
  1091 
       
  1092         TextEditor::ITextEditor *textEditor = ed.textEditor;
       
  1093         TextEditor::BaseTextEditor *editor = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget());
       
  1094 
       
  1095         if (! editor)
       
  1096             continue;
       
  1097         else if (editor->document()->revision() != ed.revision)
       
  1098             continue; // outdated
       
  1099 
       
  1100         editor->setExtraSelections(TextEditor::BaseTextEditor::CodeWarningsSelection,
       
  1101                                    ed.selections);
       
  1102 
       
  1103         editor->setIfdefedOutBlocks(ed.ifdefedOutBlocks);
       
  1104     }
       
  1105 
       
  1106     m_todo.clear();
       
  1107 
       
  1108 }
       
  1109 
       
  1110 void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
       
  1111 {
       
  1112     QMutexLocker locker(&mutex);
       
  1113     m_dirty = true;
       
  1114 }
       
  1115 
       
  1116 void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
       
  1117 {
       
  1118     do {
       
  1119         QMutexLocker locker(&mutex);
       
  1120         m_dirty = true;
       
  1121         m_projects.remove(project);
       
  1122     } while (0);
       
  1123 
       
  1124     GC();
       
  1125 }
       
  1126 
       
  1127 void CppModelManager::onAboutToUnloadSession()
       
  1128 {
       
  1129     if (m_core->progressManager()) {
       
  1130         m_core->progressManager()->cancelTasks(CppTools::Constants::TASK_INDEX);
       
  1131     }
       
  1132 
       
  1133     do {
       
  1134         QMutexLocker locker(&mutex);
       
  1135         m_projects.clear();
       
  1136         m_dirty = true;
       
  1137     } while (0);
       
  1138 
       
  1139     GC();
       
  1140 }
       
  1141 
       
  1142 void CppModelManager::updateIncludesInPaths(QFutureInterface<void> &future,
       
  1143                                             CppModelManager *manager,
       
  1144                                             QStringList paths,
       
  1145                                             QStringList frameworkPaths,
       
  1146                                             QStringList suffixes)
       
  1147 {
       
  1148     QMap<QString, QStringList> entriesInPaths;
       
  1149     typedef QPair<QString, QString> SymLink;
       
  1150     typedef QList<SymLink> SymLinks;
       
  1151     SymLinks symlinks;
       
  1152     int processed = 0;
       
  1153 
       
  1154     future.setProgressRange(0, paths.size());
       
  1155 
       
  1156     // Add framework header directories to path list
       
  1157     QStringList frameworkFilter;
       
  1158     frameworkFilter << QLatin1String("*.framework");
       
  1159     QStringListIterator fwPathIt(frameworkPaths);
       
  1160     while (fwPathIt.hasNext()) {
       
  1161         const QString &fwPath = fwPathIt.next();
       
  1162         QStringList entriesInFrameworkPath;
       
  1163         const QStringList &frameworks = QDir(fwPath).entryList(frameworkFilter, QDir::Dirs | QDir::NoDotAndDotDot);
       
  1164         QStringListIterator fwIt(frameworks);
       
  1165         while (fwIt.hasNext()) {
       
  1166             QString framework = fwIt.next();
       
  1167             paths.append(fwPath + QLatin1Char('/') + framework + QLatin1String("/Headers"));
       
  1168             framework.chop(10); // remove the ".framework"
       
  1169             entriesInFrameworkPath.append(framework + QLatin1Char('/'));
       
  1170         }
       
  1171         entriesInPaths.insert(fwPath, entriesInFrameworkPath);
       
  1172     }
       
  1173 
       
  1174     while (!paths.isEmpty()) {
       
  1175         if (future.isPaused())
       
  1176             future.waitForResume();
       
  1177 
       
  1178         if (future.isCanceled())
       
  1179             return;
       
  1180 
       
  1181         const QString path = paths.takeFirst();
       
  1182 
       
  1183         // Skip already scanned paths
       
  1184         if (entriesInPaths.contains(path))
       
  1185             continue;
       
  1186 
       
  1187         QStringList entries;
       
  1188 
       
  1189         QDirIterator i(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
       
  1190         while (i.hasNext()) {
       
  1191             const QString fileName = i.next();
       
  1192             const QFileInfo fileInfo = i.fileInfo();
       
  1193             const QString suffix = fileInfo.suffix();
       
  1194             if (suffix.isEmpty() || suffixes.contains(suffix)) {
       
  1195                 QString text = fileInfo.fileName();
       
  1196                 if (fileInfo.isDir()) {
       
  1197                     text += QLatin1Char('/');
       
  1198 
       
  1199                     // Also scan subdirectory, but avoid endless recursion with symbolic links
       
  1200                     if (fileInfo.isSymLink()) {
       
  1201                         QString target = fileInfo.symLinkTarget();
       
  1202 
       
  1203                         // Don't add broken symlinks
       
  1204                         if (!QFileInfo(target).exists())
       
  1205                             continue;
       
  1206 
       
  1207                         QMap<QString, QStringList>::const_iterator result = entriesInPaths.find(target);
       
  1208                         if (result != entriesInPaths.constEnd()) {
       
  1209                             entriesInPaths.insert(fileName, result.value());
       
  1210                         } else {
       
  1211                             paths.append(target);
       
  1212                             symlinks.append(SymLink(fileName, target));
       
  1213                         }
       
  1214                     } else {
       
  1215                         paths.append(fileName);
       
  1216                     }
       
  1217                 }
       
  1218                 entries.append(text);
       
  1219             }
       
  1220         }
       
  1221 
       
  1222         entriesInPaths.insert(path, entries);
       
  1223 
       
  1224         ++processed;
       
  1225         future.setProgressRange(0, processed + paths.size());
       
  1226         future.setProgressValue(processed);
       
  1227     }
       
  1228     // link symlinks
       
  1229     QListIterator<SymLink> it(symlinks);
       
  1230     it.toBack();
       
  1231     while (it.hasPrevious()) {
       
  1232         SymLink v = it.previous();
       
  1233         QMap<QString, QStringList>::const_iterator result = entriesInPaths.find(v.second);
       
  1234         entriesInPaths.insert(v.first, result.value());
       
  1235     }
       
  1236 
       
  1237     manager->setIncludesInPaths(entriesInPaths);
       
  1238 
       
  1239     future.reportFinished();
       
  1240 }
       
  1241 
       
  1242 void CppModelManager::parse(QFutureInterface<void> &future,
       
  1243                             CppPreprocessor *preproc,
       
  1244                             QStringList files)
       
  1245 {
       
  1246     if (files.isEmpty())
       
  1247         return;
       
  1248 
       
  1249     Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase();
       
  1250     QStringList headers, sources;
       
  1251     Core::MimeType cSourceTy = db->findByType(QLatin1String("text/x-csrc"));
       
  1252     Core::MimeType cppSourceTy = db->findByType(QLatin1String("text/x-c++src"));
       
  1253     Core::MimeType mSourceTy = db->findByType(QLatin1String("text/x-objcsrc"));
       
  1254 
       
  1255     Core::MimeType cHeaderTy = db->findByType(QLatin1String("text/x-hdr"));
       
  1256     Core::MimeType cppHeaderTy = db->findByType(QLatin1String("text/x-c++hdr"));
       
  1257 
       
  1258     foreach (const QString &file, files) {
       
  1259         const QFileInfo fileInfo(file);
       
  1260 
       
  1261         if (cSourceTy.matchesFile(fileInfo) || cppSourceTy.matchesFile(fileInfo) || mSourceTy.matchesFile(fileInfo))
       
  1262             sources.append(file);
       
  1263 
       
  1264         else if (cHeaderTy.matchesFile(fileInfo) || cppHeaderTy.matchesFile(fileInfo))
       
  1265             headers.append(file);
       
  1266     }
       
  1267 
       
  1268     foreach (const QString &file, files) {
       
  1269         preproc->snapshot.remove(file);
       
  1270     }
       
  1271 
       
  1272     files = sources;
       
  1273     files += headers;
       
  1274 
       
  1275     preproc->setTodo(files);
       
  1276 
       
  1277     future.setProgressRange(0, files.size());
       
  1278 
       
  1279     QString conf = QLatin1String(pp_configuration_file);
       
  1280 
       
  1281     bool processingHeaders = false;
       
  1282 
       
  1283     for (int i = 0; i < files.size(); ++i) {
       
  1284         if (future.isPaused())
       
  1285             future.waitForResume();
       
  1286 
       
  1287         if (future.isCanceled())
       
  1288             break;
       
  1289 
       
  1290         // Change the priority of the background parser thread to idle.
       
  1291         QThread::currentThread()->setPriority(QThread::IdlePriority);
       
  1292 
       
  1293         QString fileName = files.at(i);
       
  1294 
       
  1295         bool isSourceFile = false;
       
  1296         if (cppSourceTy.matchesFile(fileName) || cSourceTy.matchesFile(fileName))
       
  1297             isSourceFile = true;
       
  1298 
       
  1299         if (isSourceFile)
       
  1300             (void) preproc->run(conf);
       
  1301 
       
  1302         else if (! processingHeaders) {
       
  1303             (void) preproc->run(conf);
       
  1304 
       
  1305             processingHeaders = true;
       
  1306         }
       
  1307 
       
  1308         preproc->run(fileName);
       
  1309 
       
  1310         future.setProgressValue(files.size() - preproc->todo().size());
       
  1311 
       
  1312         if (isSourceFile)
       
  1313             preproc->resetEnvironment();
       
  1314 
       
  1315         // Restore the previous thread priority.
       
  1316         QThread::currentThread()->setPriority(QThread::NormalPriority);
       
  1317     }
       
  1318 
       
  1319     future.setProgressValue(files.size());
       
  1320 
       
  1321     delete preproc;
       
  1322 }
       
  1323 
       
  1324 void CppModelManager::GC()
       
  1325 {
       
  1326     protectSnapshot.lock();
       
  1327     Snapshot currentSnapshot = m_snapshot;
       
  1328     protectSnapshot.unlock();
       
  1329 
       
  1330     QSet<QString> processed;
       
  1331     QStringList todo = projectFiles();
       
  1332 
       
  1333     while (! todo.isEmpty()) {
       
  1334         QString fn = todo.last();
       
  1335         todo.removeLast();
       
  1336 
       
  1337         if (processed.contains(fn))
       
  1338             continue;
       
  1339 
       
  1340         processed.insert(fn);
       
  1341 
       
  1342         if (Document::Ptr doc = currentSnapshot.document(fn)) {
       
  1343             todo += doc->includedFiles();
       
  1344         }
       
  1345     }
       
  1346 
       
  1347     QStringList removedFiles;
       
  1348 
       
  1349     Snapshot newSnapshot;
       
  1350     for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
       
  1351         const QString fileName = it.key();
       
  1352 
       
  1353         if (processed.contains(fileName))
       
  1354             newSnapshot.insert(it.value());
       
  1355         else
       
  1356             removedFiles.append(fileName);
       
  1357     }
       
  1358 
       
  1359     emit aboutToRemoveFiles(removedFiles);
       
  1360 
       
  1361     protectSnapshot.lock();
       
  1362     m_snapshot = newSnapshot;
       
  1363     protectSnapshot.unlock();
       
  1364 }
       
  1365 #endif
       
  1366