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