summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libs/cplusplus/CppDocument.cpp259
-rw-r--r--src/libs/cplusplus/CppDocument.h23
-rw-r--r--src/libs/cplusplus/ModelManagerInterface.h2
-rw-r--r--src/libs/qmljs/qmljslink.cpp4
-rw-r--r--src/plugins/cpptools/cppmodelmanager.cpp169
-rw-r--r--src/plugins/cpptools/cppmodelmanager.h2
-rw-r--r--src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp486
-rw-r--r--src/plugins/qmljstools/qmljsfindexportedcpptypes.h55
-rw-r--r--src/plugins/qmljstools/qmljsmodelmanager.cpp51
-rw-r--r--src/plugins/qmljstools/qmljsmodelmanager.h9
-rw-r--r--src/plugins/qmljstools/qmljstools-lib.pri6
11 files changed, 616 insertions, 450 deletions
diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index a72648b8ba..169ec2b694 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -256,7 +256,8 @@ Document::Document(const QString &fileName)
: _fileName(QDir::cleanPath(fileName)),
_globalNamespace(0),
_revision(0),
- _editorRevision(0)
+ _editorRevision(0),
+ _fastCheck(false)
{
_control = new Control();
@@ -574,8 +575,10 @@ void Document::check(CheckMode mode)
_globalNamespace = _control->newNamespace(0);
Bind semantic(_translationUnit);
- if (mode == FastCheck)
+ if (mode == FastCheck) {
+ _fastCheck = true;
semantic.setSkipFunctionBodies(true);
+ }
if (! _translationUnit->ast())
return; // nothing to do.
@@ -589,253 +592,19 @@ void Document::check(CheckMode mode)
}
}
-class FindExposedQmlTypes : protected ASTVisitor
+void Document::keepSourceAndAST()
{
- Document *_doc;
- QList<Document::ExportedQmlType> _exportedTypes;
- CompoundStatementAST *_compound;
- ASTMatcher _matcher;
- ASTPatternBuilder _builder;
- Overview _overview;
-
-public:
- FindExposedQmlTypes(Document *doc)
- : ASTVisitor(doc->translationUnit())
- , _doc(doc)
- , _compound(0)
- {}
-
- QList<Document::ExportedQmlType> operator()()
- {
- _exportedTypes.clear();
- accept(translationUnit()->ast());
- return _exportedTypes;
- }
-
-protected:
- virtual bool visit(CompoundStatementAST *ast)
- {
- CompoundStatementAST *old = _compound;
- _compound = ast;
- accept(ast->statement_list);
- _compound = old;
- return false;
- }
-
- virtual bool visit(CallAST *ast)
- {
- IdExpressionAST *idExp = ast->base_expression->asIdExpression();
- if (!idExp || !idExp->name)
- return false;
- TemplateIdAST *templateId = idExp->name->asTemplateId();
- if (!templateId || !templateId->identifier_token)
- return false;
-
- // check the name
- const Identifier *templateIdentifier = translationUnit()->identifier(templateId->identifier_token);
- if (!templateIdentifier)
- return false;
- const QString callName = QString::fromUtf8(templateIdentifier->chars());
- if (callName != QLatin1String("qmlRegisterType"))
- return false;
-
- // must have a single typeid template argument
- if (!templateId->template_argument_list || !templateId->template_argument_list->value
- || templateId->template_argument_list->next)
- return false;
- TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
- if (!typeId)
- return false;
-
- // must have four arguments
- if (!ast->expression_list
- || !ast->expression_list->value || !ast->expression_list->next
- || !ast->expression_list->next->value || !ast->expression_list->next->next
- || !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
- || !ast->expression_list->next->next->next->value
- || ast->expression_list->next->next->next->next)
- return false;
-
- // last argument must be a string literal
- const StringLiteral *nameLit = 0;
- if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
- nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
- if (!nameLit) {
- // disable this warning for now, we don't want to encourage using string literals if they don't mean to
- // in the future, we will also accept annotations for the qmlRegisterType arguments in comments
-// translationUnit()->warning(ast->expression_list->next->next->next->value->firstToken(),
-// "The type will only be available in Qt Creator's QML editors when the type name is a string literal");
- return false;
- }
-
- // if the first argument is a string literal, things are easy
- QString packageName;
- if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral()) {
- const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
- packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
- }
- // as a special case, allow an identifier package argument if there's a
- // Q_ASSERT(QLatin1String(uri) == QLatin1String("actual uri"));
- // in the enclosing compound statement
- IdExpressionAST *uriName = ast->expression_list->value->asIdExpression();
- if (packageName.isEmpty() && uriName && _compound) {
- for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
- StatementAST *stmt = it->value;
-
- packageName = nameOfUriAssert(stmt, uriName);
- if (!packageName.isEmpty())
- break;
- }
- }
-
- // second and third argument must be integer literals
- const NumericLiteral *majorLit = 0;
- const NumericLiteral *minorLit = 0;
- if (NumericLiteralAST *majorAst = ast->expression_list->next->value->asNumericLiteral())
- majorLit = translationUnit()->numericLiteral(majorAst->literal_token);
- if (NumericLiteralAST *minorAst = ast->expression_list->next->next->value->asNumericLiteral())
- minorLit = translationUnit()->numericLiteral(minorAst->literal_token);
-
- // build the descriptor
- Document::ExportedQmlType exportedType;
- exportedType.typeName = QString::fromUtf8(nameLit->chars(), nameLit->size());
- if (!packageName.isEmpty() && majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
- exportedType.packageName = packageName;
- exportedType.majorVersion = QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt();
- exportedType.minorVersion = QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt();
- } else {
- // disable this warning, see above for details
-// translationUnit()->warning(ast->base_expression->firstToken(),
-// "The module will not be available in Qt Creator's QML editors because the uri and version numbers\n"
-// "cannot be determined by static analysis. The type will still be available globally.");
- exportedType.packageName = QLatin1String("<default>");
- }
-
- // we want to do lookup later, so also store the surrounding scope
- unsigned line, column;
- translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
- exportedType.scope = _doc->scopeAt(line, column);
-
- // and the expression
- const Token begin = translationUnit()->tokenAt(typeId->firstToken());
- const Token last = translationUnit()->tokenAt(typeId->lastToken() - 1);
- exportedType.typeExpression = _doc->source().mid(begin.begin(), last.end() - begin.begin());
-
- _exportedTypes += exportedType;
-
- return false;
- }
-
-private:
- QString stringOf(AST *ast)
- {
- const Token begin = translationUnit()->tokenAt(ast->firstToken());
- const Token last = translationUnit()->tokenAt(ast->lastToken() - 1);
- return _doc->source().mid(begin.begin(), last.end() - begin.begin());
- }
-
- ExpressionAST *skipStringCall(ExpressionAST *exp)
- {
- if (!exp)
- return 0;
-
- IdExpressionAST *callName = _builder.IdExpression();
- CallAST *call = _builder.Call(callName);
- if (!exp->match(call, &_matcher))
- return exp;
-
- const QString name = stringOf(callName);
- if (name != QLatin1String("QLatin1String")
- && name != QLatin1String("QString"))
- return exp;
-
- if (!call->expression_list || call->expression_list->next)
- return exp;
-
- return call->expression_list->value;
- }
-
- QString nameOfUriAssert(StatementAST *stmt, IdExpressionAST *uriName)
- {
- QString null;
-
- IdExpressionAST *outerCallName = _builder.IdExpression();
- BinaryExpressionAST *binary = _builder.BinaryExpression();
- // assert(... == ...);
- ExpressionStatementAST *pattern = _builder.ExpressionStatement(
- _builder.Call(outerCallName, _builder.ExpressionList(
- binary)));
-
- if (!stmt->match(pattern, &_matcher)) {
- outerCallName = _builder.IdExpression();
- binary = _builder.BinaryExpression();
- // the expansion of Q_ASSERT(...),
- // ((!(... == ...)) ? qt_assert(...) : ...);
- pattern = _builder.ExpressionStatement(
- _builder.NestedExpression(
- _builder.ConditionalExpression(
- _builder.NestedExpression(
- _builder.UnaryExpression(
- _builder.NestedExpression(
- binary))),
- _builder.Call(outerCallName))));
-
- if (!stmt->match(pattern, &_matcher))
- return null;
- }
-
- const QString outerCall = stringOf(outerCallName);
- if (outerCall != QLatin1String("qt_assert")
- && outerCall != QLatin1String("assert")
- && outerCall != QLatin1String("Q_ASSERT"))
- return null;
-
- if (translationUnit()->tokenAt(binary->binary_op_token).kind() != T_EQUAL_EQUAL)
- return null;
-
- ExpressionAST *lhsExp = skipStringCall(binary->left_expression);
- ExpressionAST *rhsExp = skipStringCall(binary->right_expression);
- if (!lhsExp || !rhsExp)
- return null;
-
- StringLiteralAST *uriString = lhsExp->asStringLiteral();
- IdExpressionAST *uriArgName = lhsExp->asIdExpression();
- if (!uriString)
- uriString = rhsExp->asStringLiteral();
- if (!uriArgName)
- uriArgName = rhsExp->asIdExpression();
- if (!uriString || !uriArgName)
- return null;
-
- if (stringOf(uriArgName) != stringOf(uriName))
- return null;
-
- const StringLiteral *packageLit = translationUnit()->stringLiteral(uriString->literal_token);
- return QString::fromUtf8(packageLit->chars(), packageLit->size());
- }
-};
-
-void Document::findExposedQmlTypes()
-{
- if (! _translationUnit->ast())
- return;
-
- QByteArray qmlRegisterTypeToken("qmlRegisterType");
- if (_translationUnit->control()->findIdentifier(
- qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
- FindExposedQmlTypes finder(this);
- _exportedQmlTypes = finder();
- }
-}
-
-void Document::releaseSource()
-{
- _source.clear();
+ _keepSourceAndASTCount.ref();
}
-void Document::releaseTranslationUnit()
+void Document::releaseSourceAndAST()
{
- _translationUnit->release();
+ if (!_keepSourceAndASTCount.deref()) {
+ _source.clear();
+ _translationUnit->release();
+ if (_fastCheck)
+ _control->squeeze();
+ }
}
Snapshot::Snapshot()
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index 48d72cad3c..024fec93fb 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -40,6 +40,7 @@
#include <QtCore/QDateTime>
#include <QtCore/QHash>
#include <QtCore/QFileInfo>
+#include <QtCore/QAtomicInt>
namespace CPlusPlus {
@@ -125,11 +126,6 @@ public:
void check(CheckMode mode = FullCheck);
- void findExposedQmlTypes();
-
- void releaseSource();
- void releaseTranslationUnit();
-
static Ptr create(const QString &fileName);
class DiagnosticMessage
@@ -320,18 +316,8 @@ public:
const MacroUse *findMacroUseAt(unsigned offset) const;
const UndefinedMacroUse *findUndefinedMacroUseAt(unsigned offset) const;
- class ExportedQmlType {
- public:
- QString packageName;
- QString typeName;
- int majorVersion;
- int minorVersion;
- Scope *scope;
- QString typeExpression;
- };
-
- QList<ExportedQmlType> exportedQmlTypes() const
- { return _exportedQmlTypes; }
+ void keepSourceAndAST();
+ void releaseSourceAndAST();
private:
QString _fileName;
@@ -344,11 +330,12 @@ private:
QList<Block> _skippedBlocks;
QList<MacroUse> _macroUses;
QList<UndefinedMacroUse> _undefinedMacroUses;
- QList<ExportedQmlType> _exportedQmlTypes;
QByteArray _source;
QDateTime _lastModified;
+ QAtomicInt _keepSourceAndASTCount;
unsigned _revision;
unsigned _editorRevision;
+ bool _fastCheck;
friend class Snapshot;
};
diff --git a/src/libs/cplusplus/ModelManagerInterface.h b/src/libs/cplusplus/ModelManagerInterface.h
index 0a9cf6eaee..75d084053a 100644
--- a/src/libs/cplusplus/ModelManagerInterface.h
+++ b/src/libs/cplusplus/ModelManagerInterface.h
@@ -140,8 +140,6 @@ public:
virtual void findMacroUsages(const CPlusPlus::Macro &macro) = 0;
- virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const = 0;
-
Q_SIGNALS:
void documentUpdated(CPlusPlus::Document::Ptr doc);
void sourceFilesRefreshed(const QStringList &files);
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 7c2eaaa816..d43dbbe05c 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -502,7 +502,9 @@ void Link::loadImplicitDefaultImports(Imports *imports)
import.info = info;
import.object = new ObjectValue(d->valueOwner);
foreach (QmlObjectValue *object,
- d->valueOwner->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) {
+ d->valueOwner->cppQmlTypes().typesForImport(
+ defaultPackage,
+ ComponentVersion(ComponentVersion::MaxVersion, ComponentVersion::MaxVersion))) {
import.object->setMember(object->className(), object);
}
d->importCache.insert(ImportCacheKey(info), import);
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index 489a58c03c..1aab932648 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -290,15 +290,11 @@ public:
void operator()()
{
_doc->check(_mode);
- _doc->findExposedQmlTypes();
- _doc->releaseSource();
- _doc->releaseTranslationUnit();
-
- if (_mode == Document::FastCheck)
- _doc->control()->squeeze();
if (_modelManager)
_modelManager->emitDocumentUpdated(_doc); // ### TODO: compress
+
+ _doc->releaseSourceAndAST();
}
};
} // end of anonymous namespace
@@ -590,6 +586,7 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
const QByteArray preprocessedCode = preprocess(fileName, contents);
doc->setSource(preprocessedCode);
+ doc->keepSourceAndAST();
doc->tokenize();
snapshot.insert(doc);
@@ -1284,166 +1281,6 @@ void CppModelManager::GC()
protectSnapshot.unlock();
}
-static FullySpecifiedType stripPointerAndReference(const FullySpecifiedType &type)
-{
- Type *t = type.type();
- while (t) {
- if (PointerType *ptr = t->asPointerType())
- t = ptr->elementType().type();
- else if (ReferenceType *ref = t->asReferenceType())
- t = ref->elementType().type();
- else
- break;
- }
- return FullySpecifiedType(t);
-}
-
-static QString toQmlType(const FullySpecifiedType &type)
-{
- Overview overview;
- QString result = overview(stripPointerAndReference(type));
- if (result == QLatin1String("QString"))
- result = QLatin1String("string");
- return result;
-}
-
-static Class *lookupClass(const QString &expression, Scope *scope, TypeOfExpression &typeOf)
-{
- QList<LookupItem> results = typeOf(expression, scope);
- Class *klass = 0;
- foreach (const LookupItem &item, results) {
- if (item.declaration()) {
- klass = item.declaration()->asClass();
- if (klass)
- return klass;
- }
- }
- return 0;
-}
-
-static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
- QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *classes,
- TypeOfExpression &typeOf)
-{
- using namespace LanguageUtils;
-
- Overview namePrinter;
-
- classes->insert(klass, fmo);
-
- for (unsigned i = 0; i < klass->memberCount(); ++i) {
- Symbol *member = klass->memberAt(i);
- if (!member->name())
- continue;
- if (Function *func = member->type()->asFunctionType()) {
- if (!func->isSlot() && !func->isInvokable() && !func->isSignal())
- continue;
- FakeMetaMethod method(namePrinter(func->name()), toQmlType(func->returnType()));
- if (func->isSignal())
- method.setMethodType(FakeMetaMethod::Signal);
- else
- method.setMethodType(FakeMetaMethod::Slot);
- for (unsigned a = 0; a < func->argumentCount(); ++a) {
- Symbol *arg = func->argumentAt(a);
- QString name(CppModelManager::tr("unnamed"));
- if (arg->name())
- name = namePrinter(arg->name());
- method.addParameter(name, toQmlType(arg->type()));
- }
- fmo->addMethod(method);
- }
- if (QtPropertyDeclaration *propDecl = member->asQtPropertyDeclaration()) {
- const FullySpecifiedType &type = propDecl->type();
- const bool isList = false; // ### fixme
- const bool isWritable = propDecl->flags() & QtPropertyDeclaration::WriteFunction;
- const bool isPointer = type.type() && type.type()->isPointerType();
- const int revision = 0; // ### fixme
- FakeMetaProperty property(
- namePrinter(propDecl->name()),
- toQmlType(type),
- isList, isWritable, isPointer,
- revision);
- fmo->addProperty(property);
- }
- if (QtEnum *qtEnum = member->asQtEnum()) {
- // find the matching enum
- Enum *e = 0;
- QList<LookupItem> result = typeOf(namePrinter(qtEnum->name()), klass);
- foreach (const LookupItem &item, result) {
- if (item.declaration()) {
- e = item.declaration()->asEnum();
- if (e)
- break;
- }
- }
- if (!e)
- continue;
-
- FakeMetaEnum metaEnum(namePrinter(e->name()));
- for (unsigned j = 0; j < e->memberCount(); ++j) {
- Symbol *enumMember = e->memberAt(j);
- if (!enumMember->name())
- continue;
- metaEnum.addKey(namePrinter(enumMember->name()), 0);
- }
- fmo->addEnum(metaEnum);
- }
- }
-
- // only single inheritance is supported
- if (klass->baseClassCount() > 0) {
- BaseClass *base = klass->baseClassAt(0);
- if (!base->name())
- return;
-
- const QString baseClassName = namePrinter(base->name());
- fmo->setSuperclassName(baseClassName);
-
- Class *baseClass = lookupClass(baseClassName, klass, typeOf);
- if (!baseClass)
- return;
-
- FakeMetaObject::Ptr baseFmo = classes->value(baseClass);
- if (!baseFmo) {
- baseFmo = FakeMetaObject::Ptr(new FakeMetaObject);
- populate(baseFmo, baseClass, classes, typeOf);
- }
- }
-}
-
-QList<LanguageUtils::FakeMetaObject::ConstPtr> CppModelManager::exportedQmlObjects(const Document::Ptr &doc) const
-{
- using namespace LanguageUtils;
- QList<FakeMetaObject::ConstPtr> exportedObjects;
- QHash<Class *, FakeMetaObject::Ptr> classes;
-
- const QList<CPlusPlus::Document::ExportedQmlType> exported = doc->exportedQmlTypes();
- if (exported.isEmpty())
- return exportedObjects;
-
- TypeOfExpression typeOf;
- const Snapshot currentSnapshot = snapshot();
- typeOf.init(doc, currentSnapshot);
- foreach (const Document::ExportedQmlType &exportedType, exported) {
- FakeMetaObject::Ptr fmo(new FakeMetaObject);
- fmo->addExport(exportedType.typeName, exportedType.packageName,
- ComponentVersion(exportedType.majorVersion, exportedType.minorVersion));
- exportedObjects += fmo;
-
- Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
- if (!klass)
- continue;
-
- // add the no-package export, so the cpp name can be used in properties
- Overview overview;
- fmo->addExport(overview(klass->name()), QString(), ComponentVersion());
-
- populate(fmo, klass, &classes, typeOf);
- }
-
- return exportedObjects;
-}
-
void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
{
emit sourceFilesRefreshed(files);
diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h
index d902f7d4d1..13a1b85195 100644
--- a/src/plugins/cpptools/cppmodelmanager.h
+++ b/src/plugins/cpptools/cppmodelmanager.h
@@ -128,8 +128,6 @@ public:
virtual void findMacroUsages(const CPlusPlus::Macro &macro);
- virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const;
-
void finishedRefreshingSourceFiles(const QStringList &files);
Q_SIGNALS:
diff --git a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
new file mode 100644
index 0000000000..82e6dcb085
--- /dev/null
+++ b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
@@ -0,0 +1,486 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "qmljsfindexportedcpptypes.h"
+
+#include <qmljs/qmljsinterpreter.h>
+#include <cplusplus/AST.h>
+#include <cplusplus/TranslationUnit.h>
+#include <cplusplus/ASTVisitor.h>
+#include <cplusplus/ASTMatcher.h>
+#include <cplusplus/ASTPatternBuilder.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+#include <cplusplus/Names.h>
+#include <cplusplus/Literals.h>
+#include <cplusplus/CoreTypes.h>
+#include <cplusplus/Symbols.h>
+#include <utils/qtcassert.h>
+
+#include <QtCore/QDebug>
+
+using namespace QmlJSTools;
+
+namespace {
+using namespace CPlusPlus;
+
+class ExportedQmlType {
+public:
+ QString packageName;
+ QString typeName;
+ LanguageUtils::ComponentVersion version;
+ Scope *scope;
+ QString typeExpression;
+};
+
+class FindExportsVisitor : protected ASTVisitor
+{
+ CPlusPlus::Document::Ptr _doc;
+ QList<ExportedQmlType> _exportedTypes;
+ CompoundStatementAST *_compound;
+ ASTMatcher _matcher;
+ ASTPatternBuilder _builder;
+ Overview _overview;
+
+public:
+ FindExportsVisitor(CPlusPlus::Document::Ptr doc)
+ : ASTVisitor(doc->translationUnit())
+ , _doc(doc)
+ , _compound(0)
+ {}
+
+ QList<ExportedQmlType> operator()()
+ {
+ _exportedTypes.clear();
+ accept(translationUnit()->ast());
+ return _exportedTypes;
+ }
+
+protected:
+ virtual bool visit(CompoundStatementAST *ast)
+ {
+ CompoundStatementAST *old = _compound;
+ _compound = ast;
+ accept(ast->statement_list);
+ _compound = old;
+ return false;
+ }
+
+ virtual bool visit(CallAST *ast)
+ {
+ IdExpressionAST *idExp = ast->base_expression->asIdExpression();
+ if (!idExp || !idExp->name)
+ return false;
+ TemplateIdAST *templateId = idExp->name->asTemplateId();
+ if (!templateId || !templateId->identifier_token)
+ return false;
+
+ // check the name
+ const Identifier *templateIdentifier = translationUnit()->identifier(templateId->identifier_token);
+ if (!templateIdentifier)
+ return false;
+ const QString callName = QString::fromUtf8(templateIdentifier->chars());
+ if (callName != QLatin1String("qmlRegisterType"))
+ return false;
+
+ // must have a single typeid template argument
+ if (!templateId->template_argument_list || !templateId->template_argument_list->value
+ || templateId->template_argument_list->next)
+ return false;
+ TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
+ if (!typeId)
+ return false;
+
+ // must have four arguments
+ if (!ast->expression_list
+ || !ast->expression_list->value || !ast->expression_list->next
+ || !ast->expression_list->next->value || !ast->expression_list->next->next
+ || !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
+ || !ast->expression_list->next->next->next->value
+ || ast->expression_list->next->next->next->next)
+ return false;
+
+ // last argument must be a string literal
+ const StringLiteral *nameLit = 0;
+ if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
+ nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
+ if (!nameLit) {
+ // disable this warning for now, we don't want to encourage using string literals if they don't mean to
+ // in the future, we will also accept annotations for the qmlRegisterType arguments in comments
+// translationUnit()->warning(ast->expression_list->next->next->next->value->firstToken(),
+// "The type will only be available in Qt Creator's QML editors when the type name is a string literal");
+ return false;
+ }
+
+ // if the first argument is a string literal, things are easy
+ QString packageName;
+ if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral()) {
+ const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
+ packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
+ }
+ // as a special case, allow an identifier package argument if there's a
+ // Q_ASSERT(QLatin1String(uri) == QLatin1String("actual uri"));
+ // in the enclosing compound statement
+ IdExpressionAST *uriName = ast->expression_list->value->asIdExpression();
+ if (packageName.isEmpty() && uriName && _compound) {
+ for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
+ StatementAST *stmt = it->value;
+
+ packageName = nameOfUriAssert(stmt, uriName);
+ if (!packageName.isEmpty())
+ break;
+ }
+ }
+
+ // second and third argument must be integer literals
+ const NumericLiteral *majorLit = 0;
+ const NumericLiteral *minorLit = 0;
+ if (NumericLiteralAST *majorAst = ast->expression_list->next->value->asNumericLiteral())
+ majorLit = translationUnit()->numericLiteral(majorAst->literal_token);
+ if (NumericLiteralAST *minorAst = ast->expression_list->next->next->value->asNumericLiteral())
+ minorLit = translationUnit()->numericLiteral(minorAst->literal_token);
+
+ // build the descriptor
+ ExportedQmlType exportedType;
+ exportedType.typeName = QString::fromUtf8(nameLit->chars(), nameLit->size());
+ if (!packageName.isEmpty() && majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
+ exportedType.packageName = packageName;
+ exportedType.version = LanguageUtils::ComponentVersion(
+ QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt(),
+ QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt());
+ } else {
+ // disable this warning, see above for details
+// translationUnit()->warning(ast->base_expression->firstToken(),
+// "The module will not be available in Qt Creator's QML editors because the uri and version numbers\n"
+// "cannot be determined by static analysis. The type will still be available globally.");
+ exportedType.packageName = QmlJS::CppQmlTypes::defaultPackage;
+ }
+
+ // we want to do lookup later, so also store the surrounding scope
+ unsigned line, column;
+ translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
+ exportedType.scope = _doc->scopeAt(line, column);
+
+ // and the expression
+ const Token begin = translationUnit()->tokenAt(typeId->firstToken());
+ const Token last = translationUnit()->tokenAt(typeId->lastToken() - 1);
+ exportedType.typeExpression = _doc->source().mid(begin.begin(), last.end() - begin.begin());
+
+ _exportedTypes += exportedType;
+
+ return false;
+ }
+
+private:
+ QString stringOf(AST *ast)
+ {
+ const Token begin = translationUnit()->tokenAt(ast->firstToken());
+ const Token last = translationUnit()->tokenAt(ast->lastToken() - 1);
+ return _doc->source().mid(begin.begin(), last.end() - begin.begin());
+ }
+
+ ExpressionAST *skipStringCall(ExpressionAST *exp)
+ {
+ if (!exp)
+ return 0;
+
+ IdExpressionAST *callName = _builder.IdExpression();
+ CallAST *call = _builder.Call(callName);
+ if (!exp->match(call, &_matcher))
+ return exp;
+
+ const QString name = stringOf(callName);
+ if (name != QLatin1String("QLatin1String")
+ && name != QLatin1String("QString"))
+ return exp;
+
+ if (!call->expression_list || call->expression_list->next)
+ return exp;
+
+ return call->expression_list->value;
+ }
+
+ QString nameOfUriAssert(StatementAST *stmt, IdExpressionAST *uriName)
+ {
+ QString null;
+
+ IdExpressionAST *outerCallName = _builder.IdExpression();
+ BinaryExpressionAST *binary = _builder.BinaryExpression();
+ // assert(... == ...);
+ ExpressionStatementAST *pattern = _builder.ExpressionStatement(
+ _builder.Call(outerCallName, _builder.ExpressionList(
+ binary)));
+
+ if (!stmt->match(pattern, &_matcher)) {
+ outerCallName = _builder.IdExpression();
+ binary = _builder.BinaryExpression();
+ // the expansion of Q_ASSERT(...),
+ // ((!(... == ...)) ? qt_assert(...) : ...);
+ pattern = _builder.ExpressionStatement(
+ _builder.NestedExpression(
+ _builder.ConditionalExpression(
+ _builder.NestedExpression(
+ _builder.UnaryExpression(
+ _builder.NestedExpression(
+ binary))),
+ _builder.Call(outerCallName))));
+
+ if (!stmt->match(pattern, &_matcher))
+ return null;
+ }
+
+ const QString outerCall = stringOf(outerCallName);
+ if (outerCall != QLatin1String("qt_assert")
+ && outerCall != QLatin1String("assert")
+ && outerCall != QLatin1String("Q_ASSERT"))
+ return null;
+
+ if (translationUnit()->tokenAt(binary->binary_op_token).kind() != T_EQUAL_EQUAL)
+ return null;
+
+ ExpressionAST *lhsExp = skipStringCall(binary->left_expression);
+ ExpressionAST *rhsExp = skipStringCall(binary->right_expression);
+ if (!lhsExp || !rhsExp)
+ return null;
+
+ StringLiteralAST *uriString = lhsExp->asStringLiteral();
+ IdExpressionAST *uriArgName = lhsExp->asIdExpression();
+ if (!uriString)
+ uriString = rhsExp->asStringLiteral();
+ if (!uriArgName)
+ uriArgName = rhsExp->asIdExpression();
+ if (!uriString || !uriArgName)
+ return null;
+
+ if (stringOf(uriArgName) != stringOf(uriName))
+ return null;
+
+ const StringLiteral *packageLit = translationUnit()->stringLiteral(uriString->literal_token);
+ return QString::fromUtf8(packageLit->chars(), packageLit->size());
+ }
+};
+
+static FullySpecifiedType stripPointerAndReference(const FullySpecifiedType &type)
+{
+ Type *t = type.type();
+ while (t) {
+ if (PointerType *ptr = t->asPointerType())
+ t = ptr->elementType().type();
+ else if (ReferenceType *ref = t->asReferenceType())
+ t = ref->elementType().type();
+ else
+ break;
+ }
+ return FullySpecifiedType(t);
+}
+
+static QString toQmlType(const FullySpecifiedType &type)
+{
+ Overview overview;
+ QString result = overview(stripPointerAndReference(type));
+ if (result == QLatin1String("QString"))
+ result = QLatin1String("string");
+ return result;
+}
+
+static Class *lookupClass(const QString &expression, Scope *scope, TypeOfExpression &typeOf)
+{
+ QList<LookupItem> results = typeOf(expression, scope);
+ Class *klass = 0;
+ foreach (const LookupItem &item, results) {
+ if (item.declaration()) {
+ klass = item.declaration()->asClass();
+ if (klass)
+ return klass;
+ }
+ }
+ return 0;
+}
+
+static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
+ QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *classes,
+ TypeOfExpression &typeOf)
+{
+ using namespace LanguageUtils;
+
+ Overview namePrinter;
+
+ classes->insert(klass, fmo);
+
+ for (unsigned i = 0; i < klass->memberCount(); ++i) {
+ Symbol *member = klass->memberAt(i);
+ if (!member->name())
+ continue;
+ if (Function *func = member->type()->asFunctionType()) {
+ if (!func->isSlot() && !func->isInvokable() && !func->isSignal())
+ continue;
+ FakeMetaMethod method(namePrinter(func->name()), toQmlType(func->returnType()));
+ if (func->isSignal())
+ method.setMethodType(FakeMetaMethod::Signal);
+ else
+ method.setMethodType(FakeMetaMethod::Slot);
+ for (unsigned a = 0; a < func->argumentCount(); ++a) {
+ Symbol *arg = func->argumentAt(a);
+ QString name;
+ if (arg->name())
+ name = namePrinter(arg->name());
+ method.addParameter(name, toQmlType(arg->type()));
+ }
+ fmo->addMethod(method);
+ }
+ if (QtPropertyDeclaration *propDecl = member->asQtPropertyDeclaration()) {
+ const FullySpecifiedType &type = propDecl->type();
+ const bool isList = false; // ### fixme
+ const bool isWritable = propDecl->flags() & QtPropertyDeclaration::WriteFunction;
+ const bool isPointer = type.type() && type.type()->isPointerType();
+ const int revision = 0; // ### fixme
+ FakeMetaProperty property(
+ namePrinter(propDecl->name()),
+ toQmlType(type),
+ isList, isWritable, isPointer,
+ revision);
+ fmo->addProperty(property);
+ }
+ if (QtEnum *qtEnum = member->asQtEnum()) {
+ // find the matching enum
+ Enum *e = 0;
+ QList<LookupItem> result = typeOf(namePrinter(qtEnum->name()), klass);
+ foreach (const LookupItem &item, result) {
+ if (item.declaration()) {
+ e = item.declaration()->asEnum();
+ if (e)
+ break;
+ }
+ }
+ if (!e)
+ continue;
+
+ FakeMetaEnum metaEnum(namePrinter(e->name()));
+ for (unsigned j = 0; j < e->memberCount(); ++j) {
+ Symbol *enumMember = e->memberAt(j);
+ if (!enumMember->name())
+ continue;
+ metaEnum.addKey(namePrinter(enumMember->name()), 0);
+ }
+ fmo->addEnum(metaEnum);
+ }
+ }
+
+ // only single inheritance is supported
+ if (klass->baseClassCount() > 0) {
+ BaseClass *base = klass->baseClassAt(0);
+ if (!base->name())
+ return;
+
+ const QString baseClassName = namePrinter(base->name());
+ fmo->setSuperclassName(baseClassName);
+
+ Class *baseClass = lookupClass(baseClassName, klass, typeOf);
+ if (!baseClass)
+ return;
+
+ FakeMetaObject::Ptr baseFmo = classes->value(baseClass);
+ if (!baseFmo) {
+ baseFmo = FakeMetaObject::Ptr(new FakeMetaObject);
+ populate(baseFmo, baseClass, classes, typeOf);
+ }
+ }
+}
+
+QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(
+ const Document::Ptr &doc,
+ const Snapshot &snapshot,
+ const QList<ExportedQmlType> &exportedTypes)
+{
+ using namespace LanguageUtils;
+ QList<FakeMetaObject::ConstPtr> exportedObjects;
+ QHash<Class *, FakeMetaObject::Ptr> classes;
+
+ if (exportedTypes.isEmpty())
+ return exportedObjects;
+
+ TypeOfExpression typeOf;
+ typeOf.init(doc, snapshot);
+ foreach (const ExportedQmlType &exportedType, exportedTypes) {
+ FakeMetaObject::Ptr fmo(new FakeMetaObject);
+ fmo->addExport(exportedType.typeName,
+ exportedType.packageName,
+ exportedType.version);
+ exportedObjects += fmo;
+
+ Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
+ if (!klass)
+ continue;
+
+ // add the no-package export, so the cpp name can be used in properties
+ Overview overview;
+ fmo->addExport(overview(klass->name()), QmlJS::CppQmlTypes::cppPackage, ComponentVersion());
+
+ populate(fmo, klass, &classes, typeOf);
+ }
+
+ return exportedObjects;
+}
+
+} // anonymous namespace
+
+FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot)
+ : m_snapshot(snapshot)
+{
+}
+
+QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
+{
+ QList<LanguageUtils::FakeMetaObject::ConstPtr> noResults;
+ if (document->source().isEmpty()
+ || !document->translationUnit()->ast())
+ return noResults;
+
+ FindExportsVisitor finder(document);
+ QList<ExportedQmlType> exports = finder();
+ if (exports.isEmpty())
+ return noResults;
+
+ return exportedQmlObjects(document, m_snapshot, exports);
+}
+
+bool FindExportedCppTypes::maybeExportsTypes(const Document::Ptr &document)
+{
+ if (!document->control())
+ return false;
+ const QByteArray qmlRegisterTypeToken("qmlRegisterType");
+ if (document->control()->findIdentifier(
+ qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
+ return true;
+ }
+ return false;
+}
diff --git a/src/plugins/qmljstools/qmljsfindexportedcpptypes.h b/src/plugins/qmljstools/qmljsfindexportedcpptypes.h
new file mode 100644
index 0000000000..304894e943
--- /dev/null
+++ b/src/plugins/qmljstools/qmljsfindexportedcpptypes.h
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
+#define QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
+
+#include <cplusplus/CppDocument.h>
+#include <languageutils/fakemetaobject.h>
+
+namespace QmlJSTools {
+
+class FindExportedCppTypes
+{
+public:
+ FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
+ QList<LanguageUtils::FakeMetaObject::ConstPtr> operator()(const CPlusPlus::Document::Ptr &document);
+
+ static bool maybeExportsTypes(const CPlusPlus::Document::Ptr &document);
+
+private:
+ CPlusPlus::Snapshot m_snapshot;
+};
+
+} // namespace QmlJSTools
+
+#endif // QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp
index 6ff2c89550..545761ad7e 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp
@@ -33,6 +33,7 @@
#include "qmljsmodelmanager.h"
#include "qmljstoolsconstants.h"
#include "qmljsplugindumper.h"
+#include "qmljsfindexportedcpptypes.h"
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -41,8 +42,6 @@
#include <coreplugin/messagemanager.h>
#include <cplusplus/ModelManagerInterface.h>
#include <cplusplus/CppDocument.h>
-#include <cplusplus/TypeOfExpression.h>
-#include <cplusplus/Overview.h>
#include <qmljs/qmljscontext.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/parser/qmldirparser_p.h>
@@ -98,8 +97,10 @@ void ModelManager::delayedInitialization()
CPlusPlus::CppModelManagerInterface *cppModelManager =
CPlusPlus::CppModelManagerInterface::instance();
if (cppModelManager) {
+ // It's important to have a direct connection here so we can prevent
+ // the source and AST of the cpp document being cleaned away.
connect(cppModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
- this, SLOT(queueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)));
+ this, SLOT(maybeQueueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)), Qt::DirectConnection);
}
}
@@ -673,9 +674,25 @@ void ModelManager::loadPluginTypes(const QString &libraryPath, const QString &im
m_pluginDumper->loadPluginTypes(libraryPath, importPath, importUri, importVersion);
}
-void ModelManager::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc)
+// is called *inside a c++ parsing thread*, to allow hanging on to source and ast
+void ModelManager::maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc)
{
- m_queuedCppDocuments.insert(doc->fileName());
+ // keep source and AST alive if we want to scan for register calls
+ const bool scan = FindExportedCppTypes::maybeExportsTypes(doc);
+ if (scan)
+ doc->keepSourceAndAST();
+
+ // delegate actual queuing to the gui thread
+ QMetaObject::invokeMethod(this, "queueCppQmlTypeUpdate",
+ Q_ARG(CPlusPlus::Document::Ptr, doc), Q_ARG(bool, scan));
+}
+
+void ModelManager::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan)
+{
+ QPair<CPlusPlus::Document::Ptr, bool> prev = m_queuedCppDocuments.value(doc->fileName());
+ if (prev.first && prev.second)
+ prev.first->releaseSourceAndAST();
+ m_queuedCppDocuments.insert(doc->fileName(), qMakePair(doc, scan));
m_updateCppQmlTypesTimer->start();
}
@@ -691,20 +708,32 @@ void ModelManager::startCppQmlTypeUpdate()
m_queuedCppDocuments.clear();
}
-void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files)
+void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager,
+ CPlusPlus::CppModelManagerInterface *cppModelManager,
+ QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents)
{
CppQmlTypeHash newCppTypes = qmlModelManager->cppQmlTypes();
CPlusPlus::Snapshot snapshot = cppModelManager->snapshot();
+ FindExportedCppTypes finder(snapshot);
+
+ typedef QPair<CPlusPlus::Document::Ptr, bool> DocScanPair;
+ foreach (const DocScanPair &pair, documents) {
+ CPlusPlus::Document::Ptr doc = pair.first;
+ const bool scan = pair.second;
+ const QString fileName = doc->fileName();
+ if (!scan) {
+ newCppTypes.remove(fileName);
+ continue;
+ }
+
+ QList<LanguageUtils::FakeMetaObject::ConstPtr> exported = finder(doc);
- foreach (const QString &fileName, files) {
- CPlusPlus::Document::Ptr doc = snapshot.document(fileName);
- QList<LanguageUtils::FakeMetaObject::ConstPtr> exported;
- if (doc)
- exported = cppModelManager->exportedQmlObjects(doc);
if (!exported.isEmpty())
newCppTypes[fileName] = exported;
else
newCppTypes.remove(fileName);
+
+ doc->releaseSourceAndAST();
}
QMutexLocker locker(&qmlModelManager->m_cppTypesMutex);
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.h b/src/plugins/qmljstools/qmljsmodelmanager.h
index 7fe82145b3..0a8c26fdbd 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.h
+++ b/src/plugins/qmljstools/qmljsmodelmanager.h
@@ -117,12 +117,15 @@ protected:
void updateImportPaths();
private slots:
- void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
+ void maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
+ void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan);
void startCppQmlTypeUpdate();
private:
static bool matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType);
- static void updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files);
+ static void updateCppQmlTypes(ModelManager *qmlModelManager,
+ CPlusPlus::CppModelManagerInterface *cppModelManager,
+ QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents);
mutable QMutex m_mutex;
Core::ICore *m_core;
@@ -134,7 +137,7 @@ private:
QFutureSynchronizer<void> m_synchronizer;
QTimer *m_updateCppQmlTypesTimer;
- QSet<QString> m_queuedCppDocuments;
+ QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments;
CppQmlTypeHash m_cppTypes;
mutable QMutex m_cppTypesMutex;
diff --git a/src/plugins/qmljstools/qmljstools-lib.pri b/src/plugins/qmljstools/qmljstools-lib.pri
index 2d5cad5b23..596f28816e 100644
--- a/src/plugins/qmljstools/qmljstools-lib.pri
+++ b/src/plugins/qmljstools/qmljstools-lib.pri
@@ -17,7 +17,8 @@ HEADERS += \
$$PWD/qmljsfunctionfilter.h \
$$PWD/qmljslocatordata.h \
$$PWD/qmljsindenter.h \
- $$PWD/qmljscodestylesettingspage.h
+ $$PWD/qmljscodestylesettingspage.h \
+ $$PWD/qmljsfindexportedcpptypes.h
SOURCES += \
$$PWD/qmljstoolsplugin.cpp \
@@ -30,7 +31,8 @@ SOURCES += \
$$PWD/qmljsfunctionfilter.cpp \
$$PWD/qmljslocatordata.cpp \
$$PWD/qmljsindenter.cpp \
- $$PWD/qmljscodestylesettingspage.cpp
+ $$PWD/qmljscodestylesettingspage.cpp \
+ $$PWD/qmljsfindexportedcpptypes.cpp
FORMS += \
$$PWD/qmljscodestylesettingspage.ui