diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-10-14 18:46:38 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-10-14 19:02:37 +0200 |
commit | c2f8b9535d34da6948ccf45b7d5fd90de2f1bc9e (patch) | |
tree | c6f7e058a985d7c18b51cadc76283caf555071c9 /tools | |
parent | 9e633bbda7608ac0231809e2a6a97ae8f2d849d6 (diff) | |
parent | 803f18f02e5609a1ca00a5b78ea6d3613d44e1a0 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/cmake
Removed dependencies.yaml because we don't use it yet in wip/cmake.
Fixed conflict in qmlcachegen.cpp.
Change-Id: Ie1060c737bee1daa85779903598e5b6d5020d922
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qml/conf.h | 3 | ||||
-rw-r--r-- | tools/qml/main.cpp | 17 | ||||
-rw-r--r-- | tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in | 10 | ||||
-rw-r--r-- | tools/qmlcachegen/generateloader.cpp | 233 | ||||
-rw-r--r-- | tools/qmlcachegen/qmlcachegen.cpp | 16 | ||||
-rw-r--r-- | tools/qmlcachegen/qtquickcompiler.prf | 45 | ||||
-rw-r--r-- | tools/qmlcachegen/resourcefilter.cpp | 51 | ||||
-rw-r--r-- | tools/qmllint/fakemetaobject.cpp | 14 | ||||
-rw-r--r-- | tools/qmllint/fakemetaobject.h | 3 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.cpp | 205 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.h | 7 | ||||
-rw-r--r-- | tools/qmllint/main.cpp | 9 | ||||
-rw-r--r-- | tools/qmllint/qcoloroutput.cpp | 11 | ||||
-rw-r--r-- | tools/qmllint/qcoloroutput_p.h | 2 | ||||
-rw-r--r-- | tools/qmllint/qmljstypedescriptionreader.cpp | 67 | ||||
-rw-r--r-- | tools/qmllint/scopetree.cpp | 44 | ||||
-rw-r--r-- | tools/qmllint/scopetree.h | 5 | ||||
-rw-r--r-- | tools/qmlplugindump/main.cpp | 74 | ||||
-rw-r--r-- | tools/qmlplugindump/qmlstreamwriter.cpp | 16 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerapplication.cpp | 4 | ||||
-rw-r--r-- | tools/qmlprofiler/qmlprofilerdata.cpp | 1 | ||||
-rw-r--r-- | tools/qmltime/qmltime.cpp | 2 |
22 files changed, 349 insertions, 490 deletions
diff --git a/tools/qml/conf.h b/tools/qml/conf.h index 0c9d8613a0..e83d63cba5 100644 --- a/tools/qml/conf.h +++ b/tools/qml/conf.h @@ -30,6 +30,7 @@ #include <QtQml/QQmlContext> #include <QtQml/QQmlListProperty> +#include <QtQml/qqml.h> #include <QObject> #include <QUrl> @@ -38,6 +39,7 @@ class PartialScene : public QObject Q_OBJECT Q_PROPERTY(QUrl container READ container WRITE setContainer NOTIFY containerChanged) Q_PROPERTY(QString itemType READ itemType WRITE setItemType NOTIFY itemTypeChanged) + QML_ELEMENT public: PartialScene(QObject *parent = 0) : QObject(parent) {} @@ -72,6 +74,7 @@ class Config : public QObject Q_OBJECT Q_PROPERTY(QQmlListProperty<PartialScene> sceneCompleters READ sceneCompleters) Q_CLASSINFO("DefaultProperty", "sceneCompleters") + QML_NAMED_ELEMENT(Configuration) public: Config (QObject* parent=0) : QObject(parent) {} diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp index 9edc90e050..f7d7b98277 100644 --- a/tools/qml/main.cpp +++ b/tools/qml/main.cpp @@ -60,6 +60,7 @@ #include <qqml.h> #include <qqmldebug.h> +#include <private/qmemory_p.h> #include <private/qtqmlglobal_p.h> #if QT_CONFIG(qml_animation) #include <private/qabstractanimation_p.h> @@ -68,6 +69,7 @@ #include <cstdio> #include <cstring> #include <cstdlib> +#include <memory> #define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms @@ -398,23 +400,23 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory) int main(int argc, char *argv[]) { getAppFlags(argc, argv); - QCoreApplication *app = nullptr; + std::unique_ptr<QCoreApplication> app; switch (applicationType) { #ifdef QT_GUI_LIB case QmlApplicationTypeGui: - app = new LoaderApplication(argc, argv); + app = qt_make_unique<LoaderApplication>(argc, argv); break; #ifdef QT_WIDGETS_LIB case QmlApplicationTypeWidget: - app = new QApplication(argc, argv); - static_cast<QApplication *>(app)->setWindowIcon(QIcon(iconResourcePath)); + app = qt_make_unique<QApplication>(argc, argv); + static_cast<QApplication *>(app.get())->setWindowIcon(QIcon(iconResourcePath)); break; #endif // QT_WIDGETS_LIB #endif // QT_GUI_LIB case QmlApplicationTypeCore: Q_FALLTHROUGH(); default: // QmlApplicationTypeUnknown: not allowed, but we'll exit after checking apptypeOption below - app = new QCoreApplication(argc, argv); + app = qt_make_unique<QCoreApplication>(argc, argv); break; } @@ -423,8 +425,7 @@ int main(int argc, char *argv[]) app->setOrganizationDomain("qt-project.org"); QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); - qmlRegisterType<Config>("QmlRuntime.Config", 1, 0, "Configuration"); - qmlRegisterType<PartialScene>("QmlRuntime.Config", 1, 0, "PartialScene"); + qmlRegisterTypesAndRevisions<Config, PartialScene>("QmlRuntime.Config", 1); QQmlApplicationEngine e; QStringList files; QString confFile; @@ -598,7 +599,7 @@ int main(int argc, char *argv[]) if (files.count() <= 0) { #if defined(Q_OS_DARWIN) if (applicationType == QmlApplicationTypeGui) - exitTimerId = static_cast<LoaderApplication *>(app)->startTimer(FILE_OPEN_EVENT_WAIT_TIME); + exitTimerId = static_cast<LoaderApplication *>(app.get())->startTimer(FILE_OPEN_EVENT_WAIT_TIME); else #endif noFilesGiven(); diff --git a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in index 75fbb0fcf3..be2113b258 100644 --- a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in +++ b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in @@ -50,13 +50,9 @@ but not all the files it references. get_filename_component(input_resource ${_resource} ABSOLUTE) - execute_process(COMMAND ${compiler_path} -filter-resource-file ${input_resource} -o ${new_resource_file} OUTPUT_VARIABLE remaining_files) - if(remaining_files) - list(APPEND filtered_rcc_files ${new_resource_file}) - list(APPEND loader_flags \"--resource-file-mapping=${_resource}=${new_resource_file}\") - else() - list(APPEND loader_flags \"--resource-file-mapping=${_resource}\") - endif() + execute_process(COMMAND ${compiler_path} --filter-resource-file ${input_resource} -o ${new_resource_file} OUTPUT_VARIABLE remaining_files) + list(APPEND filtered_rcc_files ${new_resource_file}) + list(APPEND loader_flags \"--resource-file-mapping=${_resource}=${new_resource_file}\") set(rcc_file_with_compilation_units) diff --git a/tools/qmlcachegen/generateloader.cpp b/tools/qmlcachegen/generateloader.cpp index 1c8a5a016a..71286137eb 100644 --- a/tools/qmlcachegen/generateloader.cpp +++ b/tools/qmlcachegen/generateloader.cpp @@ -100,228 +100,6 @@ QString symbolNamespaceForPath(const QString &relativePath) return mangledIdentifier(symbol); } -struct VirtualDirectoryEntry -{ - QString name; - QVector<VirtualDirectoryEntry*> dirEntries; - int firstChildIndex = -1; // node index inside generated data - bool isDirectory = true; - - VirtualDirectoryEntry() - {} - - ~VirtualDirectoryEntry() - { - qDeleteAll(dirEntries); - } - - VirtualDirectoryEntry *append(const QString &name) - { - for (QVector<VirtualDirectoryEntry*>::Iterator it = dirEntries.begin(), end = dirEntries.end(); - it != end; ++it) { - if ((*it)->name == name) - return *it; - } - - VirtualDirectoryEntry *subEntry = new VirtualDirectoryEntry; - subEntry->name = name; - dirEntries.append(subEntry); - return subEntry; - } - - void appendEmptyFile(const QString &name) - { - VirtualDirectoryEntry *subEntry = new VirtualDirectoryEntry; - subEntry->name = name; - subEntry->isDirectory = false; - dirEntries.append(subEntry); - } - - bool isEmpty() const { return dirEntries.isEmpty(); } -}; - -struct DataStream -{ - DataStream(QVector<unsigned char > *data = nullptr) - : data(data) - {} - - qint64 currentOffset() const { return data->size(); } - - DataStream &operator<<(quint16 value) - { - unsigned char d[2]; - qToBigEndian(value, d); - data->append(d[0]); - data->append(d[1]); - return *this; - } - DataStream &operator<<(quint32 value) - { - unsigned char d[4]; - qToBigEndian(value, d); - data->append(d[0]); - data->append(d[1]); - data->append(d[2]); - data->append(d[3]); - return *this; - } -private: - QVector<unsigned char> *data; -}; - -static bool resource_sort_order(const VirtualDirectoryEntry *lhs, const VirtualDirectoryEntry *rhs) -{ - return qt_hash(lhs->name) < qt_hash(rhs->name); -} - -struct ResourceTree -{ - ResourceTree() - {} - - void serialize(VirtualDirectoryEntry &root, QVector<unsigned char> *treeData, QVector<unsigned char> *stringData) - { - treeStream = DataStream(treeData); - stringStream = DataStream(stringData); - - QStack<VirtualDirectoryEntry *> directories; - - { - directories.push(&root); - while (!directories.isEmpty()) { - VirtualDirectoryEntry *entry = directories.pop(); - registerString(entry->name); - if (entry->isDirectory) - directories << entry->dirEntries; - } - } - - { - quint32 currentDirectoryIndex = 1; - directories.push(&root); - while (!directories.isEmpty()) { - VirtualDirectoryEntry *entry = directories.pop(); - entry->firstChildIndex = currentDirectoryIndex; - currentDirectoryIndex += entry->dirEntries.count(); - std::sort(entry->dirEntries.begin(), entry->dirEntries.end(), resource_sort_order); - - for (QVector<VirtualDirectoryEntry*>::ConstIterator child = entry->dirEntries.constBegin(), end = entry->dirEntries.constEnd(); - child != end; ++child) { - if ((*child)->isDirectory) - directories << *child; - } - } - } - - { - writeTreeEntry(&root); - directories.push(&root); - while (!directories.isEmpty()) { - VirtualDirectoryEntry *entry = directories.pop(); - - for (QVector<VirtualDirectoryEntry*>::ConstIterator child = entry->dirEntries.constBegin(), end = entry->dirEntries.constEnd(); - child != end; ++child) { - writeTreeEntry(*child); - if ((*child)->isDirectory) - directories << (*child); - } - } - } - } - -private: - DataStream treeStream; - DataStream stringStream; - QHash<QString, qint64> stringOffsets; - - void registerString(const QString &name) - { - if (stringOffsets.contains(name)) - return; - const qint64 offset = stringStream.currentOffset(); - stringOffsets.insert(name, offset); - - stringStream << quint16(name.length()) - << quint32(qt_hash(name)); - for (int i = 0; i < name.length(); ++i) - stringStream << quint16(name.at(i).unicode()); - } - - void writeTreeEntry(VirtualDirectoryEntry *entry) - { - treeStream << quint32(stringOffsets.value(entry->name)) - << quint16(entry->isDirectory ? 0x2 : 0x0); // Flags: File or Directory - - if (entry->isDirectory) { - treeStream << quint32(entry->dirEntries.count()) - << quint32(entry->firstChildIndex); - } else { - treeStream << quint16(QLocale::AnyCountry) << quint16(QLocale::C) - << quint32(0x0); - } - } -}; - -static QByteArray generateResourceDirectoryTree(QTextStream &code, const QStringList &qrcFiles, - const QStringList &sortedRetainedFiles) -{ - QByteArray call; - if (qrcFiles.isEmpty()) - return call; - - VirtualDirectoryEntry resourceDirs; - resourceDirs.name = QStringLiteral("/"); - - for (const QString &entry : qrcFiles) { - const QStringList segments = entry.split(QLatin1Char('/'), QString::SkipEmptyParts); - - VirtualDirectoryEntry *dirEntry = &resourceDirs; - - for (int i = 0; i < segments.count() - 1; ++i) - dirEntry = dirEntry->append(segments.at(i)); - if (!std::binary_search(sortedRetainedFiles.begin(), sortedRetainedFiles.end(), entry)) - dirEntry->appendEmptyFile(segments.last()); - } - - if (resourceDirs.isEmpty()) - return call; - - QVector<unsigned char> names; - QVector<unsigned char> tree; - ResourceTree().serialize(resourceDirs, &tree, &names); - - code << "static const unsigned char qt_resource_tree[] = {\n"; - for (int i = 0; i < tree.count(); ++i) { - code << uint(tree.at(i)); - if (i < tree.count() - 1) - code << ','; - if (i % 16 == 0) - code << '\n'; - } - code << "};\n"; - - code << "static const unsigned char qt_resource_names[] = {\n"; - for (int i = 0; i < names.count(); ++i) { - code << uint(names.at(i)); - if (i < names.count() - 1) - code << ','; - if (i % 16 == 0) - code << '\n'; - } - code << "};\n"; - - code << "static const unsigned char qt_resource_empty_payout[] = { 0, 0, 0, 0, 0 };\n"; - - code << "QT_BEGIN_NAMESPACE\n"; - code << "extern Q_CORE_EXPORT bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);\n"; - code << "QT_END_NAMESPACE\n"; - - call = "QT_PREPEND_NAMESPACE(qRegisterResourceData)(/*version*/0x01, qt_resource_tree, qt_resource_names, qt_resource_empty_payout);\n"; - - return call; -} - static QString qtResourceNameForFile(const QString &fileName) { QFileInfo fi(fileName); @@ -332,9 +110,8 @@ static QString qtResourceNameForFile(const QString &fileName) return name; } -bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedRetainedFiles, - const QString &outputFileName, const QStringList &resourceFileMappings, - QString *errorString) +bool generateLoader(const QStringList &compiledFiles, const QString &outputFileName, + const QStringList &resourceFileMappings, QString *errorString) { QByteArray generatedLoaderCode; @@ -345,9 +122,6 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR stream << "#include <QtCore/qurl.h>\n"; stream << "\n"; - QByteArray resourceRegisterCall = generateResourceDirectoryTree(stream, compiledFiles, - sortedRetainedFiles); - stream << "namespace QmlCacheGeneratedCode {\n"; for (int i = 0; i < compiledFiles.count(); ++i) { const QString compiledFile = compiledFiles.at(i); @@ -385,9 +159,6 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR stream << " registration.lookupCachedQmlUnit = &lookupCachedUnit;\n"; stream << " QQmlPrivate::qmlregister(QQmlPrivate::QmlUnitCacheHookRegistration, ®istration);\n"; - if (!resourceRegisterCall.isEmpty()) - stream << resourceRegisterCall; - stream << "}\n\n"; stream << "Registry::~Registry() {\n"; stream << " QQmlPrivate::qmlunregister(QQmlPrivate::QmlUnitCacheHookRegistration, quintptr(&lookupCachedUnit));\n"; diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 6e96b88c0c..d1b28c41f5 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -48,9 +48,8 @@ using namespace QQmlJS; int filterResourceFile(const QString &input, const QString &output); -bool generateLoader(const QStringList &compiledFiles, const QStringList &retainedFiles, - const QString &output, const QStringList &resourceFileMappings, - QString *errorString); +bool generateLoader(const QStringList &compiledFiles, const QString &output, + const QStringList &resourceFileMappings, QString *errorString); QString symbolNamespaceForPath(const QString &relativePath); QSet<QString> illegalNames; @@ -455,8 +454,6 @@ int main(int argc, char **argv) parser.addOption(resourceFileMappingOption); QCommandLineOption resourceOption(QStringLiteral("resource"), QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name")); parser.addOption(resourceOption); - QCommandLineOption retainOption(QStringLiteral("retain"), QCoreApplication::translate("main", "Qt resource file the contents of which should not be replaced by empty stubs"), QCoreApplication::translate("main", "resource-file-name")); - parser.addOption(retainOption); QCommandLineOption resourcePathOption(QStringLiteral("resource-path"), QCoreApplication::translate("main", "Qt resource file path corresponding to the file being compiled"), QCoreApplication::translate("main", "resource-path")); parser.addOption(resourcePathOption); QCommandLineOption resourceNameOption(QStringLiteral("resource-name"), @@ -517,12 +514,9 @@ int main(int argc, char **argv) if (target == GenerateLoader) { ResourceFileMapper mapper(sources); - ResourceFileMapper retain(parser.values(retainOption)); Error error; - QStringList retainedFiles = retain.qmlCompilerFiles(); - std::sort(retainedFiles.begin(), retainedFiles.end()); - if (!generateLoader(mapper.qmlCompilerFiles(), retainedFiles, outputFileName, + if (!generateLoader(mapper.qmlCompilerFiles(), outputFileName, parser.values(resourceFileMappingOption), &error.message)) { error.augment(QLatin1String("Error generating loader stub: ")).print(); return EXIT_FAILURE; @@ -531,10 +525,8 @@ int main(int argc, char **argv) } if (target == GenerateLoaderStandAlone) { - QStringList retainedFiles = parser.values(retainOption); - retainedFiles.sort(); Error error; - if (!generateLoader(sources, retainedFiles, outputFileName, + if (!generateLoader(sources, outputFileName, parser.values(resourceNameOption), &error.message)) { error.augment(QLatin1String("Error generating loader stub: ")).print(); return EXIT_FAILURE; diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf index 2f98aadefe..a31a7f5714 100644 --- a/tools/qmlcachegen/qtquickcompiler.prf +++ b/tools/qmlcachegen/qtquickcompiler.prf @@ -1,5 +1,15 @@ if(qtc_run|lupdate_run): return() +!contains(QT, qml) { + qt_modules = \ + $$replace(QT, -private$, _private) \ + $$replace(QT_PRIVATE, -private$, _private) + qt_modules = $$resolve_depends(qt_modules, "QT.", ".depends" ".run_depends") + !contains(qt_modules, qml): \ + return() + unset(qt_modules) +} + qtPrepareTool(QML_CACHEGEN, qmlcachegen, _FILTER) qtPrepareTool(QMAKE_RCC, rcc, _DEP) @@ -16,20 +26,6 @@ defineReplace(qmlCacheResourceFileOutputName) { return($${name}) } -defineTest(qtQuickRetainSources) { - for(retainedRes, QTQUICK_COMPILER_RETAINED_RESOURCES) { - equals(1, $$retainedRes): return(true) - } - return(false) -} - -defineTest(qtQuickSkippedResourceFile) { - for(skippedRes, QTQUICK_COMPILER_SKIPPED_RESOURCES) { - equals(1, $$skippedRes): return(true) - } - return(false) -} - # Flatten RESOURCES that may contain individual files or objects load(resources) @@ -37,29 +33,14 @@ NEWRESOURCES = QMLCACHE_RESOURCE_FILES = for(res, RESOURCES) { - qtQuickSkippedResourceFile($$res) { - NEWRESOURCES += $$res - next() - } - absRes = $$absolute_path($$res, $$_PRO_FILE_PWD_) rccContents = $$system($$QMAKE_RCC_DEP -list $$system_quote($$absRes),lines) contains(rccContents,.*\\.js$)|contains(rccContents,.*\\.qml$)|contains(rccContents,.*\\.mjs$) { new_resource = $$qmlCacheResourceFileOutputName($$res) mkpath($$dirname(new_resource)) - qtQuickRetainSources($$res) { - NEWRESOURCES += $$res - QMLCACHE_LOADER_FLAGS += --retain=$$shell_quote($$absRes) - } else { - remaining_files = $$system($$QML_CACHEGEN_FILTER -filter-resource-file \ - -o $$system_quote($$new_resource) $$system_quote($$absRes),lines) - !isEmpty(remaining_files) { - NEWRESOURCES += $$new_resource - QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes=$$new_resource) - } else { - QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes) - } - } + dummy = $$system($$QML_CACHEGEN_FILTER --filter-resource-file -o $$system_quote($$new_resource) $$system_quote($$absRes)) + NEWRESOURCES += $$new_resource + QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes=$$new_resource) QMLCACHE_RESOURCE_FILES += $$absRes diff --git a/tools/qmlcachegen/resourcefilter.cpp b/tools/qmlcachegen/resourcefilter.cpp index 3ad6e9ca0d..261102dcbe 100644 --- a/tools/qmlcachegen/resourcefilter.cpp +++ b/tools/qmlcachegen/resourcefilter.cpp @@ -57,8 +57,6 @@ int filterResourceFile(const QString &input, const QString &output) QXmlStreamWriter writer(&outputString); writer.setAutoFormatting(true); - QStringList remainingFiles; - QXmlStreamReader reader(&file); while (!reader.atEnd()) { switch (reader.readNext()) { @@ -139,47 +137,36 @@ int filterResourceFile(const QString &input, const QString &output) if (currentFileName.isEmpty()) continue; - if (!currentFileName.endsWith(QStringLiteral(".qml")) - && !currentFileName.endsWith(QStringLiteral(".js")) - && !currentFileName.endsWith(QStringLiteral(".mjs"))) { - writer.writeStartElement(QStringLiteral("file")); - - if (!fileAttributes.hasAttribute(QStringLiteral("alias"))) - fileAttributes.append(QStringLiteral("alias"), currentFileName); + writer.writeStartElement(QStringLiteral("file")); - currentFileName = inputDirectory.absoluteFilePath(currentFileName); - currentFileName = outputDirectory.relativeFilePath(currentFileName); + if (!fileAttributes.hasAttribute(QStringLiteral("alias"))) + fileAttributes.append(QStringLiteral("alias"), currentFileName); - remainingFiles << currentFileName; + currentFileName = inputDirectory.absoluteFilePath(currentFileName); + currentFileName = outputDirectory.relativeFilePath(currentFileName); - writer.writeAttributes(fileAttributes); - writer.writeCharacters(currentFileName); - writer.writeEndElement(); - } + writer.writeAttributes(fileAttributes); + writer.writeCharacters(currentFileName); + writer.writeEndElement(); continue; default: break; } } - if (!remainingFiles.isEmpty()) { - QFile outputFile(output); - if (!outputFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - fprintf(stderr, "Cannot open %s for writing.\n", qPrintable(output)); - return EXIT_FAILURE; - } - const QByteArray outputStringUtf8 = outputString.toUtf8(); - if (outputFile.write(outputStringUtf8) != outputStringUtf8.size()) - return EXIT_FAILURE; + QFile outputFile(output); + if (!outputFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + fprintf(stderr, "Cannot open %s for writing.\n", qPrintable(output)); + return EXIT_FAILURE; + } + const QByteArray outputStringUtf8 = outputString.toUtf8(); + if (outputFile.write(outputStringUtf8) != outputStringUtf8.size()) + return EXIT_FAILURE; - outputFile.close(); - if (outputFile.error() != QFileDevice::NoError) - return EXIT_FAILURE; + outputFile.close(); + if (outputFile.error() != QFileDevice::NoError) + return EXIT_FAILURE; - // The build system expects this output if we wrote a qrc file and no output - // if no files remain. - fprintf(stdout, "New resource file written with %d files.\n", remainingFiles.count()); - } return EXIT_SUCCESS; } diff --git a/tools/qmllint/fakemetaobject.cpp b/tools/qmllint/fakemetaobject.cpp index 514bb2fe42..8319ae6713 100644 --- a/tools/qmllint/fakemetaobject.cpp +++ b/tools/qmllint/fakemetaobject.cpp @@ -46,8 +46,8 @@ QString FakeMetaEnum::name() const void FakeMetaEnum::setName(const QString &name) { m_name = name; } -void FakeMetaEnum::addKey(const QString &key, int value) -{ m_keys.append(key); m_values.append(value); } +void FakeMetaEnum::addKey(const QString &key) +{ m_keys.append(key); } QString FakeMetaEnum::key(int index) const { return m_keys.at(index); } @@ -73,10 +73,6 @@ void FakeMetaEnum::addToHash(QCryptographicHash &hash) const hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar)); } - len = m_values.size(); - hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); - foreach (int value, m_values) - hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); } QString FakeMetaEnum::describe(int baseIndent) const @@ -84,16 +80,14 @@ QString FakeMetaEnum::describe(int baseIndent) const QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent); QString res = QLatin1String("Enum "); res += name(); - res += QLatin1String(":{"); + res += QLatin1String(": ["); for (int i = 0; i < keyCount(); ++i) { res += newLine; res += QLatin1String(" "); res += key(i); - res += QLatin1String(": "); - res += QString::number(m_values.value(i, -1)); } res += newLine; - res += QLatin1Char('}'); + res += QLatin1Char(']'); return res; } diff --git a/tools/qmllint/fakemetaobject.h b/tools/qmllint/fakemetaobject.h index 4e0ea1f8b3..ae76596343 100644 --- a/tools/qmllint/fakemetaobject.h +++ b/tools/qmllint/fakemetaobject.h @@ -46,7 +46,6 @@ namespace LanguageUtils { class FakeMetaEnum { QString m_name; QStringList m_keys; - QList<int> m_values; public: FakeMetaEnum(); @@ -57,7 +56,7 @@ public: QString name() const; void setName(const QString &name); - void addKey(const QString &key, int value); + void addKey(const QString &key); QString key(int index) const; int keyCount() const; QStringList keys() const; diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index 27939608d7..ee2a0c38c1 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -39,10 +39,18 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qv4codegen_p.h> +#include <private/qqmldirparser_p.h> -QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc); +static QQmlDirParser createQmldirParserForFile(const QString &filename) +{ + QFile f(filename); + f.open(QFile::ReadOnly); + QQmlDirParser parser; + parser.parse(f.readAll()); + return parser; +} -static QQmlJS::TypeDescriptionReader createReaderForFile(QString const &filename) +static QQmlJS::TypeDescriptionReader createQmltypesReaderForFile(QString const &filename) { QFile f(filename); f.open(QFile::ReadOnly); @@ -60,13 +68,12 @@ void FindUnqualifiedIDVisitor::leaveEnvironment() m_currentScope = m_currentScope->parentScope(); } -enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned }; +enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned, BasePath }; -QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin) +QStringList completeImportPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin) { static const QLatin1Char Slash('/'); static const QLatin1Char Backslash('\\'); - static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes"); const QVector<QStringRef> parts = uri.splitRef(QLatin1Char('.'), QString::SkipEmptyParts); @@ -96,7 +103,7 @@ QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePat return str; }; - for (int version = FullyVersioned; version <= Unversioned; ++version) { + for (int version = FullyVersioned; version <= BasePath; ++version) { const QString ver = versionString(vmaj, vmin, static_cast<ImportVersion>(version)); for (const QString &path : basePaths) { @@ -104,20 +111,23 @@ QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePat if (!dir.endsWith(Slash) && !dir.endsWith(Backslash)) dir += Slash; - // append to the end - qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver + SlashPluginsDotQmltypes; + if (version == BasePath) { + qmlDirPathsPaths += dir; + } else { + // append to the end + qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver; + } - if (version != Unversioned) { + if (version < Unversioned) { // insert in the middle for (int index = parts.count() - 2; index >= 0; --index) { qmlDirPathsPaths += dir + joinStringRefs(parts.mid(0, index + 1), Slash) + ver + Slash - + joinStringRefs(parts.mid(index + 1), Slash) + SlashPluginsDotQmltypes; + + joinStringRefs(parts.mid(index + 1), Slash); } } } } - return qmlDirPathsPaths; } @@ -130,19 +140,50 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int majo m_alreadySeenImports.insert(importId); } id = id.replace(QLatin1String("/"), QLatin1String(".")); - auto qmltypesPaths = completeQmltypesPaths(id, m_qmltypeDirs, major, minor); + auto qmltypesPaths = completeImportPaths(id, m_qmltypeDirs, major, minor); QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects; QList<QQmlJS::ModuleApiInfo> moduleApis; QStringList dependencies; + static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes"); + static const QLatin1String SlashQmldir("/qmldir"); for (auto const &qmltypesPath : qmltypesPaths) { - if (QFile::exists(qmltypesPath)) { - auto reader = createReaderForFile(qmltypesPath); - auto succ = reader(&objects, &moduleApis, &dependencies); - if (!succ) { - qDebug() << reader.errorMessage(); + if (QFile::exists(qmltypesPath + SlashQmldir)) { + auto reader = createQmldirParserForFile(qmltypesPath + SlashQmldir); + const auto imports = reader.imports(); + for (const QString &import : imports) + importHelper(import, prefix, major, minor); + + QHash<QString, LanguageUtils::FakeMetaObject *> qmlComponents; + const auto components = reader.components(); + for (auto it = components.begin(), end = components.end(); it != end; ++it) { + const QString filePath = qmltypesPath + QLatin1Char('/') + it->fileName; + if (!QFile::exists(filePath)) { + m_colorOut.write(QLatin1String("warning: "), Warning); + m_colorOut.write(it->fileName + QLatin1String(" is listed as component in ") + + qmltypesPath + SlashQmldir + + QLatin1String(" but does not exist.\n")); + continue; + } + + auto mo = qmlComponents.find(it.key()); + if (mo == qmlComponents.end()) + mo = qmlComponents.insert(it.key(), localQmlFile2FakeMetaObject(filePath)); + + (*mo)->addExport( + it.key(), reader.typeNamespace(), + LanguageUtils::ComponentVersion(it->majorVersion, it->minorVersion)); } - break; + for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it) { + objects.insert(it.key(), + QSharedPointer<const LanguageUtils::FakeMetaObject>(it.value())); + } + } + if (QFile::exists(qmltypesPath + SlashPluginsDotQmltypes)) { + auto reader = createQmltypesReaderForFile(qmltypesPath + SlashPluginsDotQmltypes); + auto succ = reader(&objects, &moduleApis, &dependencies); + if (!succ) + m_colorOut.writeUncolored(reader.errorMessage()); } } for (auto const &dependency : qAsConst(dependencies)) { @@ -170,7 +211,8 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath) { using namespace QQmlJS::AST; auto fake = new LanguageUtils::FakeMetaObject; - fake->setClassName(QFileInfo { filePath }.baseName()); + QString baseName = QFileInfo { filePath }.baseName(); + fake->setClassName(baseName.endsWith(".ui") ? baseName.chopped(3) : baseName); QFile file(filePath); if (!file.open(QFile::ReadOnly)) { return fake; @@ -273,6 +315,7 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath) auto sourceElement = static_cast<UiSourceElement *>(initMembers->member); if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) { LanguageUtils::FakeMetaMethod method; + method.setMethodName(fexpr->name.toString()); method.setMethodType(LanguageUtils::FakeMetaMethod::Method); FormalParameterList *parameters = fexpr->formals; while (parameters) { @@ -290,13 +333,17 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath) } else if (cast<VariableStatement *>(sourceElement->sourceElement)) { // nothing to do } else { - qDebug() << "unsupportedd sourceElement at" << sourceElement->firstSourceLocation() - << sourceElement->sourceElement->kind; + const auto loc = sourceElement->firstSourceLocation(); + m_colorOut.writeUncolored( + "unsupportedd sourceElement at " + + QString::fromLatin1("%1:%2: ").arg(loc.startLine).arg(loc.startColumn) + + QString::number(sourceElement->sourceElement->kind)); } break; } default: { - qDebug() << "unsupported element of kind" << initMembers->member->kind; + m_colorOut.writeUncolored("unsupported element of kind " + + QString::number(initMembers->member->kind)); } } initMembers = initMembers->next; @@ -304,6 +351,22 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath) return fake; } +void FindUnqualifiedIDVisitor::importDirectory(const QString &directory, const QString &prefix) +{ + QString dirname = directory; + QFileInfo info { dirname }; + if (info.isRelative()) + dirname = QDir(QFileInfo { m_filePath }.path()).filePath(dirname); + + QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter }; + while (it.hasNext()) { + LanguageUtils::FakeMetaObject *fake = localQmlFile2FakeMetaObject(it.next()); + m_exportedName2MetaObject.insert( + prefix + fake->className(), + QSharedPointer<const LanguageUtils::FakeMetaObject>(fake)); + } +} + void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString name) { for (;;) { @@ -348,11 +411,10 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *) QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter, QDirIterator::Subdirectories }; while (it.hasNext()) { - auto reader = createReaderForFile(it.next()); + auto reader = createQmltypesReaderForFile(it.next()); auto succ = reader(&objects, &moduleApis, &dependencies); - if (!succ) { - qDebug() << reader.errorMessage(); - } + if (!succ) + m_colorOut.writeUncolored(reader.errorMessage()); } } // add builtins @@ -377,6 +439,8 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *) meta->addProperty(LanguageUtils::FakeMetaProperty {"ignoreUnknownSignals", "bool", false, false, false, 0}); meta->addProperty(LanguageUtils::FakeMetaProperty {"target", "QObject", false, false, false, 0}); m_exportedName2MetaObject["Connections"] = LanguageUtils::FakeMetaObject::ConstPtr { meta }; + + importDirectory(".", QString()); return true; } @@ -476,6 +540,23 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::WithStatement *) leaveEnvironment(); } +static QString signalName(const QStringRef &handlerName) +{ + if (handlerName.startsWith("on") && handlerName.size() > 2) { + QString signal = handlerName.mid(2).toString(); + for (int i = 0; i < signal.length(); ++i) { + QCharRef ch = signal[i]; + if (ch.isLower()) + return QString(); + if (ch.isUpper()) { + ch = ch.toLower(); + return signal; + } + } + } + return QString(); +} + bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb) { using namespace QQmlJS::AST; @@ -489,8 +570,17 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb) if (m_currentScope->isVisualRootScope()) { m_rootId = identexp->name.toString(); } - } else if (name.startsWith("on") && name.size() > 2 && name.at(2).isUpper()) { - auto statement = uisb->statement; + } else { + const QString signal = signalName(name); + if (signal.isEmpty()) + return true; + + if (!m_currentScope->methods().contains(signal)) { + m_currentScope->addUnmatchedSignalHandler(name.toString(), uisb->firstSourceLocation()); + return true; + } + + const auto statement = uisb->statement; if (statement->kind == Node::Kind::Kind_ExpressionStatement) { if (static_cast<ExpressionStatement *>(statement)->expression->asFunctionDefinition()) { // functions are already handled @@ -499,17 +589,14 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb) return true; } } - QString signal = name.mid(2).toString(); - signal[0] = signal[0].toLower(); - if (!m_currentScope->methods().contains(signal)) { - qDebug() << "Info file does not contain signal" << signal; - } else { - auto method = m_currentScope->methods()[signal]; - for (auto const ¶m : method.parameterNames()) { - auto firstSourceLocation = uisb->statement->firstSourceLocation(); - bool hasMultilineStatementBody = uisb->statement->lastSourceLocation().startLine > firstSourceLocation.startLine; - m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation, hasMultilineStatementBody); - } + + auto method = m_currentScope->methods()[signal]; + for (auto const ¶m : method.parameterNames()) { + const auto firstSourceLocation = statement->firstSourceLocation(); + bool hasMultilineStatementBody + = statement->lastSourceLocation().startLine > firstSourceLocation.startLine; + m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation, + hasMultilineStatementBody); } return true; } @@ -535,13 +622,15 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp) } FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, - const QString &code, const QString &fileName) + const QString &code, const QString &fileName, + bool silent) : m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }), m_currentScope(m_rootScope.get()), m_qmltypeDirs(qmltypeDirs), m_code(code), m_rootId(QLatin1String("<id>")), - m_filePath(fileName) + m_filePath(fileName), + m_colorOut(silent) { // setup color output m_colorOut.insertColorMapping(Error, ColorOutput::RedForeground); @@ -649,18 +738,9 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import) prefix += import->importId + QLatin1Char('.'); } auto dirname = import->fileName.toString(); - if (!dirname.isEmpty()) { - QFileInfo info { dirname }; - if (info.isRelative()) { - dirname = QDir(QFileInfo { m_filePath }.path()).filePath(dirname); - } - QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter }; - while (it.hasNext()) { - LanguageUtils::FakeMetaObject *fake = localQmlFile2FakeMetaObject(it.next()); - m_exportedName2MetaObject.insert( - fake->className(), QSharedPointer<const LanguageUtils::FakeMetaObject>(fake)); - } - } + if (!dirname.isEmpty()) + importDirectory(dirname, prefix); + QString path {}; if (!import->importId.isEmpty()) { m_qmlid2meta.insert(import->importId.toString(), {}); // TODO: do not put imported ids into the same space as qml IDs @@ -768,16 +848,19 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod) return true; } -void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *) +bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::PatternElement *element) { - leaveEnvironment(); + if (element->isVariableDeclaration()) { + QQmlJS::AST::BoundNames names; + element->boundNames(&names); + for (const auto &name : names) + m_currentScope->insertJSIdentifier(name.id, element->scope); + } + + return true; } -QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc) +void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *) { - QDebugStateSaver saver(dbg); - dbg.nospace() << loc.startLine; - dbg.nospace() << ":"; - dbg.nospace() << loc.startColumn; - return dbg.maybeSpace(); + leaveEnvironment(); } diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h index 181f42f265..80413bd402 100644 --- a/tools/qmllint/findunqualified.h +++ b/tools/qmllint/findunqualified.h @@ -43,7 +43,8 @@ enum class ScopeType; class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor { public: - explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, const QString& fileName); + explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, + const QString& fileName, bool silent); ~FindUnqualifiedIDVisitor() override; bool check(); @@ -70,7 +71,7 @@ private: void importHelper(QString id, QString prefix, int major, int minor); LanguageUtils::FakeMetaObject* localQmlFile2FakeMetaObject(QString filePath); - + void importDirectory(const QString &directory, const QString &prefix); void importExportedNames(QStringRef prefix, QString name); void throwRecursionDepthError() override; @@ -125,6 +126,8 @@ private: // expression handling bool visit(QQmlJS::AST::IdentifierExpression *idexp) override; + + bool visit(QQmlJS::AST::PatternElement *) override; }; diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index 235ec16c6e..56f72dd020 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -50,7 +50,8 @@ static bool lint_file(const QString &filename, const bool silent, const bool war { QFile file(filename); if (!file.open(QFile::ReadOnly)) { - qWarning() << "Failed to open file" << filename << file.error(); + if (!silent) + qWarning() << "Failed to open file" << filename << file.error(); return false; } @@ -76,7 +77,7 @@ static bool lint_file(const QString &filename, const bool silent, const bool war if (success && !isJavaScript && warnUnqualied) { auto root = parser.rootNode(); - FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename}; + FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename, silent }; root->accept(&v); success = v.check(); } @@ -122,9 +123,9 @@ int main(int argv, char *argc[]) // use host qml import path as a sane default if nothing else has been provided QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption) ? parser.values(qmltypesDirsOption) #ifndef QT_BOOTSTRAPPED - : QStringList{QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)}; + : QStringList{QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath), QLatin1String(".")}; #else - : QStringList{}; + : QStringList{QLatin1String(".")}; #endif #else bool silent = false; diff --git a/tools/qmllint/qcoloroutput.cpp b/tools/qmllint/qcoloroutput.cpp index d2e723700a..eb4c721663 100644 --- a/tools/qmllint/qcoloroutput.cpp +++ b/tools/qmllint/qcoloroutput.cpp @@ -39,7 +39,7 @@ class ColorOutputPrivate { public: - ColorOutputPrivate() : currentColorID(-1) + ColorOutputPrivate(bool silent) : currentColorID(-1), silent(silent) { /* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance @@ -53,6 +53,7 @@ public: ColorOutput::ColorMapping colorMapping; int currentColorID; bool coloringEnabled; + bool silent; static const char *const foregrounds[]; static const char *const backgrounds[]; @@ -238,7 +239,7 @@ ColorOutput::ColorMapping ColorOutput::colorMapping() const /*! Constructs a ColorOutput instance, ready for use. */ -ColorOutput::ColorOutput() : d(new ColorOutputPrivate()) +ColorOutput::ColorOutput(bool silent) : d(new ColorOutputPrivate(silent)) { } @@ -258,7 +259,8 @@ ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer ha */ void ColorOutput::write(const QString &message, int colorID) { - d->write(colorify(message, colorID)); + if (!d->silent) + d->write(colorify(message, colorID)); } /*! @@ -269,7 +271,8 @@ void ColorOutput::write(const QString &message, int colorID) */ void ColorOutput::writeUncolored(const QString &message) { - d->write(message + QLatin1Char('\n')); + if (!d->silent) + d->write(message + QLatin1Char('\n')); } /*! diff --git a/tools/qmllint/qcoloroutput_p.h b/tools/qmllint/qcoloroutput_p.h index 710bf5db74..aefa765a87 100644 --- a/tools/qmllint/qcoloroutput_p.h +++ b/tools/qmllint/qcoloroutput_p.h @@ -89,7 +89,7 @@ public: typedef QFlags<ColorCodeComponent> ColorCode; typedef QHash<int, ColorCode> ColorMapping; - ColorOutput(); + ColorOutput(bool silent); ~ColorOutput(); void setColorMapping(const ColorMapping &cMapping); diff --git a/tools/qmllint/qmljstypedescriptionreader.cpp b/tools/qmllint/qmljstypedescriptionreader.cpp index 542cdf99eb..b8aecdddb1 100644 --- a/tools/qmllint/qmljstypedescriptionreader.cpp +++ b/tools/qmllint/qmljstypedescriptionreader.cpp @@ -123,15 +123,13 @@ void TypeDescriptionReader::readDocument(UiProgram *ast) return; } - ComponentVersion version; - const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length); - const int dotIdx = versionString.indexOf(QLatin1Char('.')); - if (dotIdx != -1) { - version = ComponentVersion(versionString.leftRef(dotIdx).toInt(), - versionString.midRef(dotIdx + 1).toInt()); - } - if (version.majorVersion() != 1) { - addError(import->versionToken, tr("Major version different from 1 not supported.")); + if (!import->version) { + addError(import->firstSourceLocation(), tr("Import statement without version.")); + return; + } + + if (import->version->majorVersion != 1) { + addError(import->version->firstSourceLocation(), tr("Major version different from 1 not supported.")); return; } @@ -666,39 +664,34 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt return; } - ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); + auto *expStmt = AST::cast<ExpressionStatement *>(ast->statement); if (!expStmt) { - addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon.")); + addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon.")); return; } - ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression); - if (!objectLit) { - addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon.")); - return; - } - - for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { - PatternProperty *assignement = AST::cast<PatternProperty *>(it->property); - if (assignement) { - StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name); - NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer); - UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer); - if (minus) - value = AST::cast<NumericLiteral *>(minus->expression); - if (!propName || !value) { - addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements.")); - continue; + if (auto *objectLit = AST::cast<ObjectPattern *>(expStmt->expression)) { + for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { + if (PatternProperty *assignement = it->property) { + if (auto *name = AST::cast<StringLiteralPropertyName *>(assignement->name)) { + fme->addKey(name->id.toString()); + continue; + } } - - double v = value->value; - if (minus) - v = -v; - fme->addKey(propName->id.toString(), v); - continue; + addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); + } + } else if (auto *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression)) { + for (PatternElementList *it = arrayLit->elements; it; it = it->next) { + if (PatternElement *element = it->element) { + if (auto *name = AST::cast<StringLiteral *>(element->initializer)) { + fme->addKey(name->value.toString()); + continue; + } + } + addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); } - PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next); - if (getterSetter) - addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements.")); + } else { + addError(ast->statement->firstSourceLocation(), + tr("Expected either array or object literal as enum definition.")); } } diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp index 2eff3fa319..1e873cca8f 100644 --- a/tools/qmllint/scopetree.cpp +++ b/tools/qmllint/scopetree.cpp @@ -81,6 +81,12 @@ void ScopeTree::insertPropertyIdentifier(QString id) this->addMethod(method); } +void ScopeTree::addUnmatchedSignalHandler(const QString &handler, + const QQmlJS::AST::SourceLocation &location) +{ + m_unmatchedSignalHandlers.append(qMakePair(handler, location)); +} + bool ScopeTree::isIdInCurrentScope(const QString &id) const { return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id); @@ -132,6 +138,15 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan workQueue.enqueue(this); while (!workQueue.empty()) { const ScopeTree* currentScope = workQueue.dequeue(); + for (const auto &handler : currentScope->m_unmatchedSignalHandlers) { + colorOut.write("Warning: ", Warning); + colorOut.write(QString::fromLatin1( + "no matching signal found for handler \"%1\" at %2:%3\n") + .arg(handler.first).arg(handler.second.startLine) + .arg(handler.second.startColumn), Normal); + printContext(colorOut, code, handler.second); + } + for (auto idLocationPair : currentScope->m_accessedIdentifiers) { if (qmlIDs.contains(idLocationPair.first)) continue; @@ -141,13 +156,11 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan noUnqualifiedIdentifier = false; colorOut.write("Warning: ", Warning); auto location = idLocationPair.second; - colorOut.write(QString::asprintf("unqualified access at %d:%d\n", location.startLine, location.startColumn), Normal); - IssueLocationWithContext issueLocationWithContext {code, location}; - colorOut.write(issueLocationWithContext.beforeText.toString(), Normal); - colorOut.write(issueLocationWithContext.issueText.toString(), Error); - colorOut.write(issueLocationWithContext.afterText.toString() + QLatin1Char('\n'), Normal); - int tabCount = issueLocationWithContext.beforeText.count(QLatin1Char('\t')); - colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText.length() - tabCount) + QString("\t").repeated(tabCount) + QString("^").repeated(location.length) + QLatin1Char('\n'), Normal); + colorOut.write(QString::asprintf("unqualified access at %d:%d\n", location.startLine, + location.startColumn), Normal); + + printContext(colorOut, code, location); + // root(JS) --> program(qml) --> (first element) if (root->m_childScopes[0]->m_childScopes[0]->m_currentScopeQMLIdentifiers.contains(idLocationPair.first)) { ScopeTree *parentScope = currentScope->m_parentScope; @@ -161,6 +174,7 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan colorOut.write("Note: ", Warning); colorOut.write(("You first have to give the root element an id\n")); } + IssueLocationWithContext issueLocationWithContext {code, location}; colorOut.write(issueLocationWithContext.beforeText.toString(), Normal); colorOut.write(rootId + QLatin1Char('.'), Hint); colorOut.write(issueLocationWithContext.issueText.toString(), Normal); @@ -212,7 +226,7 @@ QMap<QString, LanguageUtils::FakeMetaMethod>const &ScopeTree::methods() const bool ScopeTree::isIdInCurrentQMlScopes(QString id) const { auto qmlScope = getCurrentQMLScope(); - return qmlScope->m_currentScopeQMLIdentifiers.contains(id); + return qmlScope->m_currentScopeQMLIdentifiers.contains(id) || qmlScope->m_methods.contains(id); } bool ScopeTree::isIdInCurrentJSScopes(QString id) const @@ -250,6 +264,20 @@ ScopeTree *ScopeTree::getCurrentQMLScope() return qmlScope; } +void ScopeTree::printContext(ColorOutput &colorOut, const QString &code, + const QQmlJS::AST::SourceLocation &location) const +{ + IssueLocationWithContext issueLocationWithContext {code, location}; + colorOut.write(issueLocationWithContext.beforeText.toString(), Normal); + colorOut.write(issueLocationWithContext.issueText.toString(), Error); + colorOut.write(issueLocationWithContext.afterText.toString() + QLatin1Char('\n'), Normal); + int tabCount = issueLocationWithContext.beforeText.count(QLatin1Char('\t')); + colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText.length() - tabCount) + + QString("\t").repeated(tabCount) + + QString("^").repeated(location.length) + + QLatin1Char('\n'), Normal); +} + ScopeType ScopeTree::scopeType() {return m_scopeType;} void ScopeTree::addMethod(LanguageUtils::FakeMetaMethod method) diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h index 872a509123..52cdc45e96 100644 --- a/tools/qmllint/scopetree.h +++ b/tools/qmllint/scopetree.h @@ -73,6 +73,8 @@ public: void insertQMLIdentifier(QString id); void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody); void insertPropertyIdentifier(QString id); // inserts property as qml identifier as well as the corresponding + void addUnmatchedSignalHandler(const QString &handler, + const QQmlJS::AST::SourceLocation &location); bool isIdInCurrentScope(QString const &id) const; void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports); @@ -96,11 +98,14 @@ private: ScopeTree *m_parentScope; QString m_name; ScopeType m_scopeType; + QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_unmatchedSignalHandlers; bool isIdInCurrentQMlScopes(QString id) const; bool isIdInCurrentJSScopes(QString id) const; bool isIdInjectedFromSignal(QString id) const; const ScopeTree* getCurrentQMLScope() const; ScopeTree* getCurrentQMLScope(); + void printContext(ColorOutput& colorOut, const QString &code, + const QQmlJS::AST::SourceLocation &location) const; }; #endif // SCOPETREE_H diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index f0ed1f8ebe..5e999c557a 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -105,12 +105,18 @@ struct QmlVersionInfo QString pluginImportUri; int majorVersion; int minorVersion; + bool strict; }; static bool matchingImportUri(const QQmlType &ty, const QmlVersionInfo& versionInfo) { - return (versionInfo.pluginImportUri == ty.module() - && (ty.majorVersion() == versionInfo.majorVersion || ty.majorVersion() == -1)) - || ty.module().isEmpty(); + if (versionInfo.strict) { + return (versionInfo.pluginImportUri == ty.module() + && (ty.majorVersion() == versionInfo.majorVersion || ty.majorVersion() == -1)) + || ty.module().isEmpty(); + } + return ty.module().isEmpty() + || versionInfo.pluginImportUri == ty.module() + || ty.module().startsWith(versionInfo.pluginImportUri + QLatin1Char('.')); } void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info, bool extended = false, bool alreadyChangedModule = false) @@ -230,14 +236,14 @@ QByteArray convertToId(const QMetaObject *mo) // Collect all metaobjects for types registered with qmlRegisterType() without parameters void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<const QMetaObject *>& metas, - QMap<QString, QSet<QQmlType>> &compositeTypes, const QmlVersionInfo &info) { + QMap<QString, QList<QQmlType>> &compositeTypes, const QmlVersionInfo &info) { const auto qmlAllTypes = QQmlMetaType::qmlAllTypes(); for (const QQmlType &ty : qmlAllTypes) { if (!metas.contains(ty.baseMetaObject())) { if (!ty.isComposite()) { collectReachableMetaObjects(engine, ty, &metas, info); } else if (matchingImportUri(ty, info)) { - compositeTypes[ty.elementName()].insert(ty); + compositeTypes[ty.elementName()].append(ty); } } } @@ -246,7 +252,7 @@ void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<c QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, QSet<const QMetaObject *> &noncreatables, QSet<const QMetaObject *> &singletons, - QMap<QString, QSet<QQmlType>> &compositeTypes, + QMap<QString, QList<QQmlType>> &compositeTypes, const QmlVersionInfo &info, const QList<QQmlType> &skip = QList<QQmlType>() ) @@ -266,7 +272,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, qmlTypesByCppName[ty.baseMetaObject()->className()].insert(ty); collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas, info); } else { - compositeTypes[ty.elementName()].insert(ty); + compositeTypes[ty.elementName()].append(ty); } } @@ -436,10 +442,11 @@ public: } QString getPrototypeNameForCompositeType(const QMetaObject *metaObject, QSet<QByteArray> &defaultReachableNames, - QList<const QMetaObject *> *objectsToMerge) + QList<const QMetaObject *> *objectsToMerge, const QmlVersionInfo &versionInfo) { + auto ty = QQmlMetaType::qmlType(metaObject); QString prototypeName; - if (!defaultReachableNames.contains(metaObject->className())) { + if (matchingImportUri(ty, versionInfo)) { // dynamic meta objects can break things badly // but extended types are usually fine const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(metaObject->d.data); @@ -451,20 +458,20 @@ public: prototypeName = "QObject"; else prototypeName = getPrototypeNameForCompositeType( - superMetaObject, defaultReachableNames, objectsToMerge); + superMetaObject, defaultReachableNames, objectsToMerge, versionInfo); } else { prototypeName = convertToId(metaObject->className()); } return prototypeName; } - void dumpComposite(QQmlEngine *engine, const QSet<QQmlType> &compositeType, QSet<QByteArray> &defaultReachableNames) + void dumpComposite(QQmlEngine *engine, const QList<QQmlType> &compositeType, QSet<QByteArray> &defaultReachableNames, const QmlVersionInfo &versionInfo) { for (const QQmlType &type : compositeType) - dumpCompositeItem(engine, type, defaultReachableNames); + dumpCompositeItem(engine, type, defaultReachableNames, versionInfo); } - void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, QSet<QByteArray> &defaultReachableNames) + void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, QSet<QByteArray> &defaultReachableNames, const QmlVersionInfo &versionInfo) { QQmlComponent e(engine, compositeType.sourceUrl()); if (!e.isReady()) { @@ -486,7 +493,7 @@ public: KnownAttributes knownAttributes; // Get C++ base class name for the composite type QString prototypeName = getPrototypeNameForCompositeType(mainMeta, defaultReachableNames, - &objectsToMerge); + &objectsToMerge, versionInfo); qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName)); QString qmlTyName = compositeType.qmlTypeName(); @@ -991,6 +998,16 @@ void printDebugMessage(QtMsgType, const QMessageLogContext &, const QString &msg // In case of QtFatalMsg the calling code will abort() when appropriate. } +QT_BEGIN_NAMESPACE +static bool operator<(const QQmlType &a, const QQmlType &b) +{ + return a.qmlTypeName() < b.qmlTypeName() + || (a.qmlTypeName() == b.qmlTypeName() + && ((a.majorVersion() < b.majorVersion()) + || (a.majorVersion() == b.majorVersion() + && a.minorVersion() < b.minorVersion()))); +} +QT_END_NAMESPACE int main(int argc, char *argv[]) { @@ -1056,6 +1073,7 @@ int main(int argc, char *argv[]) QString dependenciesFile; QString mergeFile; bool forceQtQuickDependency = true; + bool strict = false; enum Action { Uri, Path, Builtins }; Action action = Uri; { @@ -1120,6 +1138,10 @@ int main(int argc, char *argv[]) } else if (arg == QLatin1String("--qapp") || arg == QLatin1String("-qapp")) { continue; + } else if (arg == QLatin1String("--strict") + || arg == QLatin1String("-strict")) { + strict = true; + continue; } else { std::cerr << "Invalid argument: " << qPrintable(arg) << std::endl; return EXIT_INVALIDARGUMENTS; @@ -1216,7 +1238,6 @@ int main(int argc, char *argv[]) // find all QMetaObjects reachable from the builtin module QSet<const QMetaObject *> uncreatableMetas; QSet<const QMetaObject *> singletonMetas; - QMap<QString, QSet<QQmlType>> defaultCompositeTypes; // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported QSet<QByteArray> defaultReachableNames; @@ -1225,11 +1246,13 @@ int main(int argc, char *argv[]) QSet<const QMetaObject *> metas; // composite types we want to dump information of - QMap<QString, QSet<QQmlType>> compositeTypes; + QMap<QString, QList<QQmlType>> compositeTypes; int majorVersion = qtQmlMajorVersion, minorVersion = qtQmlMinorVersion; + QmlVersionInfo info; if (action == Builtins) { - QSet<const QMetaObject *> builtins = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes, {QLatin1String("Qt"), majorVersion, minorVersion}); + QMap<QString, QList<QQmlType>> defaultCompositeTypes; + QSet<const QMetaObject *> builtins = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes, {QLatin1String("Qt"), majorVersion, minorVersion, strict}); Q_ASSERT(builtins.size() == 1); metas.insert(*builtins.begin()); } else { @@ -1289,15 +1312,12 @@ int main(int argc, char *argv[]) return EXIT_IMPORTERROR; } } + info = {pluginImportUri, majorVersion, minorVersion, strict}; + QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, info, defaultTypes); - QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, {pluginImportUri, majorVersion, minorVersion}, defaultTypes); - - for (QString iter: compositeTypes.keys()) { - if (defaultCompositeTypes.contains(iter)) { - QSet<QQmlType> compositeTypesByName = compositeTypes.value(iter); - compositeTypesByName.subtract(defaultCompositeTypes.value(iter)); - compositeTypes[iter] = compositeTypesByName; - } + for (auto it = compositeTypes.begin(), end = compositeTypes.end(); it != end; ++it) { + std::sort(it->begin(), it->end()); + it->erase(std::unique(it->begin(), it->end()), it->end()); } for (const QMetaObject *mo : qAsConst(candidates)) { @@ -1348,9 +1368,9 @@ int main(int argc, char *argv[]) dumper.dump(QQmlEnginePrivate::get(&engine), meta, uncreatableMetas.contains(meta), singletonMetas.contains(meta)); } - QMap<QString, QSet<QQmlType> >::const_iterator iter = compositeTypes.constBegin(); + QMap<QString, QList<QQmlType>>::const_iterator iter = compositeTypes.constBegin(); for (; iter != compositeTypes.constEnd(); ++iter) - dumper.dumpComposite(&engine, iter.value(), defaultReachableNames); + dumper.dumpComposite(&engine, iter.value(), defaultReachableNames, info); // define QEasingCurve as an extension of QQmlEasingValueType, this way // properties using the QEasingCurve type get useful type information. diff --git a/tools/qmlplugindump/qmlstreamwriter.cpp b/tools/qmlplugindump/qmlstreamwriter.cpp index 3632cee60d..b0fbc4e443 100644 --- a/tools/qmlplugindump/qmlstreamwriter.cpp +++ b/tools/qmlplugindump/qmlstreamwriter.cpp @@ -50,9 +50,9 @@ void QmlStreamWriter::writeEndDocument() void QmlStreamWriter::writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as) { - m_stream->write(QString("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8()); + m_stream->write(QString::fromLatin1("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8()); if (!as.isEmpty()) - m_stream->write(QString(" as %1").arg(as).toUtf8()); + m_stream->write(QString::fromLatin1(" as %1").arg(as).toUtf8()); m_stream->write("\n"); } @@ -60,7 +60,7 @@ void QmlStreamWriter::writeStartObject(const QString &component) { flushPotentialLinesWithNewlines(); writeIndent(); - m_stream->write(QString("%1 {").arg(component).toUtf8()); + m_stream->write(QString::fromLatin1("%1 {").arg(component).toUtf8()); ++m_indentDepth; m_maybeOneline = true; } @@ -89,7 +89,7 @@ void QmlStreamWriter::writeEndObject() void QmlStreamWriter::writeScriptBinding(const QString &name, const QString &rhs) { - writePotentialLine(QString("%1: %2").arg(name, rhs).toUtf8()); + writePotentialLine(QString::fromLatin1("%1: %2").arg(name, rhs).toUtf8()); } void QmlStreamWriter::writeBooleanBinding(const QString &name, bool value) @@ -104,7 +104,7 @@ void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList & // try to use a single line QString singleLine; - singleLine += QString("%1: [").arg(name); + singleLine += QString::fromLatin1("%1: [").arg(name); for (int i = 0; i < elements.size(); ++i) { singleLine += elements.at(i); if (i != elements.size() - 1) @@ -117,7 +117,7 @@ void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList & } // write multi-line - m_stream->write(QString("%1: [\n").arg(name).toUtf8()); + m_stream->write(QString::fromLatin1("%1: [\n").arg(name).toUtf8()); ++m_indentDepth; for (int i = 0; i < elements.size(); ++i) { writeIndent(); @@ -143,13 +143,13 @@ void QmlStreamWriter::writeScriptObjectLiteralBinding(const QString &name, const { flushPotentialLinesWithNewlines(); writeIndent(); - m_stream->write(QString("%1: {\n").arg(name).toUtf8()); + m_stream->write(QString::fromLatin1("%1: {\n").arg(name).toUtf8()); ++m_indentDepth; for (int i = 0; i < keyValue.size(); ++i) { const QString key = keyValue.at(i).first; const QString value = keyValue.at(i).second; writeIndent(); - m_stream->write(QString("%1: %2").arg(key, value).toUtf8()); + m_stream->write(QString::fromLatin1("%1: %2").arg(key, value).toUtf8()); if (i != keyValue.size() - 1) { m_stream->write(",\n"); } else { diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp index f92ffa9ff5..7b010546c3 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.cpp +++ b/tools/qmlprofiler/qmlprofilerapplication.cpp @@ -302,7 +302,7 @@ void QmlProfilerApplication::flush() { if (m_recording) { m_pendingRequest = REQUEST_FLUSH; - m_qmlProfilerClient->sendRecordingStatus(false); + m_qmlProfilerClient->setRecording(false); } else { if (m_profilerData->save(m_interactiveOutputFile)) { m_profilerData->clear(); @@ -392,7 +392,7 @@ void QmlProfilerApplication::userCommand(const QString &command) if (cmd == Constants::CMD_RECORD || cmd == Constants::CMD_RECORD2) { m_pendingRequest = REQUEST_TOGGLE_RECORDING; - m_qmlProfilerClient->sendRecordingStatus(!m_recording); + m_qmlProfilerClient->setRecording(!m_recording); } else if (cmd == Constants::CMD_QUIT || cmd == Constants::CMD_QUIT2) { m_pendingRequest = REQUEST_QUIT; if (m_recording) { diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index d5662a0182..9ec143975e 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -101,7 +101,6 @@ QmlProfilerData::~QmlProfilerData() void QmlProfilerData::clear() { - d->eventTypes.clear(); d->events.clear(); d->traceEndTime = std::numeric_limits<qint64>::min(); diff --git a/tools/qmltime/qmltime.cpp b/tools/qmltime/qmltime.cpp index 7baedff611..b0761a54d4 100644 --- a/tools/qmltime/qmltime.cpp +++ b/tools/qmltime/qmltime.cpp @@ -207,7 +207,7 @@ int main(int argc, char ** argv) QGuiApplication app(argc, argv); QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); - qmlRegisterType<Timer>("QmlTime", 1, 0, "Timer"); + qmlRegisterTypesAndRevisions<Timer>("QmlTime", 1); uint iterations = 1024; QString filename; |