1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "cppmodelmanager.h"
27 
28 #include "abstracteditorsupport.h"
29 #include "abstractoverviewmodel.h"
30 #include "baseeditordocumentprocessor.h"
31 #include "builtinindexingsupport.h"
32 #include "cppclassesfilter.h"
33 #include "cppcodemodelinspectordumper.h"
34 #include "cppcurrentdocumentfilter.h"
35 #include "cppfindreferences.h"
36 #include "cppfunctionsfilter.h"
37 #include "cppincludesfilter.h"
38 #include "cppindexingsupport.h"
39 #include "cpplocatordata.h"
40 #include "cpplocatorfilter.h"
41 #include "cppbuiltinmodelmanagersupport.h"
42 #include "cpprefactoringchanges.h"
43 #include "cpprefactoringengine.h"
44 #include "cppsourceprocessor.h"
45 #include "cpptoolsjsextension.h"
46 #include "cpptoolsplugin.h"
47 #include "cpptoolsconstants.h"
48 #include "cpptoolsreuse.h"
49 #include "editordocumenthandle.h"
50 #include "stringtable.h"
51 #include "symbolfinder.h"
52 #include "symbolsfindfilter.h"
53 #include "followsymbolinterface.h"
54 
55 #include <coreplugin/documentmanager.h>
56 #include <coreplugin/editormanager/editormanager.h>
57 #include <coreplugin/icore.h>
58 #include <coreplugin/jsexpander.h>
59 #include <coreplugin/progressmanager/progressmanager.h>
60 #include <coreplugin/vcsmanager.h>
61 #include <cplusplus/ASTPath.h>
62 #include <cplusplus/TypeOfExpression.h>
63 #include <extensionsystem/pluginmanager.h>
64 #include <projectexplorer/kitinformation.h>
65 #include <projectexplorer/kitmanager.h>
66 #include <projectexplorer/project.h>
67 #include <projectexplorer/projectexplorer.h>
68 #include <projectexplorer/projectmacro.h>
69 #include <projectexplorer/session.h>
70 #include <texteditor/textdocument.h>
71 #include <utils/fileutils.h>
72 #include <utils/hostosinfo.h>
73 #include <utils/qtcassert.h>
74 
75 #include <QCoreApplication>
76 #include <QDebug>
77 #include <QDir>
78 #include <QFutureWatcher>
79 #include <QMutexLocker>
80 #include <QTextBlock>
81 #include <QThreadPool>
82 #include <QTimer>
83 
84 #if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)
85 #define WITH_AST_DUMP
86 #include <iostream>
87 #include <sstream>
88 #endif
89 
90 static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";
91 
92 using namespace CppTools;
93 using namespace CppTools::Internal;
94 using namespace CPlusPlus;
95 using namespace ProjectExplorer;
96 
97 #ifdef QTCREATOR_WITH_DUMP_AST
98 
99 #include <cxxabi.h>
100 
101 class DumpAST: protected ASTVisitor
102 {
103 public:
104     int depth;
105 
DumpAST(Control * control)106     explicit DumpAST(Control *control)
107         : ASTVisitor(control), depth(0)
108     { }
109 
operator ()(AST * ast)110     void operator()(AST *ast)
111     { accept(ast); }
112 
113 protected:
preVisit(AST * ast)114     virtual bool preVisit(AST *ast)
115     {
116         std::ostringstream s;
117         PrettyPrinter pp(control(), s);
118         pp(ast);
119         QString code = QString::fromStdString(s.str());
120         code.replace('\n', ' ');
121         code.replace(QRegularExpression("\\s+"), " ");
122 
123         const char *name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11;
124 
125         QByteArray ind(depth, ' ');
126         ind += name;
127 
128         printf("%-40s %s\n", ind.constData(), qPrintable(code));
129         ++depth;
130         return true;
131     }
132 
postVisit(AST *)133     virtual void postVisit(AST *)
134     { --depth; }
135 };
136 
137 #endif // QTCREATOR_WITH_DUMP_AST
138 
139 namespace CppTools {
140 
141 using REType = RefactoringEngineType;
142 
143 namespace Internal {
144 
145 static CppModelManager *m_instance;
146 
147 class CppModelManagerPrivate
148 {
149 public:
150     // Snapshot
151     mutable QMutex m_snapshotMutex;
152     Snapshot m_snapshot;
153 
154     // Project integration
155     mutable QMutex m_projectMutex;
156     QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
157     QHash<ProjectExplorer::Project *, bool> m_projectToIndexerCanceled;
158     QMap<Utils::FilePath, QList<ProjectPart::Ptr> > m_fileToProjectParts;
159     QMap<QString, ProjectPart::Ptr> m_projectPartIdToProjectProjectPart;
160     // The members below are cached/(re)calculated from the projects and/or their project parts
161     bool m_dirty;
162     QStringList m_projectFiles;
163     ProjectExplorer::HeaderPaths m_headerPaths;
164     ProjectExplorer::Macros m_definedMacros;
165 
166     // Editor integration
167     mutable QMutex m_cppEditorDocumentsMutex;
168     QMap<QString, CppEditorDocumentHandle *> m_cppEditorDocuments;
169     QSet<AbstractEditorSupport *> m_extraEditorSupports;
170 
171     // Model Manager Supports for e.g. completion and highlighting
172     ModelManagerSupport::Ptr m_builtinModelManagerSupport;
173     ModelManagerSupport::Ptr m_activeModelManagerSupport;
174 
175     // Indexing
176     CppIndexingSupport *m_indexingSupporter;
177     CppIndexingSupport *m_internalIndexingSupport;
178     bool m_indexerEnabled;
179 
180     QMutex m_fallbackProjectPartMutex;
181     ProjectPart::Ptr m_fallbackProjectPart;
182 
183     CppFindReferences *m_findReferences;
184 
185     SymbolFinder m_symbolFinder;
186     QThreadPool m_threadPool;
187 
188     bool m_enableGC;
189     QTimer m_delayedGcTimer;
190 
191     // Refactoring
192     using REHash = QMap<REType, RefactoringEngineInterface *>;
193     REHash m_refactoringEngines;
194 
195     CppLocatorData m_locatorData;
196     std::unique_ptr<Core::ILocatorFilter> m_locatorFilter;
197     std::unique_ptr<Core::ILocatorFilter> m_classesFilter;
198     std::unique_ptr<Core::ILocatorFilter> m_includesFilter;
199     std::unique_ptr<Core::ILocatorFilter> m_functionsFilter;
200     std::unique_ptr<Core::IFindFilter> m_symbolsFindFilter;
201     std::unique_ptr<Core::ILocatorFilter> m_currentDocumentFilter;
202 };
203 
204 } // namespace Internal
205 
206 const char pp_configuration[] =
207     "# 1 \"<configuration>\"\n"
208     "#define Q_CREATOR_RUN 1\n"
209     "#define __cplusplus 1\n"
210     "#define __extension__\n"
211     "#define __context__\n"
212     "#define __range__\n"
213     "#define   restrict\n"
214     "#define __restrict\n"
215     "#define __restrict__\n"
216 
217     "#define __complex__\n"
218     "#define __imag__\n"
219     "#define __real__\n"
220 
221     "#define __builtin_va_arg(a,b) ((b)0)\n"
222 
223     "#define _Pragma(x)\n" // C99 _Pragma operator
224 
225     "#define __func__ \"\"\n"
226 
227     // ### add macros for gcc
228     "#define __PRETTY_FUNCTION__ \"\"\n"
229     "#define __FUNCTION__ \"\"\n"
230 
231     // ### add macros for win32
232     "#define __cdecl\n"
233     "#define __stdcall\n"
234     "#define __thiscall\n"
235     "#define QT_WA(x) x\n"
236     "#define CALLBACK\n"
237     "#define STDMETHODCALLTYPE\n"
238     "#define __RPC_FAR\n"
239     "#define __declspec(a)\n"
240     "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
241     "#define __try try\n"
242     "#define __except catch\n"
243     "#define __finally\n"
244     "#define __inline inline\n"
245     "#define __forceinline inline\n"
246     "#define __pragma(x)\n"
247     "#define __w64\n"
248     "#define __int64 long long\n"
249     "#define __int32 long\n"
250     "#define __int16 short\n"
251     "#define __int8 char\n"
252     "#define __ptr32\n"
253     "#define __ptr64\n";
254 
timeStampModifiedFiles(const QList<Document::Ptr> & documentsToCheck)255 QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
256 {
257     QSet<QString> sourceFiles;
258 
259     foreach (const Document::Ptr doc, documentsToCheck) {
260         const QDateTime lastModified = doc->lastModified();
261 
262         if (!lastModified.isNull()) {
263             QFileInfo fileInfo(doc->fileName());
264 
265             if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
266                 sourceFiles.insert(doc->fileName());
267         }
268     }
269 
270     return sourceFiles;
271 }
272 
273 /*!
274  * \brief createSourceProcessor Create a new source processor, which will signal the
275  * model manager when a document has been processed.
276  *
277  * Indexed file is truncated version of fully parsed document: copy of source
278  * code and full AST will be dropped when indexing is done.
279  *
280  * \return a new source processor object, which the caller needs to delete when finished.
281  */
createSourceProcessor()282 CppSourceProcessor *CppModelManager::createSourceProcessor()
283 {
284     CppModelManager *that = instance();
285     return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
286         const Document::Ptr previousDocument = that->document(doc->fileName());
287         const unsigned newRevision = previousDocument.isNull()
288                 ? 1U
289                 : previousDocument->revision() + 1;
290         doc->setRevision(newRevision);
291         that->emitDocumentUpdated(doc);
292         doc->releaseSourceAndAST();
293     });
294 }
295 
editorConfigurationFileName()296 QString CppModelManager::editorConfigurationFileName()
297 {
298     return QLatin1String("<per-editor-defines>");
299 }
300 
getRefactoringEngine(CppModelManagerPrivate::REHash & engines)301 static RefactoringEngineInterface *getRefactoringEngine(CppModelManagerPrivate::REHash &engines)
302 {
303     QTC_ASSERT(!engines.empty(), return nullptr;);
304     RefactoringEngineInterface *currentEngine = engines[REType::BuiltIn];
305     if (engines.find(REType::ClangCodeModel) != engines.end()) {
306         currentEngine = engines[REType::ClangCodeModel];
307     } else if (engines.find(REType::ClangRefactoring) != engines.end()) {
308         RefactoringEngineInterface *engine = engines[REType::ClangRefactoring];
309         if (engine->isRefactoringEngineAvailable())
310             currentEngine = engine;
311     }
312     return currentEngine;
313 }
314 
startLocalRenaming(const CursorInEditor & data,CppTools::ProjectPart * projectPart,RenameCallback && renameSymbolsCallback)315 void CppModelManager::startLocalRenaming(const CursorInEditor &data,
316                                          CppTools::ProjectPart *projectPart,
317                                          RenameCallback &&renameSymbolsCallback)
318 {
319     RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines);
320     QTC_ASSERT(engine, return;);
321     engine->startLocalRenaming(data, projectPart, std::move(renameSymbolsCallback));
322 }
323 
globalRename(const CursorInEditor & data,UsagesCallback && renameCallback,const QString & replacement)324 void CppModelManager::globalRename(const CursorInEditor &data, UsagesCallback &&renameCallback,
325                                    const QString &replacement)
326 {
327     RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines);
328     QTC_ASSERT(engine, return;);
329     engine->globalRename(data, std::move(renameCallback), replacement);
330 }
331 
findUsages(const CppTools::CursorInEditor & data,UsagesCallback && showUsagesCallback) const332 void CppModelManager::findUsages(const CppTools::CursorInEditor &data,
333                                  UsagesCallback &&showUsagesCallback) const
334 {
335     RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines);
336     QTC_ASSERT(engine, return;);
337     engine->findUsages(data, std::move(showUsagesCallback));
338 }
339 
globalFollowSymbol(const CursorInEditor & data,Utils::ProcessLinkCallback && processLinkCallback,const CPlusPlus::Snapshot & snapshot,const CPlusPlus::Document::Ptr & documentFromSemanticInfo,SymbolFinder * symbolFinder,bool inNextSplit) const340 void CppModelManager::globalFollowSymbol(
341         const CursorInEditor &data,
342         Utils::ProcessLinkCallback &&processLinkCallback,
343         const CPlusPlus::Snapshot &snapshot,
344         const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
345         SymbolFinder *symbolFinder,
346         bool inNextSplit) const
347 {
348     RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines);
349     QTC_ASSERT(engine, return;);
350     engine->globalFollowSymbol(data, std::move(processLinkCallback), snapshot, documentFromSemanticInfo,
351                                symbolFinder, inNextSplit);
352 }
353 
positionRequiresSignal(const QString & filePath,const QByteArray & content,int position) const354 bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByteArray &content,
355                                              int position) const
356 {
357     if (content.isEmpty())
358         return false;
359 
360     // Insert a dummy prefix if we don't have a real one. Otherwise the AST path will not contain
361     // anything after the CallAST.
362     QByteArray fixedContent = content;
363     if (position > 2 && content.mid(position - 2, 2) == "::")
364         fixedContent.insert(position, 'x');
365 
366     const Snapshot snapshot = this->snapshot();
367     const Document::Ptr document = snapshot.preprocessedDocument(fixedContent, filePath);
368     document->check();
369     QTextDocument textDocument(QString::fromUtf8(fixedContent));
370     QTextCursor cursor(&textDocument);
371     cursor.setPosition(position);
372 
373     // Are we at the second argument of a function call?
374     const QList<AST *> path = ASTPath(document)(cursor);
375     if (path.isEmpty() || !path.last()->asSimpleName())
376         return false;
377     const CallAST *callAst = nullptr;
378     for (auto it = path.crbegin(); it != path.crend(); ++it) {
379         if ((callAst = (*it)->asCall()))
380             break;
381     }
382     if (!callAst)
383         return false;
384     if (!callAst->expression_list || !callAst->expression_list->next)
385         return false;
386     const ExpressionAST * const secondArg = callAst->expression_list->next->value;
387     if (secondArg->firstToken() > path.last()->firstToken()
388             || secondArg->lastToken() < path.last()->lastToken()) {
389         return false;
390     }
391 
392     // Is the function called "connect" or "disconnect"?
393     if (!callAst->base_expression)
394         return false;
395     Scope *scope = document->globalNamespace();
396     for (auto it = path.crbegin(); it != path.crend(); ++it) {
397         if (const CompoundStatementAST * const stmtAst = (*it)->asCompoundStatement()) {
398             scope = stmtAst->symbol;
399             break;
400         }
401     }
402     const NameAST *nameAst = nullptr;
403     const LookupContext context(document, snapshot);
404     if (const IdExpressionAST * const idAst = callAst->base_expression->asIdExpression()) {
405         nameAst = idAst->name;
406     } else if (const MemberAccessAST * const ast = callAst->base_expression->asMemberAccess()) {
407         nameAst = ast->member_name;
408         TypeOfExpression exprType;
409         exprType.setExpandTemplates(true);
410         exprType.init(document, snapshot);
411         const QList<LookupItem> typeMatches = exprType(ast->base_expression, document, scope);
412         if (typeMatches.isEmpty())
413             return false;
414         const std::function<const NamedType *(const FullySpecifiedType &)> getNamedType
415                 = [&getNamedType](const FullySpecifiedType &type ) -> const NamedType * {
416             Type * const t = type.type();
417             if (const auto namedType = t->asNamedType())
418                 return namedType;
419             if (const auto pointerType = t->asPointerType())
420                 return getNamedType(pointerType->elementType());
421             if (const auto refType = t->asReferenceType())
422                 return getNamedType(refType->elementType());
423             return nullptr;
424         };
425         const NamedType *namedType = getNamedType(typeMatches.first().type());
426         if (!namedType && typeMatches.first().declaration())
427             namedType = getNamedType(typeMatches.first().declaration()->type());
428         if (!namedType)
429             return false;
430         const ClassOrNamespace * const result = context.lookupType(namedType->name(), scope);
431         if (!result)
432             return false;
433         scope = result->rootClass();
434         if (!scope)
435             return false;
436     }
437     if (!nameAst || !nameAst->name)
438         return false;
439     const Identifier * const id = nameAst->name->identifier();
440     if (!id)
441         return false;
442     const QString funcName = QString::fromUtf8(id->chars(), id->size());
443     if (funcName != "connect" && funcName != "disconnect")
444         return false;
445 
446     // Is the function a member function of QObject?
447     const QList<LookupItem> matches = context.lookup(nameAst->name, scope);
448     for (const LookupItem &match : matches) {
449         if (!match.scope())
450             continue;
451         const Class *klass = match.scope()->asClass();
452         if (!klass || !klass->name())
453             continue;
454         const Identifier * const classId = klass->name()->identifier();
455         if (classId && QString::fromUtf8(classId->chars(), classId->size()) == "QObject")
456             return true;
457     }
458 
459     return false;
460 }
461 
addRefactoringEngine(RefactoringEngineType type,RefactoringEngineInterface * refactoringEngine)462 void CppModelManager::addRefactoringEngine(RefactoringEngineType type,
463                                            RefactoringEngineInterface *refactoringEngine)
464 {
465     instance()->d->m_refactoringEngines[type] = refactoringEngine;
466 }
467 
removeRefactoringEngine(RefactoringEngineType type)468 void CppModelManager::removeRefactoringEngine(RefactoringEngineType type)
469 {
470     instance()->d->m_refactoringEngines.remove(type);
471 }
472 
builtinRefactoringEngine()473 RefactoringEngineInterface *CppModelManager::builtinRefactoringEngine()
474 {
475     return instance()->d->m_refactoringEngines.value(RefactoringEngineType::BuiltIn);
476 }
477 
builtinFollowSymbol()478 FollowSymbolInterface &CppModelManager::builtinFollowSymbol()
479 {
480     return instance()->d->m_builtinModelManagerSupport->followSymbolInterface();
481 }
482 
483 template<class FilterClass>
setFilter(std::unique_ptr<FilterClass> & filter,std::unique_ptr<FilterClass> && newFilter)484 static void setFilter(std::unique_ptr<FilterClass> &filter,
485                       std::unique_ptr<FilterClass> &&newFilter)
486 {
487     QTC_ASSERT(newFilter, return;);
488     filter = std::move(newFilter);
489 }
490 
setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> && filter)491 void CppModelManager::setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
492 {
493     setFilter(d->m_locatorFilter, std::move(filter));
494 }
495 
setClassesFilter(std::unique_ptr<Core::ILocatorFilter> && filter)496 void CppModelManager::setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
497 {
498     setFilter(d->m_classesFilter, std::move(filter));
499 }
500 
setIncludesFilter(std::unique_ptr<Core::ILocatorFilter> && filter)501 void CppModelManager::setIncludesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
502 {
503     setFilter(d->m_includesFilter, std::move(filter));
504 }
505 
setFunctionsFilter(std::unique_ptr<Core::ILocatorFilter> && filter)506 void CppModelManager::setFunctionsFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
507 {
508     setFilter(d->m_functionsFilter, std::move(filter));
509 }
510 
setSymbolsFindFilter(std::unique_ptr<Core::IFindFilter> && filter)511 void CppModelManager::setSymbolsFindFilter(std::unique_ptr<Core::IFindFilter> &&filter)
512 {
513     setFilter(d->m_symbolsFindFilter, std::move(filter));
514 }
515 
setCurrentDocumentFilter(std::unique_ptr<Core::ILocatorFilter> && filter)516 void CppModelManager::setCurrentDocumentFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
517 {
518     setFilter(d->m_currentDocumentFilter, std::move(filter));
519 }
520 
locatorFilter() const521 Core::ILocatorFilter *CppModelManager::locatorFilter() const
522 {
523     return d->m_locatorFilter.get();
524 }
525 
classesFilter() const526 Core::ILocatorFilter *CppModelManager::classesFilter() const
527 {
528     return d->m_classesFilter.get();
529 }
530 
includesFilter() const531 Core::ILocatorFilter *CppModelManager::includesFilter() const
532 {
533     return d->m_includesFilter.get();
534 }
535 
functionsFilter() const536 Core::ILocatorFilter *CppModelManager::functionsFilter() const
537 {
538     return d->m_functionsFilter.get();
539 }
540 
symbolsFindFilter() const541 Core::IFindFilter *CppModelManager::symbolsFindFilter() const
542 {
543     return d->m_symbolsFindFilter.get();
544 }
545 
currentDocumentFilter() const546 Core::ILocatorFilter *CppModelManager::currentDocumentFilter() const
547 {
548     return d->m_currentDocumentFilter.get();
549 }
550 
followSymbolInterface() const551 FollowSymbolInterface &CppModelManager::followSymbolInterface() const
552 {
553     return d->m_activeModelManagerSupport->followSymbolInterface();
554 }
555 
createOverviewModel() const556 std::unique_ptr<AbstractOverviewModel> CppModelManager::createOverviewModel() const
557 {
558     return d->m_activeModelManagerSupport->createOverviewModel();
559 }
560 
configurationFileName()561 QString CppModelManager::configurationFileName()
562 {
563     return Preprocessor::configurationFileName();
564 }
565 
updateModifiedSourceFiles()566 void CppModelManager::updateModifiedSourceFiles()
567 {
568     const Snapshot snapshot = this->snapshot();
569     QList<Document::Ptr> documentsToCheck;
570     foreach (const Document::Ptr document, snapshot)
571         documentsToCheck << document;
572 
573     updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
574 }
575 
576 /*!
577     \class CppTools::CppModelManager
578     \brief The CppModelManager keeps tracks of the source files the code model is aware of.
579 
580     The CppModelManager manages the source files in a Snapshot object.
581 
582     The snapshot is updated in case e.g.
583         * New files are opened/edited (Editor integration)
584         * A project manager pushes updated project information (Project integration)
585         * Files are garbage collected
586 */
587 
instance()588 CppModelManager *CppModelManager::instance()
589 {
590     QTC_ASSERT(m_instance, return nullptr;);
591     return m_instance;
592 }
593 
registerJsExtension()594 void CppModelManager::registerJsExtension()
595 {
596     Core::JsExpander::registerGlobalObject("Cpp", [this] {
597         return new CppToolsJsExtension(&d->m_locatorData);
598     });
599 }
600 
initCppTools()601 void CppModelManager::initCppTools()
602 {
603     // Objects
604     connect(Core::VcsManager::instance(), &Core::VcsManager::repositoryChanged,
605             this, &CppModelManager::updateModifiedSourceFiles);
606     connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedInternally,
607             [this](const Utils::FilePaths &filePaths) {
608         updateSourceFiles(Utils::transform<QSet>(filePaths, &Utils::FilePath::toString));
609     });
610 
611     connect(this, &CppModelManager::documentUpdated,
612             &d->m_locatorData, &CppLocatorData::onDocumentUpdated);
613 
614     connect(this, &CppModelManager::aboutToRemoveFiles,
615             &d->m_locatorData, &CppLocatorData::onAboutToRemoveFiles);
616 
617     // Set up builtin filters
618     setLocatorFilter(std::make_unique<CppLocatorFilter>(&d->m_locatorData));
619     setClassesFilter(std::make_unique<CppClassesFilter>(&d->m_locatorData));
620     setIncludesFilter(std::make_unique<CppIncludesFilter>());
621     setFunctionsFilter(std::make_unique<CppFunctionsFilter>(&d->m_locatorData));
622     setSymbolsFindFilter(std::make_unique<SymbolsFindFilter>(this));
623     setCurrentDocumentFilter(
624                 std::make_unique<Internal::CppCurrentDocumentFilter>(this));
625 }
626 
initializeBuiltinModelManagerSupport()627 void CppModelManager::initializeBuiltinModelManagerSupport()
628 {
629     d->m_builtinModelManagerSupport
630             = BuiltinModelManagerSupportProvider().createModelManagerSupport();
631     d->m_activeModelManagerSupport = d->m_builtinModelManagerSupport;
632     d->m_refactoringEngines[RefactoringEngineType::BuiltIn] =
633             &d->m_activeModelManagerSupport->refactoringEngineInterface();
634 }
635 
CppModelManager()636 CppModelManager::CppModelManager()
637     : CppModelManagerBase(nullptr)
638     , d(new CppModelManagerPrivate)
639 {
640     m_instance = this;
641 
642     // Used for weak dependency in VcsBaseSubmitEditor
643     setObjectName("CppModelManager");
644     ExtensionSystem::PluginManager::addObject(this);
645 
646     d->m_indexingSupporter = nullptr;
647     d->m_enableGC = true;
648 
649     // Visual C++ has 1MiB, macOSX has 512KiB
650     if (Utils::HostOsInfo::isWindowsHost() || Utils::HostOsInfo::isMacHost())
651         d->m_threadPool.setStackSize(2 * 1024 * 1024);
652 
653     qRegisterMetaType<QSet<QString> >();
654     connect(this, &CppModelManager::sourceFilesRefreshed,
655             this, &CppModelManager::onSourceFilesRefreshed);
656 
657     d->m_findReferences = new CppFindReferences(this);
658     d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
659 
660     d->m_dirty = true;
661 
662     d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
663     d->m_delayedGcTimer.setSingleShot(true);
664     connect(&d->m_delayedGcTimer, &QTimer::timeout, this, &CppModelManager::GC);
665 
666     auto sessionManager = ProjectExplorer::SessionManager::instance();
667     connect(sessionManager, &ProjectExplorer::SessionManager::projectAdded,
668             this, &CppModelManager::onProjectAdded);
669     connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject,
670             this, &CppModelManager::onAboutToRemoveProject);
671     connect(sessionManager, &ProjectExplorer::SessionManager::aboutToLoadSession,
672             this, &CppModelManager::onAboutToLoadSession);
673     connect(sessionManager, &ProjectExplorer::SessionManager::startupProjectChanged,
674             this, &CppModelManager::onActiveProjectChanged);
675 
676     connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
677             this, &CppModelManager::onCurrentEditorChanged);
678 
679     connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
680             this, &CppModelManager::renameIncludes);
681 
682     connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
683             this, &CppModelManager::onCoreAboutToClose);
684 
685     connect(KitManager::instance(), &KitManager::kitsChanged, this,
686             &CppModelManager::setupFallbackProjectPart);
687     connect(this, &CppModelManager::projectPartsRemoved, this,
688             &CppModelManager::setupFallbackProjectPart);
689     connect(this, &CppModelManager::projectPartsUpdated, this,
690             &CppModelManager::setupFallbackProjectPart);
691     setupFallbackProjectPart();
692 
693     qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
694     qRegisterMetaType<QList<Document::DiagnosticMessage>>(
695                 "QList<CPlusPlus::Document::DiagnosticMessage>");
696 
697     initializeBuiltinModelManagerSupport();
698 
699     d->m_internalIndexingSupport = new BuiltinIndexingSupport;
700 
701     initCppTools();
702 }
703 
~CppModelManager()704 CppModelManager::~CppModelManager()
705 {
706     ExtensionSystem::PluginManager::removeObject(this);
707 
708     delete d->m_internalIndexingSupport;
709     delete d;
710 }
711 
snapshot() const712 Snapshot CppModelManager::snapshot() const
713 {
714     QMutexLocker locker(&d->m_snapshotMutex);
715     return d->m_snapshot;
716 }
717 
document(const QString & fileName) const718 Document::Ptr CppModelManager::document(const QString &fileName) const
719 {
720     QMutexLocker locker(&d->m_snapshotMutex);
721     return d->m_snapshot.document(fileName);
722 }
723 
724 /// Replace the document in the snapshot.
725 ///
726 /// \returns true if successful, false if the new document is out-dated.
replaceDocument(Document::Ptr newDoc)727 bool CppModelManager::replaceDocument(Document::Ptr newDoc)
728 {
729     QMutexLocker locker(&d->m_snapshotMutex);
730 
731     Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
732     if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
733         // the new document is outdated
734         return false;
735 
736     d->m_snapshot.insert(newDoc);
737     return true;
738 }
739 
740 /// Make sure that m_projectMutex is locked when calling this.
ensureUpdated()741 void CppModelManager::ensureUpdated()
742 {
743     if (!d->m_dirty)
744         return;
745 
746     d->m_projectFiles = internalProjectFiles();
747     d->m_headerPaths = internalHeaderPaths();
748     d->m_definedMacros = internalDefinedMacros();
749     d->m_dirty = false;
750 }
751 
internalProjectFiles() const752 QStringList CppModelManager::internalProjectFiles() const
753 {
754     QStringList files;
755     for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
756         foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
757             foreach (const ProjectFile &file, part->files)
758                 files += file.path;
759         }
760     }
761     files.removeDuplicates();
762     return files;
763 }
764 
internalHeaderPaths() const765 ProjectExplorer::HeaderPaths CppModelManager::internalHeaderPaths() const
766 {
767     ProjectExplorer::HeaderPaths headerPaths;
768     for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
769         foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
770             foreach (const ProjectExplorer::HeaderPath &path, part->headerPaths) {
771                 ProjectExplorer::HeaderPath hp(QDir::cleanPath(path.path), path.type);
772                 if (!headerPaths.contains(hp))
773                     headerPaths.push_back(std::move(hp));
774             }
775         }
776     }
777     return headerPaths;
778 }
779 
addUnique(const ProjectExplorer::Macros & newMacros,ProjectExplorer::Macros & macros,QSet<ProjectExplorer::Macro> & alreadyIn)780 static void addUnique(const ProjectExplorer::Macros &newMacros,
781                       ProjectExplorer::Macros &macros,
782                       QSet<ProjectExplorer::Macro> &alreadyIn)
783 {
784     for (const ProjectExplorer::Macro &macro : newMacros) {
785         if (!alreadyIn.contains(macro)) {
786             macros += macro;
787             alreadyIn.insert(macro);
788         }
789     }
790 }
791 
internalDefinedMacros() const792 ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const
793 {
794     ProjectExplorer::Macros macros;
795     QSet<ProjectExplorer::Macro> alreadyIn;
796     for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
797         for (const ProjectPart::Ptr &part : pinfo.projectParts()) {
798             addUnique(part->toolChainMacros, macros, alreadyIn);
799             addUnique(part->projectMacros, macros, alreadyIn);
800         }
801     }
802     return macros;
803 }
804 
805 /// This function will acquire mutexes!
dumpModelManagerConfiguration(const QString & logFileId)806 void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
807 {
808     const Snapshot globalSnapshot = snapshot();
809     const QString globalSnapshotTitle
810         = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());
811 
812     CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
813     dumper.dumpProjectInfos(projectInfos());
814     dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
815     dumper.dumpWorkingCopy(workingCopy());
816     dumper.dumpMergedEntities(headerPaths(),
817                               ProjectExplorer:: Macro::toByteArray(definedMacros()));
818 }
819 
abstractEditorSupports() const820 QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const
821 {
822     return d->m_extraEditorSupports;
823 }
824 
addExtraEditorSupport(AbstractEditorSupport * editorSupport)825 void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
826 {
827     d->m_extraEditorSupports.insert(editorSupport);
828 }
829 
removeExtraEditorSupport(AbstractEditorSupport * editorSupport)830 void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
831 {
832     d->m_extraEditorSupports.remove(editorSupport);
833 }
834 
cppEditorDocument(const QString & filePath) const835 CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const QString &filePath) const
836 {
837     if (filePath.isEmpty())
838         return nullptr;
839 
840     QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
841     return d->m_cppEditorDocuments.value(filePath, 0);
842 }
843 
registerCppEditorDocument(CppEditorDocumentHandle * editorDocument)844 void CppModelManager::registerCppEditorDocument(CppEditorDocumentHandle *editorDocument)
845 {
846     QTC_ASSERT(editorDocument, return);
847     const QString filePath = editorDocument->filePath();
848     QTC_ASSERT(!filePath.isEmpty(), return);
849 
850     QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
851     QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0) == 0, return);
852     d->m_cppEditorDocuments.insert(filePath, editorDocument);
853 }
854 
unregisterCppEditorDocument(const QString & filePath)855 void CppModelManager::unregisterCppEditorDocument(const QString &filePath)
856 {
857     QTC_ASSERT(!filePath.isEmpty(), return);
858 
859     static short closedCppDocuments = 0;
860     int openCppDocuments = 0;
861 
862     {
863         QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
864         QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0), return);
865         QTC_CHECK(d->m_cppEditorDocuments.remove(filePath) == 1);
866         openCppDocuments = d->m_cppEditorDocuments.size();
867     }
868 
869     ++closedCppDocuments;
870     if (openCppDocuments == 0 || closedCppDocuments == 5) {
871         closedCppDocuments = 0;
872         delayedGC();
873     }
874 }
875 
references(Symbol * symbol,const LookupContext & context)876 QList<int> CppModelManager::references(Symbol *symbol, const LookupContext &context)
877 {
878     return d->m_findReferences->references(symbol, context);
879 }
880 
findUsages(Symbol * symbol,const LookupContext & context)881 void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
882 {
883     if (symbol->identifier())
884         d->m_findReferences->findUsages(symbol, context);
885 }
886 
renameUsages(Symbol * symbol,const LookupContext & context,const QString & replacement)887 void CppModelManager::renameUsages(Symbol *symbol,
888                                    const LookupContext &context,
889                                    const QString &replacement)
890 {
891     if (symbol->identifier())
892         d->m_findReferences->renameUsages(symbol, context, replacement);
893 }
894 
findMacroUsages(const CPlusPlus::Macro & macro)895 void CppModelManager::findMacroUsages(const CPlusPlus::Macro &macro)
896 {
897     d->m_findReferences->findMacroUses(macro);
898 }
899 
renameMacroUsages(const CPlusPlus::Macro & macro,const QString & replacement)900 void CppModelManager::renameMacroUsages(const CPlusPlus::Macro &macro, const QString &replacement)
901 {
902     d->m_findReferences->renameMacroUses(macro, replacement);
903 }
904 
replaceSnapshot(const Snapshot & newSnapshot)905 void CppModelManager::replaceSnapshot(const Snapshot &newSnapshot)
906 {
907     QMutexLocker snapshotLocker(&d->m_snapshotMutex);
908     d->m_snapshot = newSnapshot;
909 }
910 
buildWorkingCopyList()911 WorkingCopy CppModelManager::buildWorkingCopyList()
912 {
913     WorkingCopy workingCopy;
914 
915     foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments()) {
916         workingCopy.insert(cppEditorDocument->filePath(),
917                            cppEditorDocument->contents(),
918                            cppEditorDocument->revision());
919     }
920 
921     for (AbstractEditorSupport *es : qAsConst(d->m_extraEditorSupports))
922         workingCopy.insert(es->fileName(), es->contents(), es->revision());
923 
924     // Add the project configuration file
925     QByteArray conf = codeModelConfiguration();
926     conf += ProjectExplorer::Macro::toByteArray(definedMacros());
927     workingCopy.insert(configurationFileName(), conf);
928 
929     return workingCopy;
930 }
931 
workingCopy() const932 WorkingCopy CppModelManager::workingCopy() const
933 {
934     return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
935 }
936 
codeModelConfiguration() const937 QByteArray CppModelManager::codeModelConfiguration() const
938 {
939     return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
940 }
941 
locatorData() const942 CppLocatorData *CppModelManager::locatorData() const
943 {
944     return &d->m_locatorData;
945 }
946 
tooBigFilesRemoved(const QSet<QString> & files,int fileSizeLimitInMb)947 static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimitInMb)
948 {
949     if (fileSizeLimitInMb <= 0)
950         return files;
951 
952     QSet<QString> result;
953     QFileInfo fileInfo;
954 
955     for (const QString &filePath : files) {
956         fileInfo.setFile(filePath);
957         if (fileSizeExceedsLimit(fileInfo, fileSizeLimitInMb))
958             continue;
959 
960         result << filePath;
961     }
962 
963     return result;
964 }
965 
updateSourceFiles(const QSet<QString> & sourceFiles,ProgressNotificationMode mode)966 QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
967                                                  ProgressNotificationMode mode)
968 {
969     if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
970         return QFuture<void>();
971 
972     const QSet<QString> filteredFiles = tooBigFilesRemoved(sourceFiles, indexerFileSizeLimitInMb());
973 
974     if (d->m_indexingSupporter)
975         d->m_indexingSupporter->refreshSourceFiles(filteredFiles, mode);
976     return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode);
977 }
978 
projectInfos() const979 QList<ProjectInfo> CppModelManager::projectInfos() const
980 {
981     QMutexLocker locker(&d->m_projectMutex);
982     return d->m_projectToProjectsInfo.values();
983 }
984 
projectInfo(ProjectExplorer::Project * project) const985 ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
986 {
987     QMutexLocker locker(&d->m_projectMutex);
988     return d->m_projectToProjectsInfo.value(project, ProjectInfo());
989 }
990 
991 /// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo & projectInfo)992 void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
993 {
994     if (!projectInfo.isValid())
995         return;
996 
997     QMutexLocker snapshotLocker(&d->m_snapshotMutex);
998     foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
999         foreach (const ProjectFile &cxxFile, projectPart->files) {
1000             foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
1001                 d->m_snapshot.remove(fileName);
1002             d->m_snapshot.remove(cxxFile.path);
1003         }
1004     }
1005 }
1006 
cppEditorDocuments() const1007 QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
1008 {
1009     QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
1010     return d->m_cppEditorDocuments.values();
1011 }
1012 
1013 /// \brief Remove all given files from the snapshot.
removeFilesFromSnapshot(const QSet<QString> & filesToRemove)1014 void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
1015 {
1016     QMutexLocker snapshotLocker(&d->m_snapshotMutex);
1017     for (const QString &file : filesToRemove)
1018         d->m_snapshot.remove(file);
1019 }
1020 
1021 class ProjectInfoComparer
1022 {
1023 public:
ProjectInfoComparer(const ProjectInfo & oldProjectInfo,const ProjectInfo & newProjectInfo)1024     ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
1025                         const ProjectInfo &newProjectInfo)
1026         : m_old(oldProjectInfo)
1027         , m_oldSourceFiles(oldProjectInfo.sourceFiles())
1028         , m_new(newProjectInfo)
1029         , m_newSourceFiles(newProjectInfo.sourceFiles())
1030     {}
1031 
definesChanged() const1032     bool definesChanged() const { return m_new.definesChanged(m_old); }
configurationChanged() const1033     bool configurationChanged() const { return m_new.configurationChanged(m_old); }
configurationOrFilesChanged() const1034     bool configurationOrFilesChanged() const { return m_new.configurationOrFilesChanged(m_old); }
1035 
addedFiles() const1036     QSet<QString> addedFiles() const
1037     {
1038         QSet<QString> addedFilesSet = m_newSourceFiles;
1039         addedFilesSet.subtract(m_oldSourceFiles);
1040         return addedFilesSet;
1041     }
1042 
removedFiles() const1043     QSet<QString> removedFiles() const
1044     {
1045         QSet<QString> removedFilesSet = m_oldSourceFiles;
1046         removedFilesSet.subtract(m_newSourceFiles);
1047         return removedFilesSet;
1048     }
1049 
removedProjectParts()1050     QStringList removedProjectParts()
1051     {
1052         QSet<QString> removed = projectPartIds(m_old.projectParts());
1053         removed.subtract(projectPartIds(m_new.projectParts()));
1054         return Utils::toList(removed);
1055     }
1056 
1057     /// Returns a list of common files that have a changed timestamp.
timeStampModifiedFiles(const Snapshot & snapshot) const1058     QSet<QString> timeStampModifiedFiles(const Snapshot &snapshot) const
1059     {
1060         QSet<QString> commonSourceFiles = m_newSourceFiles;
1061         commonSourceFiles.intersect(m_oldSourceFiles);
1062 
1063         QList<Document::Ptr> documentsToCheck;
1064         for (const QString &file : commonSourceFiles) {
1065             if (Document::Ptr document = snapshot.document(file))
1066                 documentsToCheck << document;
1067         }
1068 
1069         return CppModelManager::timeStampModifiedFiles(documentsToCheck);
1070     }
1071 
1072 private:
projectPartIds(const QVector<ProjectPart::Ptr> & projectParts)1073     static QSet<QString> projectPartIds(const QVector<ProjectPart::Ptr> &projectParts)
1074     {
1075         QSet<QString> ids;
1076 
1077         foreach (const ProjectPart::Ptr &projectPart, projectParts)
1078             ids.insert(projectPart->id());
1079 
1080         return ids;
1081     }
1082 
1083 private:
1084     const ProjectInfo &m_old;
1085     const QSet<QString> m_oldSourceFiles;
1086 
1087     const ProjectInfo &m_new;
1088     const QSet<QString> m_newSourceFiles;
1089 };
1090 
1091 /// Make sure that m_projectMutex is locked when calling this.
recalculateProjectPartMappings()1092 void CppModelManager::recalculateProjectPartMappings()
1093 {
1094     d->m_projectPartIdToProjectProjectPart.clear();
1095     d->m_fileToProjectParts.clear();
1096     foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
1097         foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
1098             d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart;
1099             foreach (const ProjectFile &cxxFile, projectPart->files)
1100                 d->m_fileToProjectParts[Utils::FilePath::fromString(cxxFile.path)].append(
1101                             projectPart);
1102 
1103         }
1104     }
1105 
1106     d->m_symbolFinder.clearCache();
1107 }
1108 
watchForCanceledProjectIndexer(const QFuture<void> & future,ProjectExplorer::Project * project)1109 void CppModelManager::watchForCanceledProjectIndexer(const QFuture<void> &future,
1110                                                      ProjectExplorer::Project *project)
1111 {
1112     if (future.isCanceled() || future.isFinished())
1113         return;
1114 
1115     auto watcher = new QFutureWatcher<void>(this);
1116     connect(watcher, &QFutureWatcher<void>::canceled, this, [this, project, watcher]() {
1117         if (d->m_projectToIndexerCanceled.contains(project)) // Project not yet removed
1118             d->m_projectToIndexerCanceled.insert(project, true);
1119         watcher->disconnect(this);
1120         watcher->deleteLater();
1121     });
1122     connect(watcher, &QFutureWatcher<void>::finished, this, [this, project, watcher]() {
1123         d->m_projectToIndexerCanceled.remove(project);
1124         watcher->disconnect(this);
1125         watcher->deleteLater();
1126     });
1127     watcher->setFuture(future);
1128 }
1129 
updateCppEditorDocuments(bool projectsUpdated) const1130 void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
1131 {
1132     // Refresh visible documents
1133     QSet<Core::IDocument *> visibleCppEditorDocuments;
1134     foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
1135         if (Core::IDocument *document = editor->document()) {
1136             const QString filePath = document->filePath().toString();
1137             if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
1138                 visibleCppEditorDocuments.insert(document);
1139                 theCppEditorDocument->processor()->run(projectsUpdated);
1140             }
1141         }
1142     }
1143 
1144     // Mark invisible documents dirty
1145     QSet<Core::IDocument *> invisibleCppEditorDocuments
1146         = Utils::toSet(Core::DocumentModel::openedDocuments());
1147     invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
1148     foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
1149         const QString filePath = document->filePath().toString();
1150         if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
1151             const CppEditorDocumentHandle::RefreshReason refreshReason = projectsUpdated
1152                     ? CppEditorDocumentHandle::ProjectUpdate
1153                     : CppEditorDocumentHandle::Other;
1154             theCppEditorDocument->setRefreshReason(refreshReason);
1155         }
1156     }
1157 }
1158 
updateProjectInfo(const ProjectInfo & newProjectInfo,const QSet<QString> & additionalFiles)1159 QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo,
1160                                                  const QSet<QString> &additionalFiles)
1161 {
1162     if (!newProjectInfo.isValid())
1163         return QFuture<void>();
1164 
1165     ProjectInfo theNewProjectInfo = newProjectInfo;
1166     theNewProjectInfo.finish();
1167 
1168     QSet<QString> filesToReindex;
1169     QStringList removedProjectParts;
1170     bool filesRemoved = false;
1171     ProjectExplorer::Project *project = theNewProjectInfo.project().data();
1172 
1173     { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
1174         QMutexLocker projectLocker(&d->m_projectMutex);
1175 
1176         const QSet<QString> newSourceFiles = theNewProjectInfo.sourceFiles();
1177 
1178         // Check if we can avoid a full reindexing
1179         ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
1180         const bool previousIndexerCanceled = d->m_projectToIndexerCanceled.value(project, false);
1181         if (!previousIndexerCanceled && oldProjectInfo.isValid()) {
1182             ProjectInfoComparer comparer(oldProjectInfo, theNewProjectInfo);
1183 
1184             if (comparer.configurationOrFilesChanged()) {
1185                 d->m_dirty = true;
1186 
1187                 // If the project configuration changed, do a full reindexing
1188                 if (comparer.configurationChanged()) {
1189                     removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
1190                     filesToReindex.unite(newSourceFiles);
1191 
1192                     // The "configuration file" includes all defines and therefore should be updated
1193                     if (comparer.definesChanged()) {
1194                         QMutexLocker snapshotLocker(&d->m_snapshotMutex);
1195                         d->m_snapshot.remove(configurationFileName());
1196                     }
1197 
1198                 // Otherwise check for added and modified files
1199                 } else {
1200                     const QSet<QString> addedFiles = comparer.addedFiles();
1201                     filesToReindex.unite(addedFiles);
1202 
1203                     const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
1204                     filesToReindex.unite(modifiedFiles);
1205                 }
1206 
1207                 // Announce and purge the removed files from the snapshot
1208                 const QSet<QString> removedFiles = comparer.removedFiles();
1209                 if (!removedFiles.isEmpty()) {
1210                     filesRemoved = true;
1211                     emit aboutToRemoveFiles(Utils::toList(removedFiles));
1212                     removeFilesFromSnapshot(removedFiles);
1213                 }
1214             }
1215 
1216             removedProjectParts = comparer.removedProjectParts();
1217 
1218         // A new project was opened/created, do a full indexing
1219         } else {
1220             d->m_dirty = true;
1221             filesToReindex.unite(newSourceFiles);
1222         }
1223 
1224         // Update Project/ProjectInfo and File/ProjectPart table
1225         d->m_projectToProjectsInfo.insert(project, theNewProjectInfo);
1226         recalculateProjectPartMappings();
1227 
1228     } // Mutex scope
1229 
1230     // If requested, dump everything we got
1231     if (DumpProjectInfo)
1232         dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
1233 
1234     // Remove files from snapshot that are not reachable any more
1235     if (filesRemoved)
1236         GC();
1237 
1238     // Announce removed project parts
1239     if (!removedProjectParts.isEmpty())
1240         emit projectPartsRemoved(removedProjectParts);
1241 
1242     // Announce added project parts
1243     emit projectPartsUpdated(theNewProjectInfo.project().data());
1244 
1245     // Ideally, we would update all the editor documents that depend on the 'filesToReindex'.
1246     // However, on e.g. a session restore first the editor documents are created and then the
1247     // project updates come in. That is, there are no reasonable dependency tables based on
1248     // resolved includes that we could rely on.
1249     updateCppEditorDocuments(/*projectsUpdated = */ true);
1250 
1251     filesToReindex.unite(additionalFiles);
1252     // Trigger reindexing
1253     const QFuture<void> indexingFuture = updateSourceFiles(filesToReindex,
1254                                                            ForcedProgressNotification);
1255     if (!filesToReindex.isEmpty()) {
1256         d->m_projectToIndexerCanceled.insert(project, false);
1257     }
1258     watchForCanceledProjectIndexer(indexingFuture, project);
1259     return indexingFuture;
1260 }
1261 
projectPartForId(const QString & projectPartId) const1262 ProjectPart::Ptr CppModelManager::projectPartForId(const QString &projectPartId) const
1263 {
1264     return d->m_projectPartIdToProjectProjectPart.value(projectPartId);
1265 }
1266 
projectPart(const Utils::FilePath & fileName) const1267 QList<ProjectPart::Ptr> CppModelManager::projectPart(const Utils::FilePath &fileName) const
1268 {
1269     QMutexLocker locker(&d->m_projectMutex);
1270     return d->m_fileToProjectParts.value(fileName);
1271 }
1272 
projectPartFromDependencies(const Utils::FilePath & fileName) const1273 QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(
1274         const Utils::FilePath &fileName) const
1275 {
1276     QSet<ProjectPart::Ptr> parts;
1277     const Utils::FilePaths deps = snapshot().filesDependingOn(fileName);
1278 
1279     QMutexLocker locker(&d->m_projectMutex);
1280     for (const Utils::FilePath &dep : deps)
1281         parts.unite(Utils::toSet(d->m_fileToProjectParts.value(dep)));
1282 
1283     return parts.values();
1284 }
1285 
fallbackProjectPart()1286 ProjectPart::Ptr CppModelManager::fallbackProjectPart()
1287 {
1288     QMutexLocker locker(&d->m_fallbackProjectPartMutex);
1289     return d->m_fallbackProjectPart;
1290 }
1291 
isCppEditor(Core::IEditor * editor)1292 bool CppModelManager::isCppEditor(Core::IEditor *editor)
1293 {
1294     return editor->context().contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID);
1295 }
1296 
supportsOutline(const TextEditor::TextDocument * document)1297 bool CppModelManager::supportsOutline(const TextEditor::TextDocument *document)
1298 {
1299     return instance()->d->m_activeModelManagerSupport->supportsOutline(document);
1300 }
1301 
isClangCodeModelActive() const1302 bool CppModelManager::isClangCodeModelActive() const
1303 {
1304     return d->m_activeModelManagerSupport != d->m_builtinModelManagerSupport;
1305 }
1306 
emitDocumentUpdated(Document::Ptr doc)1307 void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
1308 {
1309     if (replaceDocument(doc))
1310         emit documentUpdated(doc);
1311 }
1312 
emitAbstractEditorSupportContentsUpdated(const QString & filePath,const QString & sourcePath,const QByteArray & contents)1313 void CppModelManager::emitAbstractEditorSupportContentsUpdated(const QString &filePath,
1314                                                                const QString &sourcePath,
1315                                                                const QByteArray &contents)
1316 {
1317     emit abstractEditorSupportContentsUpdated(filePath, sourcePath, contents);
1318 }
1319 
emitAbstractEditorSupportRemoved(const QString & filePath)1320 void CppModelManager::emitAbstractEditorSupportRemoved(const QString &filePath)
1321 {
1322     emit abstractEditorSupportRemoved(filePath);
1323 }
1324 
onProjectAdded(ProjectExplorer::Project *)1325 void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
1326 {
1327     QMutexLocker locker(&d->m_projectMutex);
1328     d->m_dirty = true;
1329 }
1330 
delayedGC()1331 void CppModelManager::delayedGC()
1332 {
1333     if (d->m_enableGC)
1334         d->m_delayedGcTimer.start(500);
1335 }
1336 
removedProjectParts(const QStringList & before,const QStringList & after)1337 static QStringList removedProjectParts(const QStringList &before, const QStringList &after)
1338 {
1339     QSet<QString> b = Utils::toSet(before);
1340     b.subtract(Utils::toSet(after));
1341 
1342     return Utils::toList(b);
1343 }
1344 
onAboutToRemoveProject(ProjectExplorer::Project * project)1345 void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
1346 {
1347     QStringList idsOfRemovedProjectParts;
1348 
1349     d->m_projectToIndexerCanceled.remove(project);
1350 
1351     {
1352         QMutexLocker locker(&d->m_projectMutex);
1353         d->m_dirty = true;
1354         const QStringList projectPartsIdsBefore = d->m_projectPartIdToProjectProjectPart.keys();
1355 
1356         d->m_projectToProjectsInfo.remove(project);
1357         recalculateProjectPartMappings();
1358 
1359         const QStringList projectPartsIdsAfter = d->m_projectPartIdToProjectProjectPart.keys();
1360         idsOfRemovedProjectParts = removedProjectParts(projectPartsIdsBefore, projectPartsIdsAfter);
1361     }
1362 
1363     if (!idsOfRemovedProjectParts.isEmpty())
1364         emit projectPartsRemoved(idsOfRemovedProjectParts);
1365 
1366     delayedGC();
1367 }
1368 
onActiveProjectChanged(ProjectExplorer::Project * project)1369 void CppModelManager::onActiveProjectChanged(ProjectExplorer::Project *project)
1370 {
1371     if (!project)
1372         return; // Last project closed.
1373 
1374     {
1375         QMutexLocker locker(&d->m_projectMutex);
1376         if (!d->m_projectToProjectsInfo.contains(project))
1377             return; // Not yet known to us.
1378     }
1379 
1380     updateCppEditorDocuments();
1381 }
1382 
onSourceFilesRefreshed() const1383 void CppModelManager::onSourceFilesRefreshed() const
1384 {
1385     if (BuiltinIndexingSupport::isFindErrorsIndexingActive()) {
1386         QTimer::singleShot(1, QCoreApplication::instance(), &QCoreApplication::quit);
1387         qDebug("FindErrorsIndexing: Done, requesting Qt Creator to quit.");
1388     }
1389 }
1390 
onCurrentEditorChanged(Core::IEditor * editor)1391 void CppModelManager::onCurrentEditorChanged(Core::IEditor *editor)
1392 {
1393     if (!editor || !editor->document())
1394         return;
1395 
1396     const QString filePath = editor->document()->filePath().toString();
1397     if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
1398         const CppEditorDocumentHandle::RefreshReason refreshReason
1399                 = theCppEditorDocument->refreshReason();
1400         if (refreshReason != CppEditorDocumentHandle::None) {
1401             const bool projectsChanged = refreshReason == CppEditorDocumentHandle::ProjectUpdate;
1402             theCppEditorDocument->setRefreshReason(CppEditorDocumentHandle::None);
1403             theCppEditorDocument->processor()->run(projectsChanged);
1404         }
1405     }
1406 }
1407 
onAboutToLoadSession()1408 void CppModelManager::onAboutToLoadSession()
1409 {
1410     if (d->m_delayedGcTimer.isActive())
1411         d->m_delayedGcTimer.stop();
1412     GC();
1413 }
1414 
dependingInternalTargets(const Utils::FilePath & file) const1415 QSet<QString> CppModelManager::dependingInternalTargets(const Utils::FilePath &file) const
1416 {
1417     QSet<QString> result;
1418     const Snapshot snapshot = this->snapshot();
1419     QTC_ASSERT(snapshot.contains(file), return result);
1420     bool wasHeader;
1421     const QString correspondingFile
1422             = correspondingHeaderOrSource(file.toString(), &wasHeader, CacheUsage::ReadOnly);
1423     const Utils::FilePaths dependingFiles = snapshot.filesDependingOn(
1424                 wasHeader ? file : Utils::FilePath::fromString(correspondingFile));
1425     for (const Utils::FilePath &fn : qAsConst(dependingFiles)) {
1426         for (const ProjectPart::Ptr &part : projectPart(fn))
1427             result.insert(part->buildSystemTarget);
1428     }
1429     return result;
1430 }
1431 
internalTargets(const Utils::FilePath & filePath) const1432 QSet<QString> CppModelManager::internalTargets(const Utils::FilePath &filePath) const
1433 {
1434     const QList<ProjectPart::Ptr> projectParts = projectPart(filePath);
1435     // if we have no project parts it's most likely a header with declarations only and CMake based
1436     if (projectParts.isEmpty())
1437         return dependingInternalTargets(filePath);
1438     QSet<QString> targets;
1439     for (const ProjectPart::Ptr &part : projectParts) {
1440         targets.insert(part->buildSystemTarget);
1441         if (part->buildTargetType != ProjectExplorer::BuildTargetType::Executable)
1442             targets.unite(dependingInternalTargets(filePath));
1443     }
1444     return targets;
1445 }
1446 
renameIncludes(const Utils::FilePath & oldFilePath,const Utils::FilePath & newFilePath)1447 void CppModelManager::renameIncludes(const Utils::FilePath &oldFilePath,
1448                                      const Utils::FilePath &newFilePath)
1449 {
1450     if (oldFilePath.isEmpty() || newFilePath.isEmpty())
1451         return;
1452 
1453     // We just want to handle renamings so return when the file was actually moved.
1454     if (oldFilePath.absolutePath() != newFilePath.absolutePath())
1455         return;
1456 
1457     const TextEditor::RefactoringChanges changes;
1458 
1459     foreach (Snapshot::IncludeLocation loc,
1460              snapshot().includeLocationsOfDocument(oldFilePath.toString())) {
1461         TextEditor::RefactoringFilePtr file = changes.file(
1462             Utils::FilePath::fromString(loc.first->fileName()));
1463         const QTextBlock &block = file->document()->findBlockByNumber(loc.second - 1);
1464         const int replaceStart = block.text().indexOf(oldFilePath.fileName());
1465         if (replaceStart > -1) {
1466             Utils::ChangeSet changeSet;
1467             changeSet.replace(block.position() + replaceStart,
1468                               block.position() + replaceStart + oldFilePath.fileName().length(),
1469                               newFilePath.fileName());
1470             file->setChangeSet(changeSet);
1471             file->apply();
1472         }
1473     }
1474 }
1475 
1476 // Return the class name which function belongs to
belongingClassName(const Function * function)1477 static const char *belongingClassName(const Function *function)
1478 {
1479     if (!function)
1480         return nullptr;
1481 
1482     if (auto funcName = function->name()) {
1483         if (auto qualifiedNameId = funcName->asQualifiedNameId()) {
1484             if (const Name *funcBaseName = qualifiedNameId->base()) {
1485                 if (auto identifier = funcBaseName->identifier())
1486                     return identifier->chars();
1487             }
1488         }
1489     }
1490 
1491     return nullptr;
1492 }
1493 
symbolsInFiles(const QSet<Utils::FilePath> & files) const1494 QSet<QString> CppModelManager::symbolsInFiles(const QSet<Utils::FilePath> &files) const
1495 {
1496     QSet<QString> uniqueSymbols;
1497     const Snapshot cppSnapShot = snapshot();
1498 
1499     // Iterate over the files and get interesting symbols
1500     for (const Utils::FilePath &file : files) {
1501         // Add symbols from the C++ code model
1502         const CPlusPlus::Document::Ptr doc = cppSnapShot.document(file);
1503         if (!doc.isNull() && doc->control()) {
1504             const CPlusPlus::Control *ctrl = doc->control();
1505             CPlusPlus::Symbol **symPtr = ctrl->firstSymbol(); // Read-only
1506             while (symPtr != ctrl->lastSymbol()) {
1507                 const CPlusPlus::Symbol *sym = *symPtr;
1508 
1509                 const CPlusPlus::Identifier *symId = sym->identifier();
1510                 // Add any class, function or namespace identifiers
1511                 if ((sym->isClass() || sym->isFunction() || sym->isNamespace()) && symId
1512                     && symId->chars()) {
1513                     uniqueSymbols.insert(QString::fromUtf8(symId->chars()));
1514                 }
1515 
1516                 // Handle specific case : get "Foo" in "void Foo::function() {}"
1517                 if (sym->isFunction() && !sym->asFunction()->isDeclaration()) {
1518                     const char *className = belongingClassName(sym->asFunction());
1519                     if (className)
1520                         uniqueSymbols.insert(QString::fromUtf8(className));
1521                 }
1522 
1523                 ++symPtr;
1524             }
1525         }
1526     }
1527     return uniqueSymbols;
1528 }
1529 
onCoreAboutToClose()1530 void CppModelManager::onCoreAboutToClose()
1531 {
1532     Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
1533     d->m_enableGC = false;
1534 }
1535 
setupFallbackProjectPart()1536 void CppModelManager::setupFallbackProjectPart()
1537 {
1538     ProjectPart::Ptr part(new ProjectPart);
1539 
1540     part->projectMacros = definedMacros();
1541     part->headerPaths = headerPaths();
1542 
1543     // Do not activate ObjectiveCExtensions since this will lead to the
1544     // "objective-c++" language option for a project-less *.cpp file.
1545     part->languageExtensions = Utils::LanguageExtension::All;
1546     part->languageExtensions &= ~Utils::LanguageExtensions(
1547         Utils::LanguageExtension::ObjectiveC);
1548 
1549     part->qtVersion = Utils::QtVersion::Qt5;
1550 
1551     // TODO: Use different fallback toolchain for different kinds of files?
1552     const Kit * const defaultKit = KitManager::isLoaded() ? KitManager::defaultKit() : nullptr;
1553     const ToolChain * const defaultTc = defaultKit
1554             ? ToolChainKitAspect::cxxToolChain(defaultKit) : nullptr;
1555     if (defaultKit && defaultTc) {
1556         Utils::FilePath sysroot = SysRootKitAspect::sysRoot(defaultKit);
1557         if (sysroot.isEmpty())
1558             sysroot = Utils::FilePath::fromString(defaultTc->sysRoot());
1559         Utils::Environment env = defaultKit->buildEnvironment();
1560         ToolChainInfo tcInfo(defaultTc, sysroot.toString(), env);
1561         part->setupToolchainProperties(tcInfo, {});
1562         if (part->language == Language::C)
1563             part->languageVersion = Utils::LanguageVersion::LatestC;
1564         else
1565             part->languageVersion = Utils::LanguageVersion::LatestCxx;
1566     }
1567     part->updateLanguageFeatures();
1568 
1569     QMutexLocker locker(&d->m_fallbackProjectPartMutex);
1570     d->m_fallbackProjectPart = part;
1571 }
1572 
GC()1573 void CppModelManager::GC()
1574 {
1575     if (!d->m_enableGC)
1576         return;
1577 
1578     // Collect files of opened editors and editor supports (e.g. ui code model)
1579     QStringList filesInEditorSupports;
1580     foreach (const CppEditorDocumentHandle *editorDocument, cppEditorDocuments())
1581         filesInEditorSupports << editorDocument->filePath();
1582 
1583     foreach (AbstractEditorSupport *abstractEditorSupport, abstractEditorSupports())
1584         filesInEditorSupports << abstractEditorSupport->fileName();
1585 
1586     Snapshot currentSnapshot = snapshot();
1587     QSet<Utils::FilePath> reachableFiles;
1588     // The configuration file is part of the project files, which is just fine.
1589     // If single files are open, without any project, then there is no need to
1590     // keep the configuration file around.
1591     QStringList todo = filesInEditorSupports + projectFiles();
1592 
1593     // Collect all files that are reachable from the project files
1594     while (!todo.isEmpty()) {
1595         const QString file = todo.last();
1596         todo.removeLast();
1597 
1598         const Utils::FilePath fileName = Utils::FilePath::fromString(file);
1599         if (reachableFiles.contains(fileName))
1600             continue;
1601         reachableFiles.insert(fileName);
1602 
1603         if (Document::Ptr doc = currentSnapshot.document(file))
1604             todo += doc->includedFiles();
1605     }
1606 
1607     // Find out the files in the current snapshot that are not reachable from the project files
1608     QStringList notReachableFiles;
1609     Snapshot newSnapshot;
1610     for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
1611         const Utils::FilePath &fileName = it.key();
1612 
1613         if (reachableFiles.contains(fileName))
1614             newSnapshot.insert(it.value());
1615         else
1616             notReachableFiles.append(fileName.toString());
1617     }
1618 
1619     // Announce removing files and replace the snapshot
1620     emit aboutToRemoveFiles(notReachableFiles);
1621     replaceSnapshot(newSnapshot);
1622     emit gcFinished();
1623 }
1624 
finishedRefreshingSourceFiles(const QSet<QString> & files)1625 void CppModelManager::finishedRefreshingSourceFiles(const QSet<QString> &files)
1626 {
1627     emit sourceFilesRefreshed(files);
1628 }
1629 
activateClangCodeModel(ModelManagerSupportProvider * modelManagerSupportProvider)1630 void CppModelManager::activateClangCodeModel(
1631         ModelManagerSupportProvider *modelManagerSupportProvider)
1632 {
1633     QTC_ASSERT(modelManagerSupportProvider, return);
1634 
1635     d->m_activeModelManagerSupport = modelManagerSupportProvider->createModelManagerSupport();
1636     d->m_refactoringEngines[RefactoringEngineType::ClangCodeModel] =
1637             &d->m_activeModelManagerSupport->refactoringEngineInterface();
1638 }
1639 
completionAssistProvider() const1640 CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const
1641 {
1642     return d->m_activeModelManagerSupport->completionAssistProvider();
1643 }
1644 
functionHintAssistProvider() const1645 CppCompletionAssistProvider *CppModelManager::functionHintAssistProvider() const
1646 {
1647     return d->m_activeModelManagerSupport->functionHintAssistProvider();
1648 }
1649 
createHoverHandler() const1650 TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const
1651 {
1652     return d->m_activeModelManagerSupport->createHoverHandler();
1653 }
1654 
createEditorDocumentProcessor(TextEditor::TextDocument * baseTextDocument) const1655 BaseEditorDocumentProcessor *CppModelManager::createEditorDocumentProcessor(
1656     TextEditor::TextDocument *baseTextDocument) const
1657 {
1658     return d->m_activeModelManagerSupport->createEditorDocumentProcessor(baseTextDocument);
1659 }
1660 
setIndexingSupport(CppIndexingSupport * indexingSupport)1661 void CppModelManager::setIndexingSupport(CppIndexingSupport *indexingSupport)
1662 {
1663     if (indexingSupport) {
1664         if (dynamic_cast<BuiltinIndexingSupport *>(indexingSupport))
1665             d->m_indexingSupporter = nullptr;
1666         else
1667             d->m_indexingSupporter = indexingSupport;
1668     }
1669 }
1670 
indexingSupport()1671 CppIndexingSupport *CppModelManager::indexingSupport()
1672 {
1673     return d->m_indexingSupporter ? d->m_indexingSupporter : d->m_internalIndexingSupport;
1674 }
1675 
projectFiles()1676 QStringList CppModelManager::projectFiles()
1677 {
1678     QMutexLocker locker(&d->m_projectMutex);
1679     ensureUpdated();
1680 
1681     return d->m_projectFiles;
1682 }
1683 
headerPaths()1684 ProjectExplorer::HeaderPaths CppModelManager::headerPaths()
1685 {
1686     QMutexLocker locker(&d->m_projectMutex);
1687     ensureUpdated();
1688 
1689     return d->m_headerPaths;
1690 }
1691 
setHeaderPaths(const ProjectExplorer::HeaderPaths & headerPaths)1692 void CppModelManager::setHeaderPaths(const ProjectExplorer::HeaderPaths &headerPaths)
1693 {
1694     QMutexLocker locker(&d->m_projectMutex);
1695     d->m_headerPaths = headerPaths;
1696 }
1697 
definedMacros()1698 ProjectExplorer::Macros CppModelManager::definedMacros()
1699 {
1700     QMutexLocker locker(&d->m_projectMutex);
1701     ensureUpdated();
1702 
1703     return d->m_definedMacros;
1704 }
1705 
enableGarbageCollector(bool enable)1706 void CppModelManager::enableGarbageCollector(bool enable)
1707 {
1708     d->m_delayedGcTimer.stop();
1709     d->m_enableGC = enable;
1710 }
1711 
symbolFinder()1712 SymbolFinder *CppModelManager::symbolFinder()
1713 {
1714     return &d->m_symbolFinder;
1715 }
1716 
sharedThreadPool()1717 QThreadPool *CppModelManager::sharedThreadPool()
1718 {
1719     return &d->m_threadPool;
1720 }
1721 
1722 } // namespace CppTools
1723