aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qmldom/CMakeLists.txt3
-rw-r--r--src/qmldom/qmldom.pro6
-rw-r--r--src/qmldom/qqmldomconstants_p.h25
-rw-r--r--src/qmldom/qqmldomerrormessage.cpp582
-rw-r--r--src/qmldom/qqmldomerrormessage_p.h195
-rw-r--r--src/qmldom/qqmldomitem.cpp66
-rw-r--r--src/qmldom/qqmldomitem_p.h77
-rw-r--r--src/qmldom/qqmldompath.cpp912
-rw-r--r--src/qmldom/qqmldompath_p.h721
-rw-r--r--tests/auto/qmldom/CMakeLists.txt2
-rw-r--r--tests/auto/qmldom/errormessage/CMakeLists.txt17
-rw-r--r--tests/auto/qmldom/errormessage/errormessage.pro23
-rw-r--r--tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp96
-rw-r--r--tests/auto/qmldom/path/CMakeLists.txt17
-rw-r--r--tests/auto/qmldom/path/path.pro23
-rw-r--r--tests/auto/qmldom/path/tst_qmldompath.cpp229
-rw-r--r--tests/auto/qmldom/qmldom.pro2
17 files changed, 2996 insertions, 0 deletions
diff --git a/src/qmldom/CMakeLists.txt b/src/qmldom/CMakeLists.txt
index 2e7134dd40..ceefec2dae 100644
--- a/src/qmldom/CMakeLists.txt
+++ b/src/qmldom/CMakeLists.txt
@@ -10,6 +10,9 @@ qt_internal_add_module(QmlDom
SOURCES
qqmldom_global.h
qqmldomconstants_p.h
+ qqmldomerrormessage.cpp qqmldomerrormessage_p.h
+ qqmldomitem.cpp qqmldomitem_p.h
+ qqmldompath.cpp qqmldompath_p.h
qqmldomstringdumper.cpp qqmldomstringdumper_p.h
DEFINES
QMLDOM_LIBRARY
diff --git a/src/qmldom/qmldom.pro b/src/qmldom/qmldom.pro
index 282566cc9d..d83d74e273 100644
--- a/src/qmldom/qmldom.pro
+++ b/src/qmldom/qmldom.pro
@@ -6,11 +6,17 @@ CONFIG += minimal_syncqt internal_module generated_privates
DEFINES += QMLDOM_LIBRARY
SOURCES += \
+ $$PWD/qqmldomerrormessage.cpp \
+ $$PWD/qqmldomitem.cpp \
+ $$PWD/qqmldompath.cpp \
$$PWD/qqmldomstringdumper.cpp
HEADERS += \
$$PWD/qqmldom_global.h \
$$PWD/qqmldomconstants_p.h \
+ $$PWD/qqmldomerrormessage_p.h \
+ $$PWD/qqmldomitem_p.h \
+ $$PWD/qqmldompath_p.h \
$$PWD/qqmldomstringdumper_p.h
load(qt_module)
diff --git a/src/qmldom/qqmldomconstants_p.h b/src/qmldom/qqmldomconstants_p.h
index 43cbbdda28..c7e7de5918 100644
--- a/src/qmldom/qqmldomconstants_p.h
+++ b/src/qmldom/qqmldomconstants_p.h
@@ -61,6 +61,31 @@ namespace Dom {
Q_NAMESPACE_EXPORT(QMLDOM_EXPORT)
+enum class PathRoot {
+ Other,
+ Modules,
+ Cpp,
+ Libs,
+ Top,
+ Env,
+ Universe
+};
+Q_ENUM_NS(PathRoot)
+
+enum class PathCurrent {
+ Other,
+ Obj,
+ ObjChain,
+ ScopeChain,
+ Component,
+ Module,
+ Ids,
+ Types,
+ LookupStrict,
+ LookupDynamic,
+ Lookup
+};
+Q_ENUM_NS(PathCurrent)
enum class EscapeOptions{
OuterQuotes,
NoOuterQuotes
diff --git a/src/qmldom/qqmldomerrormessage.cpp b/src/qmldom/qqmldomerrormessage.cpp
new file mode 100644
index 0000000000..e0e9448cc4
--- /dev/null
+++ b/src/qmldom/qqmldomerrormessage.cpp
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 "qqmldomerrormessage_p.h"
+#include "qqmldomitem_p.h"
+#include "qqmldomstringdumper_p.h"
+
+#include <QtCore/QCborMap>
+#include <QtCore/QMutex>
+#include <QtCore/QMutexLocker>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+enum {
+ FatalMsgMaxLen=511
+};
+
+/*!
+\internal
+\macro NewErrorGroup
+
+\param groupId a double qouted string giving the groupId for this group
+
+\brief convenience macro creating a new ErrorGroup and registering its groupId as translatable string
+*/
+
+/*!
+\internal
+\class QQmlJS::Dom::ErrorGroup
+\brief Represents a tag grouping a set of related error messages, it can be used to disable them
+
+Every group has a unique string identifying it (the \l{groupId}), and it should be a string that can
+be translated to get the local name. The best way to acheive this is to create new groups using
+the NewErrorGroup macro.
+ */
+void ErrorGroup::dump(Sink sink) const
+{
+ sink(u"[");
+ sink(groupName());
+ sink(u"]");
+}
+
+void ErrorGroup::dumpId(Sink sink) const
+{
+ sink(u"[");
+ sink(QString(groupId()));
+ sink(u"]");
+}
+
+QLatin1String ErrorGroup::groupId() const
+{
+ return QLatin1String(m_groupId);
+}
+
+QString ErrorGroup::groupName() const
+{
+ return tr(m_groupId);
+}
+
+/*!
+\internal
+\class QQmlJS::Dom::ErrorGroups
+\brief Represents a set of tags grouping a set of related error messages
+
+The simplest way to create new ErrorMessages is to have an ErrorGroups instance,
+and use it to create new ErrorMessages using its debug, warning, error,... methods
+ */
+
+void ErrorGroups::dump(Sink sink) const
+{
+ for (int i = 0; i < groups.length(); ++i)
+ groups.at(i).dump(sink);
+}
+
+void ErrorGroups::dumpId(Sink sink) const
+{
+ for (int i = 0; i < groups.length(); ++i)
+ groups.at(i).dumpId(sink);
+}
+
+QCborArray ErrorGroups::toCbor() const
+{
+ QCborArray res;
+ for (int i = 0; i < groups.length(); ++i)
+ res.append(QCborValue(groups.at(i).groupId()));
+ return res;
+}
+
+/*!
+\internal
+\class QQmlJS::Dom::ErrorMessage
+\brief Represents an error message connected to the dom
+
+The error messages *should* be translated, but they do not need to be pre registered.
+To give a meaningful handling of error messages ErrorMessages have "tags" (ErrorGroup) that are
+grouped toghether in ErrorGroups.
+
+To create an ErrorMessage from scratch the best way is to use one of the methods provided by
+an ErrorGroups object.
+For example create an ErrorGroups called myErrors and use it to create all your errors.
+\code
+static ErrorGroups myErrors(){
+ static ErrorGroups res({NewErrorGroup("StaticAnalysis"), NewErrorGroup("FancyDetector")});
+ return res;
+}
+\endcode
+
+You can preregister the errors giving them a unique name (reverse dns notation is encouraged) with
+the msg function.
+This unique name (errorId) is a const char* (QLatin1String) to integrate better with the tr function.
+Ideally you create variables to store the errorId either by creating variables with plain strings
+that you use to initialize the error messages
+\code
+// in .h file
+constexpr const char *myError0 = "my.company.error0";
+// in some initialization function
+ErrorMessage::msg(myError0, myErrors().warning(tr("Error number 0")));
+\endcode
+or using the result of the msg function
+\code
+// in cpp file
+static auto myError1 = ErrorMessage::msg("my.company.error1", myErrors().warning(tr("Error number 1")));
+static auto myError2 = ErrorMessage::msg("my.company.error2", myErrors().error(tr("Error number 2 on %1")));
+\endcode
+and then use them like this
+\code
+ErrorMessage::load(myError2, QLatin1String("extra info")).handle(errorHandler);
+\endcode
+or using directly the string (more error prone)
+\code
+errorHandler(ErrorMessage::load(QLatin1String("my.company.error1")));
+\endcode
+
+The \l{withItem} method can be used to set the path file and location if not aready set.
+ */
+
+ErrorMessage ErrorGroups::errorMessage(Dumper msg, ErrorLevel level, Path element, QString canonicalFilePath, SourceLocation location) const
+{
+ if (level == ErrorLevel::Fatal)
+ fatal(msg, element, canonicalFilePath, location);
+ return ErrorMessage(dumperToString(msg), *this, level, element, canonicalFilePath, location);
+}
+
+ErrorMessage ErrorGroups::errorMessage(const DiagnosticMessage &msg, Path element, QString canonicalFilePath) const
+{
+ ErrorMessage res(*this, msg, element, canonicalFilePath);
+ if (!res.location.isValid() && (res.location.startLine != 0 || res.location.startColumn != 0)) {
+ res.location.offset = -1;
+ res.location.length = 1;
+ }
+ return res;
+}
+
+void ErrorGroups::fatal(Dumper msg, Path element, QStringView canonicalFilePath, SourceLocation location) const
+{
+ enum { FatalMsgMaxLen = 1023 };
+ char buf[FatalMsgMaxLen+1];
+ int ibuf = 0;
+ auto sink = [&ibuf, &buf](QStringView s) {
+ int is = 0;
+ while (ibuf < FatalMsgMaxLen && is < s.length()) {
+ QChar c = s.at(is);
+ if (c == QChar::fromLatin1('\n') || c == QChar::fromLatin1('\r') || (c >= QChar::fromLatin1(' ') && c <= QChar::fromLatin1('~')))
+ buf[ibuf++] = c.toLatin1();
+ else
+ buf[ibuf++] = '~';
+ ++is;
+ }
+ };
+ if (!canonicalFilePath.isEmpty()) {
+ sink(canonicalFilePath);
+ sink(u":");
+ }
+ if (location.length) {
+ sinkInt(sink, location.startLine);
+ sink(u":");
+ sinkInt(sink, location.startColumn);
+ sink(u":");
+ }
+ dump(sink);
+ msg(sink);
+ if (element.length()>0) {
+ sink(u" for ");
+ element.dump(sink);
+ }
+ buf[ibuf] = 0;
+ qFatal("%s", buf);
+}
+
+ErrorMessage ErrorGroups::debug(QString message) const
+{
+ return ErrorMessage(message, *this, ErrorLevel::Debug);
+}
+
+ErrorMessage ErrorGroups::debug(Dumper message) const
+{
+ return ErrorMessage(dumperToString(message), *this, ErrorLevel::Debug);
+}
+
+ErrorMessage ErrorGroups::info(QString message) const
+{
+ return ErrorMessage(message, *this, ErrorLevel::Info);
+}
+
+ErrorMessage ErrorGroups::info(Dumper message) const
+{
+ return ErrorMessage(dumperToString(message), *this, ErrorLevel::Info);
+}
+
+ErrorMessage ErrorGroups::hint(QString message) const
+{
+ return ErrorMessage(message, *this, ErrorLevel::Hint);
+}
+
+ErrorMessage ErrorGroups::hint(Dumper message) const
+{
+ return ErrorMessage(dumperToString(message), *this, ErrorLevel::Hint);
+}
+
+ErrorMessage ErrorGroups::maybeWarning(QString message) const
+{
+ return ErrorMessage(message, *this, ErrorLevel::MaybeWarning);
+}
+
+ErrorMessage ErrorGroups::maybeWarning(Dumper message) const
+{
+ return ErrorMessage(dumperToString(message), *this, ErrorLevel::MaybeWarning);
+}
+
+ErrorMessage ErrorGroups::warning(QString message) const
+{
+ return ErrorMessage(message, *this, ErrorLevel::Warning);
+}
+
+ErrorMessage ErrorGroups::warning(Dumper message) const
+{
+ return ErrorMessage(dumperToString(message), *this, ErrorLevel::Warning);
+}
+
+ErrorMessage ErrorGroups::maybeError(QString message) const
+{
+ return ErrorMessage(message, *this, ErrorLevel::MaybeError);
+}
+
+ErrorMessage ErrorGroups::maybeError(Dumper message) const
+{
+ return ErrorMessage(dumperToString(message), *this, ErrorLevel::MaybeError);
+}
+
+ErrorMessage ErrorGroups::error(QString message) const
+{
+ return ErrorMessage(message, *this, ErrorLevel::Error);
+}
+
+ErrorMessage ErrorGroups::error(Dumper message) const
+{
+ return ErrorMessage(dumperToString(message), *this, ErrorLevel::Error);
+}
+
+int ErrorGroups::cmp(const ErrorGroups &o1, const ErrorGroups &o2)
+{
+ auto &g1 = o1.groups;
+ auto &g2 = o2.groups;
+ if (g1.length() < g2.length())
+ return -1;
+ if (g1.length() < g2.length())
+ return 1;
+ for (int i = 0; i < g1.length(); ++i) {
+ int c = std::strcmp(g1.at(i).groupId().data(), g2.at(i).groupId().data());
+ if (c != 0)
+ return c;
+ }
+ return 0;
+}
+
+ErrorMessage::ErrorMessage(QString msg, ErrorGroups errorGroups, Level level, Path element, QString canonicalFilePath, SourceLocation location, QLatin1String errorId):
+ errorId(errorId), message(msg), errorGroups(errorGroups), level(level), path(element), file(canonicalFilePath), location(location)
+{
+ if (level == Level::Fatal) // we should not end up here, it should have been handled at a higher level already
+ errorGroups.fatal(msg, element, canonicalFilePath, location);
+}
+
+ErrorMessage::ErrorMessage(ErrorGroups errorGroups, const DiagnosticMessage &msg, Path element,
+ QString canonicalFilePath, QLatin1String errorId):
+ errorId(errorId), message(msg.message), errorGroups(errorGroups),
+ level(errorLevelFromQtMsgType(msg.type)), path(element), file(canonicalFilePath), location(msg.loc)
+{
+ if (level == Level::Fatal) // we should not end up here, it should have been handled at a higher level already
+ errorGroups.fatal(msg.message, element, canonicalFilePath, location);
+}
+
+
+static QBasicMutex *registryMutex()
+{
+ static QBasicMutex rMutex{};
+ return &rMutex;
+}
+
+
+static ErrorGroups myErrors()
+{
+ static ErrorGroups g = {{NewErrorGroup("ErrorMessage")}};
+ return g;
+}
+
+struct StorableMsg {
+ StorableMsg():
+ msg(QStringLiteral(u"dummy"), myErrors(), ErrorLevel::Error)
+ {}
+
+ StorableMsg(const ErrorMessage &e):
+ msg(e)
+ {}
+
+ ErrorMessage msg;
+};
+
+static QHash<QLatin1String, StorableMsg> &registry()
+{
+ static QHash<QLatin1String, StorableMsg> r;
+ return r;
+}
+
+QLatin1String ErrorMessage::msg(const char *errorId, ErrorMessage err)
+{
+ return msg(QLatin1String(errorId), err);
+}
+
+QLatin1String ErrorMessage::msg(QLatin1String errorId, ErrorMessage err)
+{
+ bool doubleRegister = false;
+ ErrorMessage old = myErrors().debug(u"dummy");
+ {
+ QMutexLocker l(registryMutex());
+ auto &r = registry();
+ if (r.contains(err.errorId)) {
+ old = r[err.errorId].msg;
+ doubleRegister = true;
+ }
+ r[errorId] = StorableMsg{err.withErrorId(errorId)};
+ }
+ if (doubleRegister)
+ defaultErrorHandler(myErrors().warning(tr("Double registration of error %1: (%2) vs (%3)").arg(errorId, err.withErrorId(errorId).toString(), old.toString())));
+ return errorId;
+}
+
+void ErrorMessage::visitRegisteredMessages(std::function<bool (ErrorMessage)> visitor)
+{
+ QHash<QLatin1String, StorableMsg> r;
+ {
+ QMutexLocker l(registryMutex());
+ r = registry();
+ }
+ auto it = r.cbegin();
+ auto end = r.cend();
+ while (it != end) {
+ visitor(it->msg);
+ ++it;
+ }
+}
+
+ErrorMessage ErrorMessage::load(QLatin1String errorId)
+{
+ ErrorMessage res = myErrors().error([errorId](Sink s){
+ s(u"Unregistered error ");
+ s(QString(errorId)); });
+ {
+ QMutexLocker l(registryMutex());
+ res = registry().value(errorId,res).msg;
+ }
+ return res;
+}
+
+ErrorMessage ErrorMessage::load(const char *errorId)
+{
+ return load(QLatin1String(errorId));
+}
+
+ErrorMessage &ErrorMessage::withErrorId(QLatin1String errorId)
+{
+ this->errorId = errorId;
+ return *this;
+}
+
+ErrorMessage &ErrorMessage::withPath(const Path &path)
+{
+ this->path = path;
+ return *this;
+}
+
+ErrorMessage &ErrorMessage::withFile(QString f)
+{
+ file=f;
+ return *this;
+}
+
+ErrorMessage &ErrorMessage::withFile(QStringView f)
+{
+ file = f.toString();
+ return *this;
+}
+
+ErrorMessage &ErrorMessage::withLocation(SourceLocation loc)
+{
+ location = loc;
+ return *this;
+}
+
+ErrorMessage &ErrorMessage::withItem(DomItem el)
+{
+ if (path.length() == 0)
+ path = el.canonicalPath();
+ if (file.isEmpty())
+ file = el.canonicalFilePath();
+ if (!location.isValid())
+ location = el.location();
+ return *this;
+}
+
+ErrorMessage ErrorMessage::handle(const ErrorHandler &errorHandler)
+{
+ if (errorHandler)
+ errorHandler(*this);
+ else
+ defaultErrorHandler(*this);
+ return *this;
+}
+
+void ErrorMessage::dump(Sink sink) const
+{
+ if (!file.isEmpty()) {
+ sink(file);
+ sink(u":");
+ }
+ if (location.length) {
+ sinkInt(sink, location.startLine);
+ sink(u":");
+ sinkInt(sink, location.startColumn);
+ sink(u": ");
+ }
+ errorGroups.dump(sink);
+ sink(u" ");
+ dumpErrorLevel(sink, level);
+ if (! errorId.isEmpty()) {
+ sink(u" ");
+ sink(QString(errorId));
+ }
+ sink(u": ");
+ sink(message);
+ if (path.length()>0) {
+ sink(u" for ");
+ path.dump(sink);
+ }
+}
+
+QString ErrorMessage::toString() const
+{
+ return dumperToString([this](Sink sink){ this->dump(sink); });
+}
+
+QCborMap ErrorMessage::toCbor() const
+{
+ return QCborMap({
+ {QStringLiteral(u"errorId"),errorId},
+ {QStringLiteral(u"message"), message},
+ {QStringLiteral(u"errorGroups"), errorGroups.toCbor()},
+ {QStringLiteral(u"level"), int(level)},
+ {QStringLiteral(u"path"), path.toString()},
+ {QStringLiteral(u"file"), file},
+ {QStringLiteral(u"location"), QCborMap({
+ {QStringLiteral(u"offset"),location.offset},
+ {QStringLiteral(u"length"),location.length},
+ {QStringLiteral(u"startLine"),location.startLine},
+ {QStringLiteral(u"startColumn"),location.startColumn}})}
+ });
+}
+
+/*!
+ * \internal
+ * \brief writes an ErrorMessage to QDebug
+ * \param error the error to write
+ */
+void errorToQDebug(const ErrorMessage &error)
+{
+ dumperToQDebug([&error](Sink s){ error.dump(s); }, error.level);
+}
+
+/*!
+ * \internal
+ * \brief Error handler that ignores all errors (excluding fatal ones)
+ */
+void silentError(const ErrorMessage &)
+{
+}
+
+void errorHandlerHandler(const ErrorMessage &msg, ErrorHandler *h = nullptr)
+{
+ static ErrorHandler handler = &errorToQDebug;
+ if (h) {
+ handler = *h;
+ } else {
+ handler(msg);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Calls the default error handler (by default errorToQDebug)
+ */
+void defaultErrorHandler(const ErrorMessage &error)
+{
+ errorHandlerHandler(error);
+}
+
+/*!
+ * \internal
+ * \brief Sets the default error handler
+ */
+void setDefaultErrorHandler(ErrorHandler h)
+{
+ errorHandlerHandler(ErrorMessage(QString(), ErrorGroups({})), &h);
+}
+
+ErrorLevel errorLevelFromQtMsgType(QtMsgType msgType)
+{
+ switch (msgType) {
+ case QtFatalMsg:
+ return ErrorLevel::Fatal;
+ case QtCriticalMsg:
+ return ErrorLevel::Error;
+ case QtWarningMsg:
+ return ErrorLevel::Warning;
+ case QtInfoMsg:
+ return ErrorLevel::Info;
+ case QtDebugMsg:
+ return ErrorLevel::Debug;
+ default:
+ return ErrorLevel::Error;
+ }
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomerrormessage_p.h b/src/qmldom/qqmldomerrormessage_p.h
new file mode 100644
index 0000000000..f17c009835
--- /dev/null
+++ b/src/qmldom/qqmldomerrormessage_p.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 ERRORMESSAGE_H
+#define ERRORMESSAGE_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 "qqmldom_global.h"
+#include "qqmldomstringdumper_p.h"
+#include "qqmldompath_p.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QString>
+#include <QtCore/QCborArray>
+#include <QtCore/QCborMap>
+#include <QtQml/private/qqmljsdiagnosticmessage_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+QMLDOM_EXPORT ErrorLevel errorLevelFromQtMsgType(QtMsgType msgType);
+
+class ErrorGroups;
+class DomItem;
+using std::function;
+
+#define NewErrorGroup(name) QQmlJS::Dom::ErrorGroup(QT_TRANSLATE_NOOP("ErrorGroup", name))
+
+class QMLDOM_EXPORT ErrorGroup {
+ Q_GADGET
+ Q_DECLARE_TR_FUNCTIONS(ErrorGroup)
+public:
+ constexpr ErrorGroup(const char *groupId):
+ m_groupId(groupId)
+ {}
+
+
+ void dump(Sink sink) const;
+ void dumpId(Sink sink) const;
+
+ QLatin1String groupId() const;
+ QString groupName() const;
+ private:
+ const char *m_groupId;
+};
+
+class QMLDOM_EXPORT ErrorGroups{
+ Q_GADGET
+public:
+ void dump(Sink sink) const;
+ void dumpId(Sink sink) const;
+ QCborArray toCbor() const;
+
+ [[nodiscard]] ErrorMessage errorMessage(Dumper msg, ErrorLevel level, Path element = Path(), QString canonicalFilePath = QString(), SourceLocation location = SourceLocation()) const;
+ [[nodiscard]] ErrorMessage errorMessage(const DiagnosticMessage &msg, Path element = Path(), QString canonicalFilePath = QString()) const;
+
+ void fatal(Dumper msg, Path element = Path(), QStringView canonicalFilePath = u"", SourceLocation location = SourceLocation()) const;
+
+ [[nodiscard]] ErrorMessage debug(QString message) const;
+ [[nodiscard]] ErrorMessage debug(Dumper message) const;
+ [[nodiscard]] ErrorMessage info(QString message) const;
+ [[nodiscard]] ErrorMessage info(Dumper message) const;
+ [[nodiscard]] ErrorMessage hint(QString message) const;
+ [[nodiscard]] ErrorMessage hint(Dumper message) const;
+ [[nodiscard]] ErrorMessage maybeWarning(QString message) const;
+ [[nodiscard]] ErrorMessage maybeWarning(Dumper message) const;
+ [[nodiscard]] ErrorMessage warning(QString message) const;
+ [[nodiscard]] ErrorMessage warning(Dumper message) const;
+ [[nodiscard]] ErrorMessage maybeError(QString message) const;
+ [[nodiscard]] ErrorMessage maybeError(Dumper message) const;
+ [[nodiscard]] ErrorMessage error(QString message) const;
+ [[nodiscard]] ErrorMessage error(Dumper message) const;
+
+ static int cmp(const ErrorGroups &g1, const ErrorGroups &g2);
+
+ QVector<ErrorGroup> groups;
+};
+
+inline bool operator==(const ErrorGroups& lhs, const ErrorGroups& rhs){ return ErrorGroups::cmp(lhs,rhs) == 0; }
+inline bool operator!=(const ErrorGroups& lhs, const ErrorGroups& rhs){ return ErrorGroups::cmp(lhs,rhs) != 0; }
+inline bool operator< (const ErrorGroups& lhs, const ErrorGroups& rhs){ return ErrorGroups::cmp(lhs,rhs) < 0; }
+inline bool operator> (const ErrorGroups& lhs, const ErrorGroups& rhs){ return ErrorGroups::cmp(lhs,rhs) > 0; }
+inline bool operator<=(const ErrorGroups& lhs, const ErrorGroups& rhs){ return ErrorGroups::cmp(lhs,rhs) <= 0; }
+inline bool operator>=(const ErrorGroups& lhs, const ErrorGroups& rhs){ return ErrorGroups::cmp(lhs,rhs) >= 0; }
+
+class QMLDOM_EXPORT ErrorMessage { // reuse Some of the other DiagnosticMessages?
+ Q_GADGET
+ Q_DECLARE_TR_FUNCTIONS(ErrorMessage)
+public:
+ using Level = ErrorLevel;
+ // error registry (usage is optional)
+ static QLatin1String msg(const char *errorId, ErrorMessage err);
+ static QLatin1String msg(QLatin1String errorId, ErrorMessage err);
+ static void visitRegisteredMessages(std::function<bool(ErrorMessage)> visitor);
+ [[nodiscard]] static ErrorMessage load(QLatin1String errorId);
+ [[nodiscard]] static ErrorMessage load(const char *errorId);
+ template<typename... T>
+ [[nodiscard]] static ErrorMessage load(QLatin1String errorId, T... args){
+ ErrorMessage res = load(errorId);
+ res.message = res.message.arg(args...);
+ return res;
+ }
+
+ ErrorMessage(QString message, ErrorGroups errorGroups, Level level = Level::Warning, Path path = Path(), QString file = QString(), SourceLocation location = SourceLocation(), QLatin1String errorId = QLatin1String(""));
+ ErrorMessage(ErrorGroups errorGroups, const DiagnosticMessage &msg, Path path = Path(), QString file = QString(), QLatin1String errorId = QLatin1String(""));
+
+ [[nodiscard]] ErrorMessage &withErrorId(QLatin1String errorId);
+ [[nodiscard]] ErrorMessage &withPath(const Path &);
+ [[nodiscard]] ErrorMessage &withFile(QString);
+ [[nodiscard]] ErrorMessage &withFile(QStringView);
+ [[nodiscard]] ErrorMessage &withLocation(SourceLocation);
+ [[nodiscard]] ErrorMessage &withItem(DomItem);
+
+ ErrorMessage handle(const ErrorHandler &errorHandler=nullptr);
+
+ void dump(Sink s) const;
+ QString toString() const;
+ QCborMap toCbor() const;
+
+ QLatin1String errorId;
+ QString message;
+ ErrorGroups errorGroups;
+ Level level;
+ Path path;
+ QString file;
+ SourceLocation location;
+};
+
+inline bool operator !=(const ErrorMessage &e1, const ErrorMessage &e2) {
+ return e1.errorId != e2.errorId || e1.message != e2.message || e1.errorGroups != e2.errorGroups
+ || e1.level != e2.level || e1.path != e2.path || e1.file != e2.file
+ || e1.location.startLine != e2.location.startLine || e1.location.offset != e2.location.offset
+ || e1.location.startColumn != e2.location.startColumn || e1.location.length != e2.location.length;
+}
+inline bool operator ==(const ErrorMessage &e1, const ErrorMessage &e2) {
+ return !(e1 != e2);
+}
+
+QMLDOM_EXPORT void silentError(const ErrorMessage &);
+QMLDOM_EXPORT void errorToQDebug(const ErrorMessage &);
+
+QMLDOM_EXPORT void defaultErrorHandler(const ErrorMessage &);
+QMLDOM_EXPORT void setDefaultErrorHandler(ErrorHandler h);
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
+#endif // ERRORMESSAGE_H
diff --git a/src/qmldom/qqmldomitem.cpp b/src/qmldom/qqmldomitem.cpp
new file mode 100644
index 0000000000..e572c52f8f
--- /dev/null
+++ b/src/qmldom/qqmldomitem.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 "qqmldomitem_p.h"
+#include "qqmldompath_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+Path DomItem::canonicalPath() const
+{
+ return Path();
+}
+
+QString DomItem::canonicalFilePath() const
+{
+ return QString();
+}
+
+SourceLocation DomItem::location() const
+{
+ return SourceLocation();
+}
+
+DomItem::DomItem()
+{
+}
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomitem_p.h b/src/qmldom/qqmldomitem_p.h
new file mode 100644
index 0000000000..59199bd9af
--- /dev/null
+++ b/src/qmldom/qqmldomitem_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 QQMLDOMITEM_P_H
+#define QQMLDOMITEM_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 "qqmldom_global.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+// we didn't have enough 'O's to properly name everything...
+namespace Dom {
+class Path;
+
+class DomItem {
+public:
+ Path canonicalPath() const;
+ QString canonicalFilePath() const;
+ SourceLocation location() const;
+
+ DomItem();
+};
+
+}
+}
+QT_END_NAMESPACE
+
+#endif // QQMLDOMITEM_P_H
diff --git a/src/qmldom/qqmldompath.cpp b/src/qmldom/qqmldompath.cpp
new file mode 100644
index 0000000000..871fb20f5c
--- /dev/null
+++ b/src/qmldom/qqmldompath.cpp
@@ -0,0 +1,912 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 "qqmldompath_p.h"
+#include "qqmldomitem_p.h"
+#include "qqmldomerrormessage_p.h"
+#include <QtCore/QDebug>
+#include <QtCore/QTextStream>
+#include <QtCore/QChar>
+
+#include <cstdint>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+class ErrorMessage;
+
+namespace PathEls {
+
+/*!
+\internal
+\class QQmljs::Dom::QmlPath::Path
+
+\brief Represents an immutable JsonPath like path in the Qml code model (from a DomItem to another
+ DomItem)
+
+It can be created either from a string, with the path static functions
+or by modifying an existing path
+\code
+Path qmlFilePath =
+ Path::fromString(u"$env.qmlFilesByPath[\"/path/to/file\"]");
+Path imports = qmlFilePath.subField(u"imports")
+Path currentComponentImports = Path::current(u"component").subField(u"imports");
+\endcode
+
+This is a way to refer to elements in the Dom models that is not dependent from the source location,
+and thus can be used also be used in visual tools.
+A Path is quite stable toward reallocations or changes in the Dom model, and accessing it is safe
+even when "dangling", thus it is a good long term reference to an element in the Dom model.
+
+Path objects are a value type that have a shared pointer to extra data if needed, thus one should
+use them as value objects.
+The implementation has still optimization potential, but the behavior for the user should be already
+the final one.
+
+Path is both a range, and a single element (a bit like strings and characters in python).
+
+The root contexts are:
+\list
+\li \l{$modules} All the known modules (even not imported), this is a global, rename independent
+ reference
+\li \l{$cpp} The Cpp names (namespaces, and Cpp types) visible in the current component
+\li \l{$libs} The plugins/libraries and their contents
+\li \l{$top} A top level entry in the DOM model, either $env or $universe (stepping in the universe
+ one looses the reference to its environment)
+\li \l{$env} The environment containing the currently available modules, i.e. the top level entry in
+ the DOM model
+\li \l{$universe} The dom unverse used by ths environment, and potentially shared with others that
+ contains all the known parse entries, and also the latest, potentially invalid entries
+\li \l{$} ? undecided, one the previous ones?
+\endlist
+
+The current contexts are:
+\list
+\li \l{@obj} The current object (if in a map or list goes up until it is in the current object) .
+\li \l{@component} The root object of the current component.
+\li \l{@module} The current module instantiation.
+\li \l{@ids} The ids in the current component.
+\li \l{@types} All the types in the current component (reachable through imports, respecting renames)
+\li \l{@instantiation} The current instantiation, either component instantiation or module
+ instantiation
+\li \l{@lookupStrict} The strict lookup inside the current object: localJS, ids, properties, proto
+ properties, component, its properties, global context, oterwise error
+\li \l{@lookupDynamic} The default lookup inside the current object: localJS, ids, properties, proto
+ properties, component, its properties, global context, ..
+\li \l{@lookup} Either lookupStrict or lookupDynamic depending on the current component and context.
+\li \l{@} ? undecided, one the previous ones
+\endlist
+ */
+
+void Base::dump(Sink sink) const {
+ if (hasSquareBrackets())
+ sink(u"[");
+ sink(name());
+ if (hasSquareBrackets())
+ sink(u"]");
+}
+
+Filter::Filter(function<bool(DomItem)> f, QStringView filterDescription): filterFunction(f), filterDescription(filterDescription) {}
+
+QString Filter::name() const {
+ return QLatin1String("?(%1)").arg(filterDescription); }
+
+bool Filter::checkName(QStringView s) const
+{
+ return s.startsWith(u"?(")
+ && s.mid(2, s.length()-3) == filterDescription
+ && s.endsWith(u")");
+}
+
+enum class ParserState{
+ Start,
+ IndexOrKey,
+ End
+};
+
+} // namespace PathEls
+
+using namespace PathEls;
+
+PathComponent::~PathComponent(){
+}
+
+int PathComponent::cmp(const PathComponent &p1, const PathComponent &p2)
+{
+ int k1 = static_cast<int>(p1.kind());
+ int k2 = static_cast<int>(p2.kind());
+ if (k1 < k2)
+ return -1;
+ if (k1 > k2)
+ return 1;
+ switch (p1.kind()) {
+ case Kind::Empty:
+ return 0;
+ case Kind::Field:
+ return p1.data.field.fieldName.compare(p2.data.field.fieldName);
+ case Kind::Index:
+ if (p1.data.index.indexValue < p2.data.index.indexValue)
+ return -1;
+ if (p1.data.index.indexValue > p2.data.index.indexValue)
+ return 1;
+ return 0;
+ case Kind::Key:
+ return p1.data.key.keyValue.compare(p2.data.key.keyValue);
+ case Kind::Root:
+ {
+ int c = int(p1.data.root.contextKind) - int(p2.data.root.contextKind);
+ if (c != 0)
+ return c;
+ return p1.data.root.contextName.compare(p2.data.root.contextName);
+ }
+ case Kind::Current:
+ {
+ int c = int(p1.data.current.contextKind) - int(p2.data.current.contextKind);
+ if (c != 0)
+ return c;
+ return p1.data.current.contextName.compare(p2.data.current.contextName);
+ }
+ case Kind::Any:
+ return 0;
+ case Kind::Filter:
+ {
+ int c = p1.data.filter.filterDescription.compare(p2.data.filter.filterDescription);
+ if (c != 0)
+ return c;
+ if (p1.data.filter.filterDescription.startsWith(u"<")) {
+ // assuming non comparable native code (target comparison is not portable)
+ auto pp1 = &p1;
+ auto pp2 = &p2;
+ if (pp1 < pp2)
+ return -1;
+ if (pp1 > pp2)
+ return 1;
+ }
+ return 0;
+ }
+ }
+ Q_ASSERT(false && "unexpected PathComponent in PathComponent::cmp");
+ return 0;
+}
+
+PathComponent Path::component(int i) const
+{
+ if (i < 0)
+ i += m_length;
+ if (i >= m_length || i < 0) {
+ Q_ASSERT(false && "index out of bounds");
+ return Component();
+ }
+ i = i - m_length - m_endOffset;
+ auto data = m_data.get();
+ while (data) {
+ i += data->components.length();
+ if (i >= 0)
+ return data->components.at(i);
+ data = data->parent.get();
+ }
+ Q_ASSERT(false && "Invalid data reached while resolving a seemengly valid index in Path (inconsisten Path object)");
+ return Component();
+}
+
+Path Path::operator[](int i) const
+{
+ return mid(i,1);
+}
+
+QQmlJS::Dom::Path::operator bool() const
+{
+ return length() != 0;
+}
+
+PathRoot Path::headRoot() const
+{
+ auto comp = component(0);
+ if (Root const * r = comp.base()->asRoot())
+ return r->contextKind;
+ return PathRoot::Other;
+}
+
+PathCurrent Path::headCurrent() const
+{
+ auto comp = component(0);
+ if (Current const * c = comp.base()->asCurrent())
+ return c->contextKind;
+ return PathCurrent::Other;
+}
+
+Path::Kind Path::headKind() const
+{
+ return component(0).kind();
+}
+
+QString Path::headName() const
+{
+ return component(0).name();
+}
+
+bool Path::checkHeadName(QStringView name) const
+{
+ return component(0).checkName(name);
+}
+
+index_type Path::headIndex(index_type defaultValue) const
+{
+ return component(0).index(defaultValue);
+}
+
+function<bool (DomItem)> Path::headFilter() const
+{
+ auto comp = component(0);
+ if (Filter const * f = comp.base()->asFilter()) {
+ return f->filterFunction;
+ }
+ return {};
+}
+
+Path Path::head() const
+{
+ return mid(0,1);
+}
+
+Path Path::last() const
+{
+ return mid(m_length-1, 1);
+}
+
+Source Path::split() const
+{
+ int i = length();
+ while (i>0) {
+ const PathEls::PathComponent &c=component(--i);
+ if (c.kind() == Kind::Field || c.kind() == Kind::Root || c.kind() == Kind::Current) {
+ return Source{mid(0, i), mid(i)};
+ }
+ }
+ return Source{Path(), *this};
+}
+
+bool inQString(QStringView el, QString base)
+{
+ if (quintptr(base.constData()) > quintptr(el.begin())
+ || quintptr(base.constData() + base.size()) < quintptr(el.begin()))
+ return false;
+ ptrdiff_t diff = base.constData() - el.begin();
+ return diff >= 0 && diff < base.size();
+}
+
+bool inQString(QString el, QString base)
+{
+ if (quintptr(base.constData()) > quintptr(el.constData())
+ || quintptr(base.constData() + base.size()) < quintptr(el.constData()))
+ return false;
+ ptrdiff_t diff = base.constData() - el.constData();
+ return diff >= 0 && diff < base.size() && diff + el.size() < base.size();
+}
+
+Path Path::fromString(QStringView s, ErrorHandler errorHandler)
+{
+ if (s.isEmpty())
+ return Path();
+ int len=1;
+ const QChar dot = QChar::fromLatin1('.');
+ const QChar lsBrace = QChar::fromLatin1('[');
+ const QChar rsBrace = QChar::fromLatin1(']');
+ const QChar dollar = QChar::fromLatin1('$');
+ const QChar at = QChar::fromLatin1('@');
+ const QChar quote = QChar::fromLatin1('"');
+ const QChar backslash = QChar::fromLatin1('\\');
+ const QChar underscore = QChar::fromLatin1('_');
+ const QChar tilda = QChar::fromLatin1('~');
+ for (int i=0; i < s.length(); ++i)
+ if (s.at(i) == lsBrace || s.at(i) == dot)
+ ++len;
+ QVector<Component> components;
+ components.reserve(len);
+ int i = 0;
+ int i0 = 0;
+ ParserState state = ParserState::Start;
+ QStringList strVals;
+ while (i < s.length()) {
+ // skip space
+ while (i < s.length() && s.at(i).isSpace())
+ ++i;
+ if (i >= s.length())
+ break;
+ QChar c = s.at(i++);
+ switch (state) {
+ case ParserState::Start:
+ if (c == dollar) {
+ i0 = i;
+ while (i < s.length() && s.at(i).isLetterOrNumber()){
+ ++i;
+ }
+ components.append(Component(Root(s.mid(i0,i-i0))));
+ state = ParserState::End;
+ } else if (c == at) {
+ i0 = i;
+ while (i < s.length() && s.at(i).isLetterOrNumber()){
+ ++i;
+ }
+ components.append(Component(Current(s.mid(i0,i-i0))));
+ state = ParserState::End;
+ } else if (c.isLetter()) {
+ myErrors().warning(tr("Field expressions should start with dot even a the start of the path %1.")
+ .arg(s)).handle(errorHandler);
+ return Path();
+ } else {
+ --i;
+ state = ParserState::End;
+ }
+ break;
+ case ParserState::IndexOrKey:
+ if (c.isDigit()) {
+ i0 = i-1;
+ while (i < s.length() && s.at(i).isDigit())
+ ++i;
+ bool ok;
+ components.append(Component(static_cast<index_type>(s.mid(i0,i-i0).toString()
+ .toLongLong(&ok))));
+ if (!ok) {
+ myErrors().warning(tr("Error extracting integer from '%1' at char %2.")
+ .arg(s.mid(i0,i-i0))
+ .arg(QString::number(i0))).handle(errorHandler);
+ }
+ } else if (c.isLetter() || c == tilda || c == underscore) {
+ i0 = i-1;
+ while (i < s.length() && (s.at(i).isLetterOrNumber() || s.at(i) == underscore || s.at(i) == tilda))
+ ++i;
+ components.append(Component(Key(s.mid(i0, i-i0))));
+ } else if (c == quote) {
+ i0 = i;
+ QString strVal;
+ QStringView key;
+ bool needsConversion = false;
+ bool properEnd = false;
+ while (i < s.length()) {
+ c = s.at(i);
+ if (c == quote) {
+ properEnd = true;
+ break;
+ } else if (c == backslash) {
+ needsConversion = true;
+ strVal.append(s.mid(i0, i - i0).toString());
+ c = s.at(++i);
+ i0 = i + 1;
+ if (c == QChar::fromLatin1('n'))
+ strVal.append(QChar::fromLatin1('\n'));
+ else if (c == QChar::fromLatin1('r'))
+ strVal.append(QChar::fromLatin1('\r'));
+ else if (c == QChar::fromLatin1('t'))
+ strVal.append(QChar::fromLatin1('\t'));
+ else
+ strVal.append(c);
+ }
+ ++i;
+ }
+ if (properEnd) {
+ if (needsConversion) {
+ strVal.append(s.mid(i0, i - i0).toString());
+ strVals.append(strVal);
+ key=strVal;
+ } else {
+ key = s.mid(i0, i - i0);
+ }
+ ++i;
+ } else {
+ myErrors().error(tr("Unclosed quoted string at char %1.")
+ .arg(QString::number(i - 1))).handle(errorHandler);
+ return Path();
+ }
+ components.append(Key(key));
+ } else if (c == QChar::fromLatin1('*')) {
+ components.append(Component(Any()));
+ } else if (c == QChar::fromLatin1('?')) {
+ while (i < s.length() && s.at(i).isSpace())
+ ++i;
+ if (i >= s.length() || s.at(i) != QChar::fromLatin1('(')) {
+ myErrors().error(tr("Expected a brace in filter after the question mark (at char %1).")
+ .arg(QString::number(i))).handle(errorHandler);
+ return Path();
+ }
+ i0 = ++i;
+ while (i < s.length() && s.at(i) != QChar::fromLatin1(')')) ++i; // check matching braces when skipping??
+ if (i >= s.length() || s.at(i) != QChar::fromLatin1(')')) {
+ myErrors().error(tr("Expected a closing brace in filter after the question mark (at char %1).")
+ .arg(QString::number(i))).handle(errorHandler);
+ return Path();
+ }
+ //auto filterDesc = s.mid(i0,i-i0);
+ ++i;
+ myErrors().error(tr("Filter from string not yet implemented.")).handle(errorHandler);
+ return Path();
+ } else {
+ myErrors().error(tr("Unexpected character '%1' after square braket at %2.")
+ .arg(c).arg(i-1)).handle(errorHandler);
+ return Path();
+ }
+ while (i < s.length() && s.at(i).isSpace()) ++i;
+ if (i >= s.length() || s.at(i) != rsBrace) {
+ myErrors().error(tr("square braces misses closing brace at char %1.")
+ .arg(QString::number(i))).handle(errorHandler);
+ return Path();
+ } else {
+ ++i;
+ }
+ state = ParserState::End;
+ break;
+ case ParserState::End:
+ if (c == dot) {
+ while (i < s.length() && s.at(i).isSpace()) ++i;
+ if (i == s.length()) {
+ components.append(Component());
+ state = ParserState::End;
+ } else if (s.at(i).isLetter() || s.at(i) == underscore || s.at(i) == tilda) {
+ i0 = i;
+ while (i < s.length() && (s.at(i).isLetterOrNumber() || s.at(i) == underscore || s.at(i) == tilda)) {
+ ++i;
+ }
+ components.append(Component(Field(s.mid(i0,i-i0))));
+ state = ParserState::End;
+ } else if (s.at(i).isDigit()) {
+ i0 = i;
+ while (i < s.length() && s.at(i).isDigit()){
+ ++i;
+ }
+ bool ok;
+ components.append(Component(static_cast<index_type>(s.mid(i0,i-i0).toString().toLongLong(&ok))));
+ if (!ok) {
+ myErrors().warning(tr("Error extracting integer from '%1' at char %2.")
+ .arg(s.mid(i0,i-i0))
+ .arg(QString::number(i0))).handle(errorHandler);
+ return Path();
+ } else {
+ myErrors().hint(tr("Index shound use square brackets and not a dot (at char %1).")
+ .arg(QString::number(i0))).handle(errorHandler);
+ }
+ state = ParserState::End;
+ } else if (s.at(i) == dot || s.at(i) == lsBrace) {
+ components.append(Component());
+ state = ParserState::End;
+ } else if (s.at(i) == at) {
+ i0 = ++i;
+ while (i < s.length() && s.at(i).isLetterOrNumber()){
+ ++i;
+ }
+ components.append(Component(Current(s.mid(i0,i-i0))));
+ state = ParserState::End;
+ } else if (s.at(i) == dollar) {
+ i0 = ++i;
+ while (i < s.length() && s.at(i).isLetterOrNumber()){
+ ++i;
+ }
+ components.append(Component(Root(s.mid(i0,i-i0))));
+ state = ParserState::End;
+ } else {
+ c=s.at(i);
+ myErrors().error(tr("Unexpected character '%1' after dot (at char %2).")
+ .arg(QStringView(&c,1))
+ .arg(QString::number(i-1))).handle(errorHandler);
+ return Path();
+ }
+ } else if (c == lsBrace) {
+ state = ParserState::IndexOrKey;
+ } else {
+ myErrors().error(tr("Unexpected character '%1' after end of component (char %2).")
+ .arg(QStringView(&c,1))
+ .arg(QString::number(i-1))).handle(errorHandler);
+ return Path();
+ }
+ break;
+ }
+ }
+ switch (state) {
+ case ParserState::Start:
+ return Path();
+ case ParserState::IndexOrKey:
+ errorHandler(myErrors().error(tr("unclosed square brace at end.")));
+
+ return Path();
+ case ParserState::End:
+ return Path(0, components.length(), std::make_shared<PathData>(strVals, components));
+ }
+ Q_ASSERT(false && "Unexpected state in Path::fromString");
+ return Path();
+}
+
+Path Path::root(PathRoot s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Root(s)))));
+}
+
+Path Path::root(QString s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(s), QVector<Component>(1,Component(Root(s)))));
+}
+
+Path Path::index(index_type i)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Index(i)))));
+}
+
+Path Path::root(QStringView s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Root(s)))));
+}
+
+
+Path Path::field(QStringView s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Field(s)))));
+}
+
+Path Path::field(QString s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(s), QVector<Component>(1,Component(Field(s)))));
+}
+
+Path Path::key(QStringView s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Key(s)))));
+}
+
+Path Path::key(QString s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(s), QVector<Component>(1,Component(Key(s)))));
+}
+
+Path Path::current(PathCurrent s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Current(s)))));
+}
+
+Path Path::current(QString s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(s), QVector<Component>(1,Component(Current(s)))));
+}
+
+Path Path::current(QStringView s)
+{
+ return Path(0,1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Current(s)))));
+}
+
+Path Path::empty()
+{
+ return Path();
+}
+
+Path Path::subEmpty() const
+{
+ if (m_endOffset != 0)
+ return noEndOffset().subEmpty();
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component()), m_data));
+}
+
+Path Path::subField(QString name) const
+{
+ auto res = subField(QStringView(name));
+ res.m_data->strData.append(name);
+ return res;
+}
+
+Path Path::subField(QStringView name) const
+{
+ if (m_endOffset != 0)
+ return noEndOffset().subField(name);
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Field(name))), m_data));
+}
+
+Path Path::subKey(QString name) const
+{
+ auto res = subKey(QStringView(name));
+ res.m_data->strData.append(name);
+ return res;
+}
+
+Path Path::subKey(QStringView name) const
+{
+ if (m_endOffset != 0)
+ return noEndOffset().subKey(name);
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Key(name))), m_data));
+}
+
+Path Path::subIndex(index_type i) const
+{
+ if (m_endOffset != 0)
+ return noEndOffset().subIndex(i);
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(i)), m_data));
+}
+
+Path Path::subAny() const
+{
+ if (m_endOffset != 0)
+ return noEndOffset().subAny();
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Any())), m_data));
+}
+
+Path Path::subFilter(function<bool (DomItem)> filter, QString desc) const
+{
+ auto res = subFilter(filter, QStringView(desc));
+ res.m_data->strData.append(desc);
+ return res;
+}
+
+Path Path::subFilter(function<bool (DomItem)> filter, QStringView desc) const
+{
+ if (m_endOffset != 0)
+ return noEndOffset().subFilter(filter, desc);
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Filter(filter, desc))), m_data));
+}
+
+Path Path::subCurrent(PathCurrent s) const
+{
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Current(s))), m_data));
+}
+
+Path Path::subCurrent(QString s) const
+{
+ auto res = subCurrent(QStringView(s));
+ res.m_data->strData.append(s);
+ return res;
+}
+
+Path Path::subCurrent(QStringView s) const
+{
+ if (m_endOffset != 0)
+ return noEndOffset().subCurrent(s);
+ return Path(0,m_length+1,std::make_shared<PathData>(QStringList(), QVector<Component>(1,Component(Current(s))), m_data));
+}
+
+Path Path::subPath(Path toAdd, bool avoidToAddAsBase) const
+{
+ if (toAdd.length() == 0)
+ return *this;
+ int resLength = length() + toAdd.length();
+ if (m_endOffset != 0) {
+ Path thisExtended = this->expandBack();
+ if (thisExtended.length() > resLength)
+ thisExtended = thisExtended.mid(0, resLength);
+ Path added = thisExtended.mid(length(), thisExtended.length() - length());
+ if (added == toAdd.mid(0, toAdd.length())) {
+ if (resLength == thisExtended.length())
+ return thisExtended;
+ else
+ return thisExtended.subPath(toAdd.mid(added.length(), resLength - thisExtended.length()));
+ }
+ }
+ if (!avoidToAddAsBase) {
+ Path toAddExtended = toAdd.expandFront();
+ if (toAddExtended.length() >= resLength) {
+ toAddExtended = toAddExtended.mid(toAddExtended.length() - resLength, resLength);
+ if (toAddExtended.mid(0,length()) == *this)
+ return toAddExtended;
+ }
+ }
+ QVector<Component> components;
+ components.reserve(toAdd.length());
+ QStringList addedStrs;
+ bool addHasStr = false;
+ auto data = toAdd.m_data.get();
+ while (data) {
+ if (!data->strData.isEmpty()) {
+ addHasStr = true;
+ break;
+ }
+ data = data->parent.get();
+ }
+ if (addHasStr) {
+ QStringList myStrs;
+ data = m_data.get();
+ while (data) {
+ myStrs.append(data->strData);
+ data = data->parent.get();
+ }
+ data = toAdd.m_data.get();
+ while (data) {
+ for (int ij = 0; ij < data->strData.length(); ++ij) {
+ bool hasAlready = false;
+ for (int ii = 0; ii < myStrs.length() && !hasAlready; ++ii)
+ hasAlready = inQString(data->strData[ij], myStrs[ii]);
+ if (!hasAlready)
+ addedStrs.append(data->strData[ij]);
+ }
+ data = data->parent.get();
+ }
+ }
+ QStringList toAddStrs;
+ for (int i = 0; i < toAdd.length(); ++i) {
+ components.append(toAdd.component(i));
+ QStringView compStrView = toAdd.component(i).stringView();
+ if (!compStrView.isEmpty()) {
+ for (int j = 0; j < addedStrs.length(); ++j) {
+ if (inQString(compStrView, addedStrs[j])) {
+ toAddStrs.append(addedStrs[j]);
+ addedStrs.removeAt(j);
+ break;
+ }
+ }
+ }
+ }
+ return Path(0, m_length + toAdd.length(),
+ std::make_shared<PathData>(toAddStrs, components, ((m_endOffset == 0) ? m_data : noEndOffset().m_data)));
+}
+
+Path Path::expandFront() const
+{
+ int newLen = 0;
+ auto data = m_data.get();
+ while (data) {
+ newLen += data->components.length();
+ data = data->parent.get();
+ }
+ newLen -= m_endOffset;
+ return Path(m_endOffset, newLen, m_data);
+}
+
+Path Path::expandBack() const
+{
+ if (m_endOffset > 0)
+ return Path(0, m_length + m_endOffset, m_data);
+ return *this;
+}
+
+Path &Path::operator++()
+{
+ if (m_length > 0)
+ m_length -= 1;
+ return *this;
+}
+
+Path Path::operator++(int)
+{
+ Path res = *this;
+ if (m_length > 0)
+ m_length -= 1;
+ return res;
+}
+
+int Path::cmp(const Path &p1, const Path &p2)
+{
+ // lexicographic ordering
+ const int lMin = qMin(p1.m_length, p2.m_length);
+ if (p1.m_data.get() == p2.m_data.get() && p1.m_endOffset == p2.m_endOffset && p1.m_length == p2.m_length)
+ return 0;
+ for (int i = 0; i < lMin; ++i) {
+ int c = Component::cmp(p1.component(i), p2.component(i));
+ if (c != 0)
+ return c;
+ }
+ if (lMin < p2.m_length)
+ return -1;
+ if (p1.m_length > lMin)
+ return 1;
+ return 0;
+}
+
+Path::Path(quint16 endOffset, quint16 length, std::shared_ptr<PathData> data)
+ :m_endOffset(endOffset), m_length(length), m_data(data)
+{
+}
+
+Path Path::noEndOffset() const
+{
+ if (m_length == 0)
+ return Path();
+ if (m_endOffset == 0)
+ return *this;
+ // peel back
+ qint16 endOffset = m_endOffset;
+ std::shared_ptr<PathData> lastData = m_data;
+ while (lastData && endOffset >= lastData->components.length()) {
+ endOffset -= lastData->components.length();
+ lastData = lastData->parent;
+ }
+ if (endOffset > 0) {
+ Q_ASSERT(lastData && "Internal problem, reference to non existing PathData");
+ return Path(0, m_length, std::make_shared<PathData>(lastData->strData, lastData->components.mid(0, lastData->components.length() - endOffset), lastData->parent));
+ }
+ return Path(0, m_length, lastData);
+}
+
+ErrorGroups Path::myErrors()
+{
+ static ErrorGroups res = {{NewErrorGroup("PathParsing")}};
+ return res;
+}
+
+void Path::dump(Sink sink) const
+{
+ bool first = true;
+ for (int i = 0; i < m_length; ++i) {
+ auto c = component(i);
+ if (!c.hasSquareBrackets()) {
+ if (!first || (c.kind() != Kind::Root && c.kind() != Kind::Current))
+ sink(u".");
+ }
+ c.dump(sink);
+ first = false;
+ }
+}
+
+QString Path::toString() const
+{
+ QString res;
+ QTextStream stream(&res);
+ dump([&stream](QStringView str){ stream << str; });
+ stream.flush();
+ return res;
+}
+
+Path Path::dropFront() const
+{
+ if (m_length > 0)
+ return Path(m_endOffset, m_length - 1, m_data);
+ return Path();
+}
+
+Path Path::dropTail() const
+{
+ if (m_length > 0)
+ return Path(m_endOffset + 1, m_length - 1, m_data);
+ return Path();
+}
+
+Path Path::mid(int offset, int length) const
+{
+ length = qMin(m_length - offset, length);
+ if (offset < 0 || offset >= m_length || length <= 0 || length > m_length)
+ return Path();
+ int newEndOffset = m_endOffset + m_length - offset - length;
+ return Path(newEndOffset, length, m_data);
+}
+
+Path Path::mid(int offset) const
+{
+ return mid(offset, m_length - offset);
+}
+
+Path Path::fromString(QString s, ErrorHandler errorHandler)
+{
+ Path res = fromString(QStringView(s), errorHandler);
+ if (res.m_data)
+ res.m_data->strData.append(s);
+ return res;
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldompath_p.h b/src/qmldom/qqmldompath_p.h
new file mode 100644
index 0000000000..839a34a233
--- /dev/null
+++ b/src/qmldom/qqmldompath_p.h
@@ -0,0 +1,721 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 QMLDOM_PATH_H
+#define QMLDOM_PATH_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 "qqmldomconstants_p.h"
+#include "qqmldomstringdumper_p.h"
+#include "qqmldom_global.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QMetaEnum>
+#include <QtCore/QString>
+#include <QtCore/QStringView>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+#include <QtCore/QDebug>
+
+#include <functional>
+#include <iterator>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+class ErrorGroups;
+class ErrorMessage;
+class DomItem;
+class Path;
+
+using ErrorHandler = std::function<void(const ErrorMessage &)> ;
+
+using index_type = qint64;
+
+namespace PathEls {
+
+enum class Kind{
+ Empty,
+ Field,
+ Index,
+ Key,
+ Root,
+ Current,
+ Any,
+ Filter
+};
+
+class TestPaths;
+class Empty;
+class Field;
+class Index;
+class Key;
+class Root;
+class Current;
+class Any;
+class Filter;
+
+class Base {
+public:
+ virtual ~Base() = default;
+ virtual Kind kind() const = 0;
+ virtual QString name() const = 0;
+ virtual bool checkName(QStringView s) const = 0;
+ virtual QStringView stringView() const { return QStringView(); }
+ virtual index_type index(index_type defaultValue=-1) const { return defaultValue; }
+
+ virtual void dump(Sink sink) const;
+ virtual bool hasSquareBrackets() const { return false; }
+
+ // casting, could use optional, but that is c++17...
+ virtual const Empty *asEmpty() const { return nullptr; }
+ virtual const Field *asField() const { return nullptr; }
+ virtual const Index *asIndex() const { return nullptr; }
+ virtual const Key *asKey() const { return nullptr; }
+ virtual const Root *asRoot() const { return nullptr; }
+ virtual const Current *asCurrent() const { return nullptr; }
+ virtual const Any *asAny() const { return nullptr; }
+ virtual const Filter *asFilter() const { return nullptr; }
+};
+
+class Empty: public Base {
+public:
+ Kind kind() const override { return Kind::Empty; }
+ QString name() const override { return QString(); }
+ bool checkName(QStringView s) const override { return s.isEmpty(); }
+ const Empty * asEmpty() const override { return this; }
+};
+
+class Field: public Base {
+public:
+ Field(QStringView n): fieldName(n) {}
+ Kind kind() const override { return Kind::Field; }
+ QString name() const override { return fieldName.toString(); }
+ bool checkName(QStringView s) const override { return s == fieldName; }
+ QStringView stringView() const override { return fieldName; }
+ const Field * asField() const override { return this; }
+ void dump(Sink sink) const override { sink(fieldName); }
+
+ QStringView fieldName;
+};
+
+class Index: public Base {
+public:
+ Index(index_type i): indexValue(i) {}
+ Kind kind() const override { return Kind::Index; }
+ QString name() const override { return QString::number(indexValue); }
+ bool checkName(QStringView s) const override { return s == name(); }
+ index_type index(index_type = -1) const override { return indexValue; }
+ bool hasSquareBrackets() const override { return true; }
+ const Index * asIndex() const override { return this; }
+
+ index_type indexValue;
+};
+
+class Key: public Base {
+public:
+ Key(QStringView n): keyValue(n) {}
+ Kind kind() const override { return Kind::Key; }
+ QString name() const override { return keyValue.toString(); }
+ bool checkName(QStringView s) const override { return s == keyValue; }
+ QStringView stringView() const override { return keyValue; }
+ void dump(Sink sink) const override {
+ sink(u"[");
+ sinkEscaped(sink, keyValue);
+ sink(u"]");
+ }
+ bool hasSquareBrackets() const override { return true; }
+ const Key * asKey() const override { return this; }
+
+ QStringView keyValue;
+};
+
+class Root: public Base {
+public:
+ Root(): contextKind(PathRoot::Other), contextName() {}
+ Root(PathRoot r): contextKind(r), contextName() {}
+ Root(QStringView n) {
+ QMetaEnum metaEnum = QMetaEnum::fromType<PathRoot>();
+ contextKind = PathRoot::Other;
+ for (int i = 0; i < metaEnum.keyCount(); ++ i)
+ if (n.compare(QString::fromUtf8(metaEnum.key(i)), Qt::CaseInsensitive) == 0)
+ contextKind = PathRoot(metaEnum.value(i));
+ if (contextKind == PathRoot::Other)
+ contextName = n;
+ }
+ Kind kind() const override { return Kind::Root; }
+ QString name() const override {
+ switch (contextKind) {
+ case PathRoot::Modules:
+ return QStringLiteral(u"$modules");
+ case PathRoot::Cpp:
+ return QStringLiteral(u"$cpp");
+ case PathRoot::Libs:
+ return QStringLiteral(u"$libs");
+ case PathRoot::Top:
+ return QStringLiteral(u"$top");
+ case PathRoot::Env:
+ return QStringLiteral(u"$env");
+ case PathRoot::Universe:
+ return QStringLiteral(u"$universe");
+ case PathRoot::Other:
+ return QString::fromUtf8("$").append(contextName.toString());
+ }
+ Q_ASSERT(false && "Unexpected contextKind in name");
+ return QString();
+ }
+ bool checkName(QStringView s) const override {
+ if (contextKind != PathRoot::Other)
+ return s.compare(name(), Qt::CaseInsensitive) == 0;
+ return s.startsWith(QChar::fromLatin1('$')) && s.mid(1) == contextName;
+ }
+ QStringView stringView() const override { return contextName; }
+ void dump(Sink sink) const override {
+ sink(name());
+ }
+ const Root *asRoot() const override { return this; }
+
+ PathRoot contextKind;
+ QStringView contextName;
+};
+
+class Current: public Base {
+public:
+ Current(): contextName() {}
+ Current(PathCurrent c): contextKind(c) {}
+ Current(QStringView n) {
+ QMetaEnum metaEnum = QMetaEnum::fromType<PathCurrent>();
+ contextKind = PathCurrent::Other;
+ for (int i = 0; i < metaEnum.keyCount(); ++ i)
+ if (n.compare(QString::fromUtf8(metaEnum.key(i)), Qt::CaseInsensitive) == 0)
+ contextKind = PathCurrent(metaEnum.value(i));
+ if (contextKind == PathCurrent::Other)
+ contextName = n;
+ }
+ Kind kind() const override { return Kind::Current; }
+ QString name() const override {
+ switch (contextKind) {
+ case PathCurrent::Other:
+ return QString::fromUtf8("@").append(contextName.toString());
+ case PathCurrent::Obj:
+ return QStringLiteral(u"@obj");
+ case PathCurrent::ObjChain:
+ return QStringLiteral(u"@objChain");
+ case PathCurrent::ScopeChain:
+ return QStringLiteral(u"@scopeChain");
+ case PathCurrent::Component:
+ return QStringLiteral(u"@component");
+ case PathCurrent::Module:
+ return QStringLiteral(u"@module");
+ case PathCurrent::Ids:
+ return QStringLiteral(u"@ids");
+ case PathCurrent::Types:
+ return QStringLiteral(u"@types");
+ case PathCurrent::LookupStrict:
+ return QStringLiteral(u"@lookupStrict");
+ case PathCurrent::LookupDynamic:
+ return QStringLiteral(u"@lookupDynamic");
+ case PathCurrent::Lookup:
+ return QStringLiteral(u"@lookup");
+ }
+ Q_ASSERT(false && "Unexpected contextKind in Current::name");
+ return QString();
+ }
+ bool checkName(QStringView s) const override {
+ if (contextKind != PathCurrent::Other)
+ return s.compare(name(), Qt::CaseInsensitive) == 0;
+ return s.startsWith(QChar::fromLatin1('@')) && s.mid(1) == contextName;
+ }
+ QStringView stringView() const override { return contextName; }
+ const Current *asCurrent() const override { return this; }
+
+ PathCurrent contextKind;
+ QStringView contextName;
+};
+
+class Any: public Base {
+public:
+ Kind kind() const override { return Kind::Any; }
+ QString name() const override { return QLatin1String("*"); }
+ bool checkName(QStringView s) const override { return s == u"*"; }
+ bool hasSquareBrackets() const override { return true; }
+ const Any *asAny() const override { return this; }
+};
+
+class QMLDOM_EXPORT Filter: public Base {
+public:
+ Filter(std::function<bool(DomItem)> f, QStringView filterDescription = u"<native code filter>");
+ Kind kind() const override { return Kind::Filter; }
+ QString name() const override;
+ bool checkName(QStringView s) const override;
+ QStringView stringView() const override { return filterDescription; }
+ bool hasSquareBrackets() const override { return true; }
+ const Filter *asFilter() const override { return this; }
+
+ std::function<bool(DomItem)> filterFunction;
+ QStringView filterDescription;
+};
+
+class QMLDOM_EXPORT PathComponent {
+public:
+ PathComponent(): data() {}
+ ~PathComponent();
+
+ Kind kind() const { return base()->kind(); }
+ QString name() const { return base()->name(); };
+ bool checkName(QStringView s) const { return base()->checkName(s); }
+ QStringView stringView() const { return base()->stringView(); };
+ index_type index(index_type defaultValue=-1) const { return base()->index(defaultValue); }
+ void dump(Sink sink) const { base()->dump(sink); }
+ bool hasSquareBrackets() const { return base()->hasSquareBrackets(); }
+
+ const Empty *asEmpty() const { return base()->asEmpty(); }
+ const Field *asField() const { return base()->asField(); }
+ const Index *asIndex() const { return base()->asIndex(); }
+ const Key *asKey() const { return base()->asKey(); }
+ const Root *asRoot() const { return base()->asRoot(); }
+ const Current *asCurrent() const { return base()->asCurrent(); }
+ const Any *asAny() const { return base()->asAny(); }
+ static int cmp(const PathComponent &p1, const PathComponent &p2);
+private:
+ friend class QQmlJS::Dom::Path;
+ friend class QQmlJS::Dom::PathEls::TestPaths;
+
+ PathComponent(const Empty &o): data(o) {}
+ PathComponent(const Field &o): data(o) {}
+ PathComponent(const Index &o): data(o) {}
+ PathComponent(const Key &o): data(o) {}
+ PathComponent(const Root &o): data(o) {}
+ PathComponent(const Current &o): data(o) {}
+ PathComponent(const Any &o): data(o) {}
+ PathComponent(const Filter &o): data(o) {}
+
+ Base *base() {
+ return reinterpret_cast<Base*>(&data);
+ }
+ const Base *base() const {
+ return reinterpret_cast<const Base*>(&data);
+ }
+ union Data {
+ Data(): empty() { }
+ Data(const Data &d) {
+ switch (d.kind()){
+ case Kind::Empty:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&empty) && "non C++11 compliant compiler");
+ new (&empty) Empty(d.empty);
+ break;
+ case Kind::Field:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&field) && "non C++11 compliant compiler");
+ new (&field) Field(d.field);
+ break;
+ case Kind::Index:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&index) && "non C++11 compliant compiler");
+ new (&index) Index(d.index);
+ break;
+ case Kind::Key:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&key) && "non C++11 compliant compiler");
+ new (&key) Key(d.key);
+ break;
+ case Kind::Root:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&root) && "non C++11 compliant compiler");
+ new (&root) Root(d.root);
+ break;
+ case Kind::Current:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&current) && "non C++11 compliant compiler");
+ new (&current) Current(d.current);
+ break;
+ case Kind::Any:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&any) && "non C++11 compliant compiler");
+ new (&any) Any(d.any);
+ break;
+ case Kind::Filter:
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&filter) && "non C++11 compliant compiler");
+ new (&filter) Filter(d.filter);
+ break;
+ }
+ }
+ Data(const Empty &o) {
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&empty) && "non C++11 compliant compiler");
+ new (&empty) Empty(o);
+ }
+ Data(const Field &o) {
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&field) && "non C++11 compliant compiler");
+ new (&field) Field(o);
+ }
+ Data(const Index &o){
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&index) && "non C++11 compliant compiler");
+ new (&index) Index(o);
+ }
+ Data(const Key &o) {
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&key) && "non C++11 compliant compiler");
+ new (&key) Key(o);
+ }
+ Data(const Root &o) {
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&root) && "non C++11 compliant compiler");
+ new (&root) Root(o);
+ }
+ Data(const Current &o) {
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&current) && "non C++11 compliant compiler");
+ new (&current) Current(o);
+ }
+ Data(const Any &o) {
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&any) && "non C++11 compliant compiler");
+ new (&any) Any(o);
+ }
+ Data(const Filter &o) {
+ Q_ASSERT(static_cast<void*>(this)==static_cast<void*>(&filter) && "non C++11 compliant compiler");
+ new (&filter) Filter(o);
+ }
+ Data &operator=(const Data &d) {
+ Q_ASSERT(this != &d);
+ this->~Data(); // destruct & construct new...
+ new (this)Data(d);
+ return *this;
+ }
+ Kind kind() const {
+ return reinterpret_cast<const Base*>(this)->kind();
+ }
+ ~Data() {
+ reinterpret_cast<const Base*>(this)->~Base();
+ }
+ Empty empty;
+ Field field;
+ Index index;
+ Key key;
+ Root root;
+ Current current;
+ Any any;
+ Filter filter;
+ } data;
+};
+
+inline bool operator==(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) == 0; }
+inline bool operator!=(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) != 0; }
+inline bool operator< (const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) < 0; }
+inline bool operator> (const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) > 0; }
+inline bool operator<=(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) <= 0; }
+inline bool operator>=(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) >= 0; }
+
+class PathData {
+public:
+ PathData(QStringList strData, QVector<PathComponent> components): strData(strData), components(components) {}
+ PathData(QStringList strData, QVector<PathComponent> components, std::shared_ptr<PathData> parent):
+ strData(strData), components(components), parent(parent) {}
+
+ QStringList strData;
+ QVector<PathComponent> components;
+ std::shared_ptr<PathData> parent;
+};
+
+} // namespace PathEls
+
+#define QMLDOM_USTRING(name) constexpr const auto name = u#name
+// namespace, so it cam be reopened to add more entries
+namespace Fields{
+constexpr const auto access = u"access";
+constexpr const auto annotations = u"annotations";
+constexpr const auto attachedType = u"attachedType";
+constexpr const auto attachedTypeName = u"attachedTypeName";
+constexpr const auto autoExport = u"autoExport";
+constexpr const auto base = u"base";
+constexpr const auto bindingType = u"bindingType";
+constexpr const auto bindings = u"bindings";
+constexpr const auto body = u"body";
+constexpr const auto canonicalFilePath = u"canonicalFilePath";
+constexpr const auto canonicalPath = u"canonicalPath";
+constexpr const auto children = u"children";
+constexpr const auto classNames = u"classNames";
+constexpr const auto code = u"code";
+constexpr const auto components = u"components";
+constexpr const auto contents = u"contents";
+constexpr const auto contentsDate = u"contentsDate";
+constexpr const auto currentExposedAt = u"currentExposedAt";
+constexpr const auto currentIsValid = u"currentIsValid";
+constexpr const auto currentItem = u"currentItem";
+constexpr const auto currentRevision = u"currentRevision";
+constexpr const auto defaultPropertyName = u"defaultPropertyName";
+constexpr const auto designerSupported = u"designerSupported";
+constexpr const auto elementCanonicalPath = u"elementCanonicalPath";
+constexpr const auto enumerations = u"enumerations";
+constexpr const auto errors = u"errors";
+constexpr const auto exportSource = u"exportSource";
+constexpr const auto exports = u"exports";
+constexpr const auto extraRequired = u"extraRequired";
+constexpr const auto fileName = u"fileName";
+constexpr const auto get = u"get";
+constexpr const auto globalScopeName = u"globalScopeName";
+constexpr const auto globalScopeWithName = u"globalScopeWithName";
+constexpr const auto hasCallback = u"hasCallback";
+constexpr const auto idStr = u"idStr";
+constexpr const auto ids = u"ids";
+constexpr const auto import = u"import";
+constexpr const auto importId = u"importId";
+constexpr const auto imports = u"imports";
+constexpr const auto inheritVersion = u"inheritVersion";
+constexpr const auto inProgress = u"inProgress";
+constexpr const auto isAlias = u"isAlias";
+constexpr const auto isComposite = u"isComposite";
+constexpr const auto isCreatable = u"isCreatable";
+constexpr const auto isDefaultMember = u"isDefaultMember";
+constexpr const auto isInternal = u"isInternal";
+constexpr const auto isLatest = u"isLatest";
+constexpr const auto isList = u"isList";
+constexpr const auto isPointer = u"isPointer";
+constexpr const auto isRequired = u"isRequired";
+constexpr const auto isSingleton = u"isSingleton";
+constexpr const auto isValid = u"isValid";
+constexpr const auto isWritable = u"isWritable";
+constexpr const auto jsFileWithPath = u"jsFileWithPath";
+constexpr const auto kind = u"kind";
+constexpr const auto lastRevision = u"lastRevision";
+constexpr const auto lastValidRevision = u"lastValidRevision";
+constexpr const auto loadInfo = u"loadInfo";
+constexpr const auto loadOptions = u"loadOptions";
+constexpr const auto loadPaths = u"loadPaths";
+constexpr const auto loadsWithWork = u"loadsWithWork";
+constexpr const auto location = u"location";
+constexpr const auto logicalPath = u"logicalPath";
+constexpr const auto majorVersion = u"majorVersion";
+constexpr const auto metaRevisions = u"metaRevisions";
+constexpr const auto methodType = u"methodType";
+constexpr const auto methods = u"methods";
+constexpr const auto minorVersion = u"minorVersion";
+constexpr const auto moduleIndex = u"moduleIndex";
+constexpr const auto moduleIndexWithUri = u"moduleIndexWithUri";
+constexpr const auto moduleScope = u"moduleScope";
+constexpr const auto nAllLoadedCallbacks = u"nAllLoadedCallbacks";
+constexpr const auto nCallbacks = u"nCallbacks";
+constexpr const auto nLoaded = u"nLoaded";
+constexpr const auto nNotdone = u"nNotdone";
+constexpr const auto name = u"name";
+constexpr const auto nextScope = u"nextScope";
+constexpr const auto objects = u"objects";
+constexpr const auto onAttachedObject = u"onAttachedObject";
+constexpr const auto options = u"options";
+constexpr const auto parameters = u"parameters";
+constexpr const auto parentObject = u"parentObject";
+constexpr const auto path = u"path";
+constexpr const auto plugins = u"plugins";
+constexpr const auto pragma = u"pragma";
+constexpr const auto pragmas = u"pragmas";
+constexpr const auto propertyDef = u"propertyDef";
+constexpr const auto propertyDefRef = u"propertyDefRef";
+constexpr const auto propertyDefs = u"propertyDefs";
+constexpr const auto propertyName = u"propertyName";
+constexpr const auto prototype = u"prototype";
+constexpr const auto qmlDirectoryWithPath = u"qmlDirectoryWithPath";
+constexpr const auto qmlFileWithPath = u"qmlFileWithPath";
+constexpr const auto qmldirFileWithPath = u"qmldirFileWithPath";
+constexpr const auto qmltypesFileWithPath = u"qmltypesFileWithPath";
+constexpr const auto qmltypesFiles = u"qmltypesFiles";
+constexpr const auto queue = u"queue";
+constexpr const auto referredObject = u"referredObject";
+constexpr const auto referredObjectPath = u"referredObjectPath";
+constexpr const auto requestedAt = u"requestedAt";
+constexpr const auto requestingUniverse = u"requestingUniverse";
+constexpr const auto returnType = u"returnType";
+constexpr const auto returnTypeName = u"returnTypeName";
+constexpr const auto rootComponent = u"rootComponent";
+constexpr const auto sources = u"sources";
+constexpr const auto status = u"status";
+constexpr const auto stringValue = u"stringValue";
+constexpr const auto symbols = u"symbols";
+constexpr const auto target = u"target";
+constexpr const auto targetPropertyName = u"targetPropertyName";
+constexpr const auto type = u"type";
+constexpr const auto typeName = u"typeName";
+constexpr const auto types = u"types";
+constexpr const auto universe = u"universe";
+constexpr const auto uri = u"uri";
+constexpr const auto uris = u"uris";
+constexpr const auto validExposedAt = u"validExposedAt";
+constexpr const auto validItem = u"validItem";
+constexpr const auto value = u"value";
+constexpr const auto values = u"values";
+constexpr const auto version = u"version";
+constexpr const auto when = u"when";
+}
+
+class Source;
+size_t qHash(const Path &, size_t);
+
+// Define a iterator for it?
+// begin() can basically be itself, end() the empty path (zero length), iteration though dropFront()
+class QMLDOM_EXPORT Path{
+ Q_DECLARE_TR_FUNCTIONS(ErrorGroup);
+public:
+ using Kind = PathEls::Kind;
+ using Component = PathEls::PathComponent;
+ static ErrorGroups myErrors(); // use static consts and central registration instead?
+
+ Path(){}
+ int length() const { return m_length; }
+ Path operator[](int i) const;
+ operator bool() const;
+
+ PathRoot headRoot() const;
+ PathCurrent headCurrent() const;
+ Kind headKind() const;
+ QString headName() const;
+ bool checkHeadName(QStringView name) const;
+ index_type headIndex(index_type defaultValue=-1) const;
+ std::function<bool(DomItem)> headFilter() const;
+ Path head() const;
+ Path last() const;
+ Source split() const;
+
+ void dump(Sink sink) const;
+ QString toString() const;
+ Path dropFront() const;
+ Path dropTail() const;
+ Path mid(int offset, int length) const;
+ Path mid(int offset) const;
+
+ // # Path construction
+ static Path fromString(QString s, ErrorHandler errorHandler=nullptr);
+ static Path fromString(QStringView s, ErrorHandler errorHandler=nullptr);
+ static Path root(PathRoot r);
+ static Path root(QStringView s=u"");
+ static Path root(QString s);
+ static Path index(index_type i);
+ static Path field(QStringView s=u"");
+ static Path field(QString s);
+ static Path key(QStringView s=u"");
+ static Path key(QString s);
+ static Path current(PathCurrent c);
+ static Path current(QStringView s=u"");
+ static Path current(QString s);
+ static Path empty();
+ // add
+ Path subEmpty() const;
+ Path subField(QString name) const;
+ Path subField(QStringView name) const;
+ Path subKey(QString name) const;
+ Path subKey(QStringView name) const;
+ Path subIndex(index_type i) const;
+ Path subAny() const;
+ Path subFilter(std::function<bool(DomItem)>, QString) const;
+ Path subFilter(std::function<bool(DomItem)>, QStringView desc=u"<native code filter>") const;
+ Path subCurrent(PathCurrent s) const;
+ Path subCurrent(QString s) const;
+ Path subCurrent(QStringView s=u"") const;
+ Path subPath(Path toAdd, bool avoidToAddAsBase = false) const;
+
+ Path expandFront() const;
+ Path expandBack() const;
+
+ Path &operator++();
+ Path operator ++(int);
+
+ // iterator traits
+ using difference_type = long;
+ using value_type = Path;
+ using pointer = const Component*;
+ using reference = const Path&;
+ using iterator_category = std::forward_iterator_tag;
+
+ static int cmp(const Path &p1, const Path &p2);
+private:
+ explicit Path(quint16 endOffset, quint16 length, std::shared_ptr<PathEls::PathData> data);
+ friend class QQmlJS::Dom::PathEls::TestPaths;
+ friend size_t qHash(const Path &, size_t);
+
+ Component component(int i) const;
+ Path noEndOffset() const;
+
+ quint16 m_endOffset = 0;
+ quint16 m_length = 0;
+ std::shared_ptr<PathEls::PathData> m_data;
+};
+
+inline bool operator==(const Path& lhs, const Path& rhs){ return lhs.length() == rhs.length() && Path::cmp(lhs,rhs) == 0; }
+inline bool operator!=(const Path& lhs, const Path& rhs){ return lhs.length() != rhs.length() || Path::cmp(lhs,rhs) != 0; }
+inline bool operator< (const Path& lhs, const Path& rhs){ return Path::cmp(lhs,rhs) < 0; }
+inline bool operator> (const Path& lhs, const Path& rhs){ return Path::cmp(lhs,rhs) > 0; }
+inline bool operator<=(const Path& lhs, const Path& rhs){ return Path::cmp(lhs,rhs) <= 0; }
+inline bool operator>=(const Path& lhs, const Path& rhs){ return Path::cmp(lhs,rhs) >= 0; }
+
+class Source {
+public:
+ Path pathToSource;
+ Path pathFromSource;
+};
+
+inline size_t qHash(const Path &path, size_t seed)
+{
+ const size_t bufSize = 256;
+ size_t buf[bufSize];
+ size_t *it = &buf[0];
+ *it++ = path.length();
+ if (path.length()>0) {
+ int iPath = path.length();
+ size_t maxPath = bufSize / 2 - 1;
+ size_t endPath = (size_t(iPath) > maxPath) ? maxPath - iPath : 0;
+ while (size_t(iPath) > endPath) {
+ Path p = path[--iPath];
+ Path::Kind k = p.headKind();
+ *it++ = size_t(k);
+ *it++ = qHash(p.component(0).stringView(), seed)^size_t(p.headRoot())^size_t(p.headCurrent());
+ }
+ }
+ return qHash(QByteArray::fromRawData(reinterpret_cast<char *>(&buf[0]), (it - &buf[0])*sizeof(size_t)), seed);
+}
+
+inline QDebug operator<<(QDebug debug, const Path &p)
+{
+ debug << p.toString();
+ return debug;
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QMLDOM_PATH_H
diff --git a/tests/auto/qmldom/CMakeLists.txt b/tests/auto/qmldom/CMakeLists.txt
index 897069f608..7e6acf2c4a 100644
--- a/tests/auto/qmldom/CMakeLists.txt
+++ b/tests/auto/qmldom/CMakeLists.txt
@@ -1,3 +1,5 @@
# Generated from qmldom.pro.
+add_subdirectory(errormessage)
+add_subdirectory(path)
add_subdirectory(stringdumper)
diff --git a/tests/auto/qmldom/errormessage/CMakeLists.txt b/tests/auto/qmldom/errormessage/CMakeLists.txt
new file mode 100644
index 0000000000..7e40889b3e
--- /dev/null
+++ b/tests/auto/qmldom/errormessage/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Generated from errormessage.pro.
+
+#####################################################################
+## tst_qmldomerrormessage Binary:
+#####################################################################
+
+qt_internal_add_test(tst_qmldomerrormessage
+ SOURCES
+ tst_qmldomerrormessage.cpp
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::QmlDevToolsPrivate
+ Qt::QmlDomPrivate
+ Qt::Test
+)
diff --git a/tests/auto/qmldom/errormessage/errormessage.pro b/tests/auto/qmldom/errormessage/errormessage.pro
new file mode 100644
index 0000000000..a31d904c49
--- /dev/null
+++ b/tests/auto/qmldom/errormessage/errormessage.pro
@@ -0,0 +1,23 @@
+QT = core qmldevtools-private qmldom-private testlib
+TARGET = tst_qmldomerrormessage
+
+
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ tst_qmldomerrormessage.cpp
+
+
+load(qt_common)
diff --git a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
new file mode 100644
index 0000000000..e36278771d
--- /dev/null
+++ b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 <QtQmlDom/private/qqmldomerrormessage_p.h>
+
+#include <QtTest/QtTest>
+#include <QTextStream>
+#include <QDebug>
+
+#include <limits>
+
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+static ErrorGroups myErrors(){
+ static ErrorGroups res = {{NewErrorGroup("StaticAnalysis"), NewErrorGroup("FancyDetector")}};
+ return res;
+}
+
+constexpr const char *myError0 = "my.company.error0";
+
+void registerMyError() {
+ ErrorMessage::msg(myError0, myErrors().warning(u"Error number 0"));
+}
+
+static auto myError1 = ErrorMessage::msg("my.company.error1", myErrors().warning(u"Error number 1"));
+static auto myError2 = ErrorMessage::msg("my.company.error2", myErrors().error(u"Error number 2 on %1"));
+
+class TestErrorMessage: public QObject
+{
+ Q_OBJECT
+private slots:
+ void testError()
+ {
+ registerMyError();
+ auto err0 = ErrorMessage::load(myError0);
+ QCOMPARE(err0.errorId, QLatin1String(myError0));
+ QCOMPARE(err0.message, dumperToString(u"Error number 0"));
+ QCOMPARE(err0.level, ErrorLevel::Warning);
+ auto err1 = ErrorMessage::load(QLatin1String("my.company.error1"));
+ QCOMPARE(err1.errorId, myError1);
+ QCOMPARE(err1.message, dumperToString(u"Error number 1"));
+ QCOMPARE(err1.level, ErrorLevel::Warning);
+ auto err1bis = ErrorMessage::load("my.company.error1");
+ QCOMPARE(err1bis.errorId, myError1);
+ QCOMPARE(err1bis.message, dumperToString(u"Error number 1"));
+ QCOMPARE(err1bis.level, ErrorLevel::Warning);
+ auto err2 = ErrorMessage::load(myError2, QLatin1String("extra info"));
+ QCOMPARE(err2.errorId, myError2);
+ QCOMPARE(err2.message, dumperToString(u"Error number 2 on extra info"));
+ QCOMPARE(err2.level, ErrorLevel::Error);
+ }
+};
+
+}
+}
+QT_END_NAMESPACE
+
+QTEST_MAIN(QQmlJS::Dom::TestErrorMessage)
+#include "tst_qmldomerrormessage.moc"
diff --git a/tests/auto/qmldom/path/CMakeLists.txt b/tests/auto/qmldom/path/CMakeLists.txt
new file mode 100644
index 0000000000..374682b153
--- /dev/null
+++ b/tests/auto/qmldom/path/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Generated from path.pro.
+
+#####################################################################
+## tst_qmldompath Binary:
+#####################################################################
+
+qt_internal_add_test(tst_qmldompath
+ SOURCES
+ tst_qmldompath.cpp
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::QmlDevToolsPrivate
+ Qt::QmlDomPrivate
+ Qt::Test
+)
diff --git a/tests/auto/qmldom/path/path.pro b/tests/auto/qmldom/path/path.pro
new file mode 100644
index 0000000000..467948f069
--- /dev/null
+++ b/tests/auto/qmldom/path/path.pro
@@ -0,0 +1,23 @@
+QT = core qmldevtools-private qmldom-private testlib
+TARGET = tst_qmldompath
+
+
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ tst_qmldompath.cpp
+
+
+load(qt_common)
diff --git a/tests/auto/qmldom/path/tst_qmldompath.cpp b/tests/auto/qmldom/path/tst_qmldompath.cpp
new file mode 100644
index 0000000000..b11abe6283
--- /dev/null
+++ b/tests/auto/qmldom/path/tst_qmldompath.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 <QtQmlDom/private/qqmldompath_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+
+#include <QtTest/QtTest>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+namespace PathEls {
+
+class TestPaths: public QObject {
+ Q_OBJECT
+public:
+ void testPathInternals(Path p1)
+ {
+ QCOMPARE(p1.component(0).kind(), Kind::Root);
+ QCOMPARE(p1.component(1).kind(), Kind::Current);
+
+ Path p11 = Path::field(u"test");
+ QString s = QLatin1String("test");
+ Path p2 = Path::field(s);
+ Path p3 = Path::field(QLatin1String("test"));
+ QCOMPARE(p11, p2);
+ QCOMPARE(p11, p3);
+ QVERIFY(p11.m_data->strData.isEmpty());
+ QCOMPARE(p2.m_data->strData.length(), 1);
+ QCOMPARE(p2.m_data->strData.first(), s);
+ QCOMPARE(p3.m_data->strData.length(), 1);
+ QCOMPARE(p3.m_data->strData.first(), s);
+ }
+
+private slots:
+ void pathComponentTestInternalAlloc() {
+ PathComponent c;
+ QCOMPARE(c.kind(), Kind::Empty);
+ PathComponent c1{Current()};
+ QCOMPARE(c1.kind(), Kind::Current);
+ QVERIFY(c!=c1);
+ QVERIFY(c<c1);
+ QVERIFY(c1>c);
+ PathComponent c1_1{Current(PathCurrent::Ids)};
+ QCOMPARE(c1_1.kind(), Kind::Current);
+ QVERIFY(c1 != c1_1);
+ QVERIFY(c < c1_1);
+ QCOMPARE(c1_1.name(), QLatin1String("@ids"));
+ PathComponent c1_2{Current(u"ids")};
+ QCOMPARE(c1_1, c1_2);
+ PathComponent c2 = c1;
+ QCOMPARE(c2.kind(), Kind::Current);
+ QCOMPARE(c2, c1);
+ PathComponent c3;
+ QCOMPARE(c, c3);
+ QCOMPARE(c3.kind(), Kind::Empty);
+ c3 = c1;
+ QCOMPARE(c3.kind(), Kind::Current);
+ QCOMPARE(c3, c1);
+ PathComponent c4{Field(u"bla")};
+ QCOMPARE(c4.kind(), Kind::Field);
+ QCOMPARE(c4.name(), QLatin1String("bla"));
+ auto c5=PathComponent(Index(42));
+ QCOMPARE(c5.kind(), Kind::Index);
+ QCOMPARE(c5.index(), 42);
+ auto c6=PathComponent(Key(u"bla"));
+ QCOMPARE(c6.kind(), Kind::Key);
+ QCOMPARE(c6.name(), QLatin1String("bla"));
+ auto c7=PathComponent(Key(u" ugly\n \t \\string\"'bla"));
+ QCOMPARE(c7.kind(), Kind::Key);
+ QCOMPARE(c7.name(), QLatin1String(" ugly\n \t \\string\"'bla"));
+ auto c8=PathComponent(Root(u"pippo"));
+ QCOMPARE(c8.kind(), Kind::Root);
+ QCOMPARE(c8.name(), QLatin1String("$pippo"));
+ auto c8_1=PathComponent(Root(PathRoot::Env));
+ QCOMPARE(c8_1.kind(), Kind::Root);
+ QCOMPARE(c8_1.name(), QLatin1String("$env"));
+ auto c8_2=PathComponent(Root(u"env"));
+ QCOMPARE(c8_1, c8_2);
+ auto c9=PathComponent(Current(u"ippo"));
+ QCOMPARE(c9.kind(), Kind::Current);
+ QCOMPARE(c9.name(), QLatin1String("@ippo"));
+ auto c10=PathComponent(Any());
+ QCOMPARE(c10.kind(), Kind::Any);
+ QVERIFY(c9!=c10);
+ auto c11=PathComponent(Filter([](DomItem){ return true; }));
+ auto c12=c11;
+ auto c13=PathComponent(Filter([](DomItem){ return false; }));
+ auto c14=PathComponent(Filter([](DomItem){ return false; }, u"skipAll"));
+ auto c15=PathComponent(Filter([](DomItem){ return true; }, u"skipAll"));
+ QCOMPARE(c11.kind(), Kind::Filter);
+ QCOMPARE(c11, c11);
+ QVERIFY(c11 != c12); // native code assumed to be non comparable and different even if they are the same
+ QVERIFY(c11 != c13);
+ QVERIFY(c13 != c14);
+ QVERIFY(c11 != c14);
+ QCOMPARE(c14, c14);
+ QCOMPARE(c14, c15); // same description (without < at the beginning) assumes same function even if different
+ }
+
+ void testPaths() {
+ Path p;
+ QCOMPARE(p.length(), 0);
+ QCOMPARE(p.length(), 0);
+ Path p0 = Path::root();
+ QCOMPARE(p0[0].headKind(), Kind::Root);
+ QCOMPARE(p0.length(), 1);
+ Path p1 = p0.subCurrent();
+ QCOMPARE(p1.length(), 2);
+ testPathInternals(p1);
+ QCOMPARE(p1[0].headKind(), Kind::Root);
+ QCOMPARE(p1[1].headKind(), Kind::Current);
+ auto p2 = p1.subField(u"aa");
+ QCOMPARE(p2[2].headKind(), Kind::Field);
+ auto p3 = p2.subIndex(4);
+ QCOMPARE(p3[3].headKind(), Kind::Index);
+ auto p4 = p3.subKey("bla");
+ QCOMPARE(p4[4].headKind(), Kind::Key);
+ auto p5 = p4.subAny();
+ QCOMPARE(p5[5].headKind(), Kind::Any);
+ auto p6 = p5.subEmpty();
+ QCOMPARE(p6[6].headKind(), Kind::Empty);
+ auto rString = u"$.@.aa[4][\"bla\"][*].";
+ QCOMPARE(p6.toString(), rString);
+ auto p7 = p6.subFilter([](DomItem){ return true; }, u"true");
+ auto p7Str = p7.toString();
+ QCOMPARE(p7Str, u"$.@.aa[4][\"bla\"][*].[?(true)]");
+ auto p8 = p7.dropTail();
+ QCOMPARE(p8.length(), 7);
+ QCOMPARE(p8.toString(), rString);
+ QCOMPARE(p8, p6);
+ auto p9 = Path::fromString(rString);
+ QCOMPARE(p9.length(), 7);
+ auto p9Str = p9.toString();
+ QCOMPARE(p9Str, rString);
+ QCOMPARE(p9, p6);
+ auto p10 = p6.dropFront();
+ QCOMPARE(p10.length(), 6);
+ auto p10Str = p10.toString();
+ auto r2Str = u"@.aa[4][\"bla\"][*].";
+ QCOMPARE(p10Str, r2Str);
+ auto p11 = Path::fromString(r2Str);
+ auto p11Str = p11.toString();
+ QCOMPARE(p11Str, r2Str);
+ QCOMPARE(p10, p11);
+ auto p12 = p7.mid(1,6);
+ auto p12Str = p12.toString();
+ QCOMPARE(p12Str, r2Str);
+ QCOMPARE(p10, p12);
+ }
+
+ void testPathSplit()
+ {
+ QList<Path> paths({Path(),
+ Path::root(PathRoot::Env).subField(u"pippo").subKey(u"pluto").subIndex(4),
+ Path::root(PathRoot::Env).subField(u"pippo").subKey(u"pluto"),
+ Path::root(PathRoot::Env).subField(u"pippo"),
+ Path::root(PathRoot::Env).subField(u"pippo").subField(u"pp"),
+ Path::root(PathRoot::Env),
+ Path::field(u"pippo").subIndex(4),
+ Path::field(u"pippo").subKey(u"pluto").subIndex(4),
+ Path::field(u"pippo").subKey(u"pluto"),
+ Path::field(u"pippo"),
+ Path::field(u"pippo").subField(u"pp"),
+ Path::index(4),
+ Path::key(u"zz")
+ });
+ foreach (Path p, paths) {
+ Source s = p.split();
+ QCOMPARE(p, s.pathToSource.subPath(s.pathFromSource));
+ if (!s.pathFromSource)
+ QVERIFY(!s.pathToSource);
+ }
+ QCOMPARE(paths.at(1).split().pathToSource, Path::root(PathRoot::Env));
+ QCOMPARE(paths.at(2).split().pathToSource, Path::root(PathRoot::Env));
+ QCOMPARE(paths.at(3).split().pathToSource, Path::root(PathRoot::Env));
+ QCOMPARE(paths.at(4).split().pathToSource, Path::root(PathRoot::Env).subField(u"pippo"));
+ QVERIFY(!paths.at(5).split().pathToSource);
+ QVERIFY(!paths.at(6).split().pathToSource);
+ QVERIFY(!paths.at(7).split().pathToSource);
+ QVERIFY(!paths.at(8).split().pathToSource);
+ QVERIFY(!paths.at(9).split().pathToSource);
+ QCOMPARE(paths.at(10).split().pathToSource, Path::field(u"pippo"));
+ QVERIFY(!paths.at(11).split().pathToSource);
+ QVERIFY(!paths.at(12).split().pathToSource);
+ }
+};
+
+} // namespace PathEls
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+QTEST_MAIN(QQmlJS::Dom::PathEls::TestPaths)
+#include "tst_qmldompath.moc"
diff --git a/tests/auto/qmldom/qmldom.pro b/tests/auto/qmldom/qmldom.pro
index 6a7b30274c..ca1340c42d 100644
--- a/tests/auto/qmldom/qmldom.pro
+++ b/tests/auto/qmldom/qmldom.pro
@@ -1,4 +1,6 @@
TEMPLATE = subdirs
SUBDIRS += \
+ errormessage \
+ path \
stringdumper