aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qmldirparser
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qmldirparser')
-rw-r--r--src/qml/qmldirparser/qmldirparser.pri11
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp399
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h163
-rw-r--r--src/qml/qmldirparser/qqmlerror.cpp365
-rw-r--r--src/qml/qmldirparser/qqmlerror.h91
-rw-r--r--src/qml/qmldirparser/qqmlsourcecoordinate_p.h72
6 files changed, 1101 insertions, 0 deletions
diff --git a/src/qml/qmldirparser/qmldirparser.pri b/src/qml/qmldirparser/qmldirparser.pri
new file mode 100644
index 0000000000..660e7b395a
--- /dev/null
+++ b/src/qml/qmldirparser/qmldirparser.pri
@@ -0,0 +1,11 @@
+INCLUDEPATH += $$PWD
+INCLUDEPATH += $$OUT_PWD
+
+HEADERS += \
+ $$PWD/qqmldirparser_p.h \
+ $$PWD/qqmlerror.h \
+ $$PWD/qqmlsourcecoordinate_p.h
+
+SOURCES += \
+ $$PWD/qqmldirparser.cpp \
+ $$PWD/qqmlerror.cpp
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
new file mode 100644
index 0000000000..e944b52e47
--- /dev/null
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -0,0 +1,399 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmldirparser_p.h"
+#include "qqmlerror.h"
+
+#include <QtCore/QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+static int parseInt(const QStringRef &str, bool *ok)
+{
+ int pos = 0;
+ int number = 0;
+ while (pos < str.length() && str.at(pos).isDigit()) {
+ if (pos != 0)
+ number *= 10;
+ number += str.at(pos).unicode() - '0';
+ ++pos;
+ }
+ if (pos != str.length())
+ *ok = false;
+ else
+ *ok = true;
+ return number;
+}
+
+static bool parseVersion(const QString &str, int *major, int *minor)
+{
+ const int dotIndex = str.indexOf(QLatin1Char('.'));
+ if (dotIndex != -1 && str.indexOf(QLatin1Char('.'), dotIndex + 1) == -1) {
+ bool ok = false;
+ *major = parseInt(QStringRef(&str, 0, dotIndex), &ok);
+ if (ok)
+ *minor = parseInt(QStringRef(&str, dotIndex + 1, str.length() - dotIndex - 1), &ok);
+ return ok;
+ }
+ return false;
+}
+
+QQmlDirParser::QQmlDirParser() : _designerSupported(false)
+{
+}
+
+QQmlDirParser::~QQmlDirParser()
+{
+}
+
+inline static void scanSpace(const QChar *&ch) {
+ while (ch->isSpace() && !ch->isNull() && *ch != QLatin1Char('\n'))
+ ++ch;
+}
+
+inline static void scanToEnd(const QChar *&ch) {
+ while (*ch != QLatin1Char('\n') && !ch->isNull())
+ ++ch;
+}
+
+inline static void scanWord(const QChar *&ch) {
+ while (!ch->isSpace() && !ch->isNull())
+ ++ch;
+}
+
+/*!
+\a url is used for generating errors.
+*/
+bool QQmlDirParser::parse(const QString &source)
+{
+ _errors.clear();
+ _plugins.clear();
+ _components.clear();
+ _scripts.clear();
+ _designerSupported = false;
+ _className.clear();
+
+ quint16 lineNumber = 0;
+ bool firstLine = true;
+
+ const QChar *ch = source.constData();
+ while (!ch->isNull()) {
+ ++lineNumber;
+
+ bool invalidLine = false;
+ const QChar *lineStart = ch;
+
+ scanSpace(ch);
+ if (*ch == QLatin1Char('\n')) {
+ ++ch;
+ continue;
+ }
+ if (ch->isNull())
+ break;
+
+ QString sections[4];
+ int sectionCount = 0;
+
+ do {
+ if (*ch == QLatin1Char('#')) {
+ scanToEnd(ch);
+ break;
+ }
+ const QChar *start = ch;
+ scanWord(ch);
+ if (sectionCount < 4) {
+ sections[sectionCount++] = source.mid(start-source.constData(), ch-start);
+ } else {
+ reportError(lineNumber, start-lineStart, QLatin1String("unexpected token"));
+ scanToEnd(ch);
+ invalidLine = true;
+ break;
+ }
+ scanSpace(ch);
+ } while (*ch != QLatin1Char('\n') && !ch->isNull());
+
+ if (!ch->isNull())
+ ++ch;
+
+ if (invalidLine) {
+ reportError(lineNumber, 0,
+ QStringLiteral("invalid qmldir directive contains too many tokens"));
+ continue;
+ } else if (sectionCount == 0) {
+ continue; // no sections, no party.
+
+ } else if (sections[0] == QLatin1String("module")) {
+ if (sectionCount != 2) {
+ reportError(lineNumber, 0,
+ QStringLiteral("module identifier directive requires one argument, but %1 were provided").arg(sectionCount - 1));
+ continue;
+ }
+ if (!_typeNamespace.isEmpty()) {
+ reportError(lineNumber, 0,
+ QStringLiteral("only one module identifier directive may be defined in a qmldir file"));
+ continue;
+ }
+ if (!firstLine) {
+ reportError(lineNumber, 0,
+ QStringLiteral("module identifier directive must be the first directive in a qmldir file"));
+ continue;
+ }
+
+ _typeNamespace = sections[1];
+
+ } else if (sections[0] == QLatin1String("plugin")) {
+ if (sectionCount < 2 || sectionCount > 3) {
+ reportError(lineNumber, 0,
+ QStringLiteral("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1));
+
+ continue;
+ }
+
+ const Plugin entry(sections[1], sections[2]);
+
+ _plugins.append(entry);
+
+ } else if (sections[0] == QLatin1String("classname")) {
+ if (sectionCount < 2) {
+ reportError(lineNumber, 0,
+ QStringLiteral("classname directive requires an argument, but %1 were provided").arg(sectionCount - 1));
+
+ continue;
+ }
+
+ _className = sections[1];
+
+ } else if (sections[0] == QLatin1String("internal")) {
+ if (sectionCount != 3) {
+ reportError(lineNumber, 0,
+ QStringLiteral("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1));
+ continue;
+ }
+ Component entry(sections[1], sections[2], -1, -1);
+ entry.internal = true;
+ _components.insertMulti(entry.typeName, entry);
+ } else if (sections[0] == QLatin1String("singleton")) {
+ if (sectionCount < 3 || sectionCount > 4) {
+ reportError(lineNumber, 0,
+ QStringLiteral("singleton types require 2 or 3 arguments, but %1 were provided").arg(sectionCount - 1));
+ continue;
+ } else if (sectionCount == 3) {
+ // handle qmldir directory listing case where singleton is defined in the following pattern:
+ // singleton TestSingletonType TestSingletonType.qml
+ Component entry(sections[1], sections[2], -1, -1);
+ entry.singleton = true;
+ _components.insertMulti(entry.typeName, entry);
+ } else {
+ // handle qmldir module listing case where singleton is defined in the following pattern:
+ // singleton TestSingletonType 2.0 TestSingletonType20.qml
+ int major, minor;
+ if (parseVersion(sections[2], &major, &minor)) {
+ const QString &fileName = sections[3];
+ Component entry(sections[1], fileName, major, minor);
+ entry.singleton = true;
+ _components.insertMulti(entry.typeName, entry);
+ } else {
+ reportError(lineNumber, 0, QStringLiteral("invalid version %1, expected <major>.<minor>").arg(sections[2]));
+ }
+ }
+ } else if (sections[0] == QLatin1String("typeinfo")) {
+ if (sectionCount != 2) {
+ reportError(lineNumber, 0,
+ QStringLiteral("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1));
+ continue;
+ }
+#ifdef QT_CREATOR
+ TypeInfo typeInfo(sections[1]);
+ _typeInfos.append(typeInfo);
+#endif
+
+ } else if (sections[0] == QLatin1String("designersupported")) {
+ if (sectionCount != 1)
+ reportError(lineNumber, 0, QStringLiteral("designersupported does not expect any argument"));
+ else
+ _designerSupported = true;
+ } else if (sections[0] == QLatin1String("depends")) {
+ if (sectionCount != 3) {
+ reportError(lineNumber, 0,
+ QStringLiteral("depends requires 2 arguments, but %1 were provided").arg(sectionCount - 1));
+ continue;
+ }
+
+ int major, minor;
+ if (parseVersion(sections[2], &major, &minor)) {
+ Component entry(sections[1], QString(), major, minor);
+ entry.internal = true;
+ _dependencies.insert(entry.typeName, entry);
+ } else {
+ reportError(lineNumber, 0, QStringLiteral("invalid version %1, expected <major>.<minor>").arg(sections[2]));
+ }
+ } else if (sectionCount == 2) {
+ // No version specified (should only be used for relative qmldir files)
+ const Component entry(sections[0], sections[1], -1, -1);
+ _components.insertMulti(entry.typeName, entry);
+ } else if (sectionCount == 3) {
+ int major, minor;
+ if (parseVersion(sections[1], &major, &minor)) {
+ const QString &fileName = sections[2];
+
+ if (fileName.endsWith(QLatin1String(".js")) || fileName.endsWith(QLatin1String(".mjs"))) {
+ // A 'js' extension indicates a namespaced script import
+ const Script entry(sections[0], fileName, major, minor);
+ _scripts.append(entry);
+ } else {
+ const Component entry(sections[0], fileName, major, minor);
+ _components.insertMulti(entry.typeName, entry);
+ }
+ } else {
+ reportError(lineNumber, 0, QStringLiteral("invalid version %1, expected <major>.<minor>").arg(sections[1]));
+ }
+ } else {
+ reportError(lineNumber, 0,
+ QStringLiteral("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount));
+ }
+
+ firstLine = false;
+ }
+
+ return hasError();
+}
+
+void QQmlDirParser::reportError(quint16 line, quint16 column, const QString &description)
+{
+ QQmlJS::DiagnosticMessage error;
+ error.loc.startLine = line;
+ error.loc.startColumn = column;
+ error.message = description;
+ _errors.append(error);
+}
+
+bool QQmlDirParser::hasError() const
+{
+ if (! _errors.isEmpty())
+ return true;
+
+ return false;
+}
+
+void QQmlDirParser::setError(const QQmlError &e)
+{
+ _errors.clear();
+ reportError(e.line(), e.column(), e.description());
+}
+
+QList<QQmlError> QQmlDirParser::errors(const QString &uri) const
+{
+ QUrl url(uri);
+ QList<QQmlError> errors;
+ const int numErrors = _errors.size();
+ errors.reserve(numErrors);
+ for (int i = 0; i < numErrors; ++i) {
+ const QQmlJS::DiagnosticMessage &msg = _errors.at(i);
+ QQmlError e;
+ QString description = msg.message;
+ description.replace(QLatin1String("$$URI$$"), uri);
+ e.setDescription(description);
+ e.setUrl(url);
+ e.setLine(msg.loc.startLine);
+ e.setColumn(msg.loc.startColumn);
+ errors << e;
+ }
+ return errors;
+}
+
+QString QQmlDirParser::typeNamespace() const
+{
+ return _typeNamespace;
+}
+
+void QQmlDirParser::setTypeNamespace(const QString &s)
+{
+ _typeNamespace = s;
+}
+
+QList<QQmlDirParser::Plugin> QQmlDirParser::plugins() const
+{
+ return _plugins;
+}
+
+QHash<QString, QQmlDirParser::Component> QQmlDirParser::components() const
+{
+ return _components;
+}
+
+QHash<QString, QQmlDirParser::Component> QQmlDirParser::dependencies() const
+{
+ return _dependencies;
+}
+
+QList<QQmlDirParser::Script> QQmlDirParser::scripts() const
+{
+ return _scripts;
+}
+
+QList<QQmlDirParser::TypeInfo> QQmlDirParser::typeInfos() const
+{
+ return _typeInfos;
+}
+
+bool QQmlDirParser::designerSupported() const
+{
+ return _designerSupported;
+}
+
+QString QQmlDirParser::className() const
+{
+ return _className;
+}
+
+QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component)
+{
+ const QString output = QStringLiteral("{%1 %2.%3}").
+ arg(component.typeName).arg(component.majorVersion).arg(component.minorVersion);
+ return debug << qPrintable(output);
+}
+
+QDebug &operator<< (QDebug &debug, const QQmlDirParser::Script &script)
+{
+ const QString output = QStringLiteral("{%1 %2.%3}").
+ arg(script.nameSpace).arg(script.majorVersion).arg(script.minorVersion);
+ return debug << qPrintable(output);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
new file mode 100644
index 0000000000..cff9cb11a4
--- /dev/null
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDIRPARSER_P_H
+#define QQMLDIRPARSER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QUrl>
+#include <QtCore/QHash>
+#include <QtCore/QDebug>
+#include <private/qqmljsengine_p.h>
+#include <private/qv4global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlError;
+class QQmlEngine;
+class Q_QML_PRIVATE_EXPORT QQmlDirParser
+{
+public:
+ QQmlDirParser();
+ ~QQmlDirParser();
+
+ bool parse(const QString &source);
+
+ bool hasError() const;
+ void setError(const QQmlError &);
+ QList<QQmlError> errors(const QString &uri) const;
+
+ QString typeNamespace() const;
+ void setTypeNamespace(const QString &s);
+
+ struct Plugin
+ {
+ Plugin() {}
+
+ Plugin(const QString &name, const QString &path)
+ : name(name), path(path) {}
+
+ QString name;
+ QString path;
+ };
+
+ struct Component
+ {
+ Component() {}
+
+ Component(const QString &typeName, const QString &fileName, int majorVersion, int minorVersion)
+ : typeName(typeName), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion),
+ internal(false), singleton(false) {}
+
+ QString typeName;
+ QString fileName;
+ int majorVersion = 0;
+ int minorVersion = 0;
+ bool internal = false;
+ bool singleton = false;
+ };
+
+ struct Script
+ {
+ Script() {}
+
+ Script(const QString &nameSpace, const QString &fileName, int majorVersion, int minorVersion)
+ : nameSpace(nameSpace), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion) {}
+
+ QString nameSpace;
+ QString fileName;
+ int majorVersion = 0;
+ int minorVersion = 0;
+ };
+
+ QHash<QString,Component> components() const;
+ QHash<QString,Component> dependencies() const;
+ QList<Script> scripts() const;
+ QList<Plugin> plugins() const;
+ bool designerSupported() const;
+
+ struct TypeInfo
+ {
+ TypeInfo() {}
+ TypeInfo(const QString &fileName)
+ : fileName(fileName) {}
+
+ QString fileName;
+ };
+
+ QList<TypeInfo> typeInfos() const;
+
+ QString className() const;
+
+private:
+ bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true);
+ void reportError(quint16 line, quint16 column, const QString &message);
+
+private:
+ QList<QQmlJS::DiagnosticMessage> _errors;
+ QString _typeNamespace;
+ QHash<QString,Component> _components; // multi hash
+ QHash<QString,Component> _dependencies;
+ QList<Script> _scripts;
+ QList<Plugin> _plugins;
+ bool _designerSupported;
+ QList<TypeInfo> _typeInfos;
+ QString _className;
+};
+
+typedef QHash<QString,QQmlDirParser::Component> QQmlDirComponents;
+typedef QList<QQmlDirParser::Script> QQmlDirScripts;
+typedef QList<QQmlDirParser::Plugin> QQmlDirPlugins;
+
+QDebug &operator<< (QDebug &, const QQmlDirParser::Component &);
+QDebug &operator<< (QDebug &, const QQmlDirParser::Script &);
+
+QT_END_NAMESPACE
+
+#endif // QQMLDIRPARSER_P_H
diff --git a/src/qml/qmldirparser/qqmlerror.cpp b/src/qml/qmldirparser/qqmlerror.cpp
new file mode 100644
index 0000000000..5e181f7e27
--- /dev/null
+++ b/src/qml/qmldirparser/qqmlerror.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlerror.h"
+#include "qqmlsourcecoordinate_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qvector.h>
+
+#ifndef QT_NO_QOBJECT
+#include <QtCore/qobject.h>
+#include <QtCore/qpointer.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQmlError
+ \since 5.0
+ \inmodule QtQml
+ \brief The QQmlError class encapsulates a QML error.
+
+ QQmlError includes a textual description of the error, as well
+ as location information (the file, line, and column). The toString()
+ method creates a single-line, human-readable string containing all of
+ this information, for example:
+ \code
+ file:///home/user/test.qml:7:8: Invalid property assignment: double expected
+ \endcode
+
+ You can use qDebug(), qInfo(), or qWarning() to output errors to the console.
+ This method will attempt to open the file indicated by the error
+ and include additional contextual information.
+ \code
+ file:///home/user/test.qml:7:8: Invalid property assignment: double expected
+ y: "hello"
+ ^
+ \endcode
+
+ \sa QQuickView::errors(), QQmlComponent::errors()
+*/
+class QQmlErrorPrivate
+{
+public:
+ QQmlErrorPrivate();
+
+ QUrl url;
+ QString description;
+ quint16 line;
+ quint16 column;
+ QtMsgType messageType;
+#ifndef QT_NO_QOBJECT
+ QPointer<QObject> object;
+#endif
+};
+
+QQmlErrorPrivate::QQmlErrorPrivate()
+: line(0), column(0), messageType(QtMsgType::QtWarningMsg)
+{
+}
+
+/*!
+ Creates an empty error object.
+*/
+QQmlError::QQmlError()
+: d(nullptr)
+{
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QQmlError::QQmlError(const QQmlError &other)
+: d(nullptr)
+{
+ *this = other;
+}
+
+/*!
+ Assigns \a other to this error object.
+*/
+QQmlError &QQmlError::operator=(const QQmlError &other)
+{
+ if (!other.d) {
+ delete d;
+ d = nullptr;
+ } else {
+ if (!d)
+ d = new QQmlErrorPrivate;
+ d->url = other.d->url;
+ d->description = other.d->description;
+ d->line = other.d->line;
+ d->column = other.d->column;
+#ifndef QT_NO_QOBJECT
+ d->object = other.d->object;
+#endif
+ d->messageType = other.d->messageType;
+ }
+ return *this;
+}
+
+/*!
+ \internal
+*/
+QQmlError::~QQmlError()
+{
+ delete d; d = nullptr;
+}
+
+/*!
+ Returns true if this error is valid, otherwise false.
+*/
+bool QQmlError::isValid() const
+{
+ return d != nullptr;
+}
+
+/*!
+ Returns the url for the file that caused this error.
+*/
+QUrl QQmlError::url() const
+{
+ if (d)
+ return d->url;
+ return QUrl();
+}
+
+/*!
+ Sets the \a url for the file that caused this error.
+*/
+void QQmlError::setUrl(const QUrl &url)
+{
+ if (!d)
+ d = new QQmlErrorPrivate;
+ d->url = url;
+}
+
+/*!
+ Returns the error description.
+*/
+QString QQmlError::description() const
+{
+ if (d)
+ return d->description;
+ return QString();
+}
+
+/*!
+ Sets the error \a description.
+*/
+void QQmlError::setDescription(const QString &description)
+{
+ if (!d)
+ d = new QQmlErrorPrivate;
+ d->description = description;
+}
+
+/*!
+ Returns the error line number.
+*/
+int QQmlError::line() const
+{
+ if (d)
+ return qmlSourceCoordinate(d->line);
+ return -1;
+}
+
+/*!
+ Sets the error \a line number.
+*/
+void QQmlError::setLine(int line)
+{
+ if (!d)
+ d = new QQmlErrorPrivate;
+ d->line = qmlSourceCoordinate(line);
+}
+
+/*!
+ Returns the error column number.
+*/
+int QQmlError::column() const
+{
+ if (d)
+ return qmlSourceCoordinate(d->column);
+ return -1;
+}
+
+/*!
+ Sets the error \a column number.
+*/
+void QQmlError::setColumn(int column)
+{
+ if (!d)
+ d = new QQmlErrorPrivate;
+ d->column = qmlSourceCoordinate(column);
+}
+
+#ifndef QT_NO_QOBJECT
+/*!
+ Returns the nearest object where this error occurred.
+ Exceptions in bound property expressions set this to the object
+ to which the property belongs. It will be 0 for all
+ other exceptions.
+ */
+QObject *QQmlError::object() const
+{
+ if (d)
+ return d->object;
+ return nullptr;
+}
+
+/*!
+ Sets the nearest \a object where this error occurred.
+ */
+void QQmlError::setObject(QObject *object)
+{
+ if (!d)
+ d = new QQmlErrorPrivate;
+ d->object = object;
+}
+#endif // QT_NO_QOBJECT
+
+/*!
+ \since 5.9
+
+ Returns the message type.
+ */
+QtMsgType QQmlError::messageType() const
+{
+ if (d)
+ return d->messageType;
+ return QtMsgType::QtWarningMsg;
+}
+
+/*!
+ \since 5.9
+
+ Sets the \a messageType for this message. The message type determines which
+ QDebug handlers are responsible for receiving the message.
+ */
+void QQmlError::setMessageType(QtMsgType messageType)
+{
+ if (!d)
+ d = new QQmlErrorPrivate;
+ d->messageType = messageType;
+}
+
+/*!
+ Returns the error as a human readable string.
+*/
+QString QQmlError::toString() const
+{
+ QString rv;
+
+ QUrl u(url());
+ int l(line());
+
+ if (u.isEmpty() || (u.isLocalFile() && u.path().isEmpty()))
+ rv += QLatin1String("<Unknown File>");
+ else
+ rv += u.toString();
+
+ if (l != -1) {
+ rv += QLatin1Char(':') + QString::number(l);
+
+ int c(column());
+ if (c != -1)
+ rv += QLatin1Char(':') + QString::number(c);
+ }
+
+ rv += QLatin1String(": ") + description();
+
+ return rv;
+}
+
+/*!
+ \relates QQmlError
+ \fn QDebug operator<<(QDebug debug, const QQmlError &error)
+
+ Outputs a human readable version of \a error to \a debug.
+*/
+
+QDebug operator<<(QDebug debug, const QQmlError &error)
+{
+ debug << qPrintable(error.toString());
+
+ QUrl url = error.url();
+
+ if (error.line() > 0 && url.scheme() == QLatin1String("file")) {
+ QString file = url.toLocalFile();
+ QFile f(file);
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray data = f.readAll();
+ QTextStream stream(data, QIODevice::ReadOnly);
+#if QT_CONFIG(textcodec)
+ stream.setCodec("UTF-8");
+#endif
+ const QString code = stream.readAll();
+ const auto lines = code.splitRef(QLatin1Char('\n'));
+
+ if (lines.count() >= error.line()) {
+ const QStringRef &line = lines.at(error.line() - 1);
+ debug << "\n " << line.toLocal8Bit().constData();
+
+ if(error.column() > 0) {
+ int column = qMax(0, error.column() - 1);
+ column = qMin(column, line.length());
+
+ QByteArray ind;
+ ind.reserve(column);
+ for (int i = 0; i < column; ++i) {
+ const QChar ch = line.at(i);
+ if (ch.isSpace())
+ ind.append(ch.unicode());
+ else
+ ind.append(' ');
+ }
+ ind.append('^');
+ debug << "\n " << ind.constData();
+ }
+ }
+ }
+ }
+ return debug;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qmldirparser/qqmlerror.h b/src/qml/qmldirparser/qqmlerror.h
new file mode 100644
index 0000000000..f4221358e9
--- /dev/null
+++ b/src/qml/qmldirparser/qqmlerror.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLERROR_H
+#define QQMLERROR_H
+
+#include <QtQml/qtqmlglobal.h>
+
+#include <QtCore/qurl.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+// ### Qt 6: should this be called QQmlMessage, since it can have a message type?
+class QDebug;
+class QQmlErrorPrivate;
+class Q_QML_EXPORT QQmlError
+{
+public:
+ QQmlError();
+ QQmlError(const QQmlError &);
+ QQmlError &operator=(const QQmlError &);
+ ~QQmlError();
+
+ bool isValid() const;
+
+ QUrl url() const;
+ void setUrl(const QUrl &);
+ QString description() const;
+ void setDescription(const QString &);
+ int line() const;
+ void setLine(int);
+ int column() const;
+ void setColumn(int);
+
+#ifndef QT_NO_QOBJECT
+ QObject *object() const;
+ void setObject(QObject *);
+#endif
+
+ QtMsgType messageType() const;
+ void setMessageType(QtMsgType messageType);
+
+ QString toString() const;
+private:
+ QQmlErrorPrivate *d;
+};
+
+QDebug Q_QML_EXPORT operator<<(QDebug debug, const QQmlError &error);
+
+Q_DECLARE_TYPEINFO(QQmlError, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QQMLERROR_H
diff --git a/src/qml/qmldirparser/qqmlsourcecoordinate_p.h b/src/qml/qmldirparser/qqmlsourcecoordinate_p.h
new file mode 100644
index 0000000000..76ac741ae8
--- /dev/null
+++ b/src/qml/qmldirparser/qqmlsourcecoordinate_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLSOURCECOORDINATE_P_H
+#define QQMLSOURCECOORDINATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+#include <climits>
+
+QT_BEGIN_NAMESPACE
+
+inline quint16 qmlSourceCoordinate(int n)
+{
+ return (n > 0 && n <= static_cast<int>(USHRT_MAX)) ? static_cast<quint16>(n) : 0;
+}
+
+inline int qmlSourceCoordinate(quint16 n)
+{
+ return (n == 0) ? -1 : static_cast<int>(n);
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLSOURCECOORDINATE_P_H