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 ¯os,
782 QSet<ProjectExplorer::Macro> &alreadyIn)
783 {
784 for (const ProjectExplorer::Macro ¯o : 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 ¯o)
896 {
897 d->m_findReferences->findMacroUses(macro);
898 }
899
renameMacroUsages(const CPlusPlus::Macro & macro,const QString & replacement)900 void CppModelManager::renameMacroUsages(const CPlusPlus::Macro ¯o, 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