From c7a0cae7deb6e31c5b2e82c9a63ebe0a167fed09 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Tue, 5 May 2009 11:17:15 +1000 Subject: Improve error handling consistency --- src/declarative/qml/qml.pri | 6 +- src/declarative/qml/qmlcompiler.cpp | 33 +++-- src/declarative/qml/qmlcompiler_p.h | 5 +- src/declarative/qml/qmlcomponent.cpp | 24 ++-- src/declarative/qml/qmlcomponent.h | 5 +- src/declarative/qml/qmlcomponent_p.h | 3 +- src/declarative/qml/qmlcompositetypemanager.cpp | 23 +-- src/declarative/qml/qmlcompositetypemanager_p.h | 4 +- src/declarative/qml/qmldom.cpp | 18 +-- src/declarative/qml/qmldom.h | 2 + src/declarative/qml/qmldom_p.h | 2 +- src/declarative/qml/qmlerror.cpp | 180 ++++++++++++++++++++++++ src/declarative/qml/qmlerror.h | 82 +++++++++++ src/declarative/qml/qmlparser.cpp | 8 +- src/declarative/qml/qmlparser_p.h | 3 + src/declarative/qml/qmlscriptparser.cpp | 104 +++++++------- src/declarative/qml/qmlscriptparser_p.h | 11 +- src/declarative/qml/qmlvme.cpp | 24 ++-- src/declarative/qml/qmlvme_p.h | 5 +- src/declarative/util/qfxview.cpp | 79 ++++++++++- src/declarative/util/qfxview.h | 3 +- tools/qmlviewer/qmlviewer.cpp | 8 ++ 22 files changed, 501 insertions(+), 131 deletions(-) create mode 100644 src/declarative/qml/qmlerror.cpp create mode 100644 src/declarative/qml/qmlerror.h diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 9067039..69a1461 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -22,7 +22,8 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlclassfactory.cpp \ qml/qmlparserstatus.cpp \ qml/qmlcompositetypemanager.cpp \ - qml/qmlinfo.cpp + qml/qmlinfo.cpp \ + qml/qmlerror.cpp HEADERS += qml/qmlparser_p.h \ qml/qmlinstruction_p.h \ @@ -58,7 +59,8 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlcontext_p.h \ qml/qmlcompositetypemanager_p.h \ qml/qmllist.h \ - qml/qmldeclarativedata_p.h + qml/qmldeclarativedata_p.h \ + qml/qmlerror.h # for qtscript debugger QT += scripttools diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index c9bdfec..37d7fa1 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -147,7 +147,7 @@ int QmlCompiledData::indexForInt(int *data, int count) } QmlCompiler::QmlCompiler() -: exceptionLine(-1), output(0) +: exceptionLine(-1), exceptionColumn(-1), output(0) { } @@ -156,14 +156,19 @@ bool QmlCompiler::isError() const return exceptionLine != -1; } -qint64 QmlCompiler::errorLine() const +QList QmlCompiler::errors() const { - return exceptionLine; -} + QList rv; + + if(isError()) { + QmlError error; + error.setDescription(exceptionDescription); + error.setLine(exceptionLine); + error.setColumn(exceptionColumn); + rv << error; + } -QString QmlCompiler::errorDescription() const -{ - return exceptionDescription; + return rv; } bool QmlCompiler::isValidId(const QString &val) @@ -437,9 +442,19 @@ void QmlCompiler::reset(QmlCompiledComponent *cc, bool deleteMemory) cc->bytecode.clear(); } +#define COMPILE_EXCEPTION2(token, desc) \ + { \ + exceptionLine = token->line; \ + exceptionColumn = token->column; \ + QDebug d(&exceptionDescription); \ + d << desc; \ + return false; \ + } + #define COMPILE_EXCEPTION(desc) \ { \ exceptionLine = obj->line; \ + exceptionColumn = obj->column; \ QDebug d(&exceptionDescription); \ d << desc; \ return false; \ @@ -1215,10 +1230,10 @@ bool QmlCompiler::compilePropertyLiteralAssignment(QmlParser::Property *prop, //### we are restricted to a rather generic message here. If we can find a way to move // the exception into generateStoreInstruction we could potentially have better messages. // (the problem is that both compile and run exceptions can be generated, though) - COMPILE_EXCEPTION("Cannot assign value" << v->primitive << "to property" << obj->metaObject()->property(prop->index).name()); + COMPILE_EXCEPTION2(v, "Cannot assign value" << v->primitive << "to property" << obj->metaObject()->property(prop->index).name()); doassign = false; } else if (r == ReadOnly) { - COMPILE_EXCEPTION("Cannot assign value" << v->primitive << "to the read-only property" << obj->metaObject()->property(prop->index).name()); + COMPILE_EXCEPTION2(v, "Cannot assign value" << v->primitive << "to the read-only property" << obj->metaObject()->property(prop->index).name()); } else { doassign = true; } diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index 9a0ce1c..cc1a9e9 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include class QStringList; @@ -115,8 +116,7 @@ public: bool compile(QmlEngine *, QmlCompositeTypeData *, QmlCompiledComponent *); bool isError() const; - qint64 errorLine() const; - QString errorDescription() const; + QList errors() const; static bool isValidId(const QString &); static bool isBinding(const QString &); @@ -176,6 +176,7 @@ private: QSet ids; qint64 exceptionLine; + qint64 exceptionColumn; QString exceptionDescription; QmlCompiledData *output; }; diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp index c316f03..3b4d7b3 100644 --- a/src/declarative/qml/qmlcomponent.cpp +++ b/src/declarative/qml/qmlcomponent.cpp @@ -137,9 +137,7 @@ void QmlComponentPrivate::fromTypeData(QmlCompositeTypeData *data) if (!c) { Q_ASSERT(data->status == QmlCompositeTypeData::Error); - errorDescription = data->errorDescription; - qWarning().nospace() << "QmlComponent: " - << data->errorDescription.toLatin1().constData(); + errors = data->errors; } else { @@ -194,7 +192,7 @@ QmlComponent::Status QmlComponent::status() const return Loading; else if (d->engine && d->cc) return Ready; - else if (!d->errorDescription.isEmpty()) + else if (!d->errors.isEmpty()) return Error; else return Null; @@ -353,13 +351,13 @@ void QmlComponent::loadUrl(const QUrl &url) emit statusChanged(status()); } -QString QmlComponent::errorDescription() const +QList QmlComponent::errors() const { Q_D(const QmlComponent); if (isError()) - return d->errorDescription; + return d->errors; else - return QString(); + return QList(); } /*! @@ -448,7 +446,7 @@ QObject *QmlComponent::beginCreate(QmlContext *context) } if (!isReady()) { - qWarning("QmlComponent: Cannot create un-ready component"); + qWarning("QmlComponent: Component is not ready"); return 0; } @@ -466,15 +464,9 @@ QObject *QmlComponent::beginCreate(QmlContext *context) QmlVME vme; QObject *rv = vme.run(ctxt, d->cc, d->start, d->count); - if (vme.isError()) { - qWarning().nospace() -#ifdef QML_VERBOSEERRORS_ENABLED - << "QmlComponent: " -#endif - << vme.errorDescription().toLatin1().constData() << " @" - << d->url.toString().toLatin1().constData() << ":" << vme.errorLine(); - } + if (vme.isError()) + d->errors = vme.errors(); ctxt->deactivate(); diff --git a/src/declarative/qml/qmlcomponent.h b/src/declarative/qml/qmlcomponent.h index 0493c1f..90f7467 100644 --- a/src/declarative/qml/qmlcomponent.h +++ b/src/declarative/qml/qmlcomponent.h @@ -46,7 +46,7 @@ #include #include #include - +#include QT_BEGIN_HEADER @@ -77,7 +77,8 @@ public: bool isReady() const; bool isError() const; bool isLoading() const; - QString errorDescription() const; + + QList errors() const; QUrl url() const; diff --git a/src/declarative/qml/qmlcomponent_p.h b/src/declarative/qml/qmlcomponent_p.h index bb5f7bb..0507958 100644 --- a/src/declarative/qml/qmlcomponent_p.h +++ b/src/declarative/qml/qmlcomponent_p.h @@ -47,6 +47,7 @@ #include #include "private/qobject_p.h" #include "private/qmlcompositetypemanager_p.h" +#include #include "qmlcomponent.h" class QmlComponent; class QmlEngine; @@ -68,7 +69,7 @@ public: void fromTypeData(QmlCompositeTypeData *data); - QString errorDescription; + QList errors; QUrl url; int start; diff --git a/src/declarative/qml/qmlcompositetypemanager.cpp b/src/declarative/qml/qmlcompositetypemanager.cpp index 7f2cc58..fbe40bf 100644 --- a/src/declarative/qml/qmlcompositetypemanager.cpp +++ b/src/declarative/qml/qmlcompositetypemanager.cpp @@ -105,10 +105,9 @@ QmlCompositeTypeData::toCompiledComponent(QmlEngine *engine) QmlCompiler compiler; if (!compiler.compile(engine, this, compiledComponent)) { status = Error; - errorDescription = compiler.errorDescription() + - QLatin1String("@") + - url + QLatin1String(":") + - QString::number(compiler.errorLine()); + errors = compiler.errors(); + for(int ii = 0; ii < errors.count(); ++ii) + errors[ii].setUrl(url); compiledComponent->release(); compiledComponent = 0; } @@ -188,7 +187,10 @@ void QmlCompositeTypeManager::replyFinished() reply->url().toString(); unit->status = QmlCompositeTypeData::Error; - unit->errorDescription = errorDescription; + // ### FIXME + QmlError error; + error.setDescription(errorDescription); + unit->errors << error; doComplete(unit); } else { @@ -215,7 +217,10 @@ void QmlCompositeTypeManager::loadSource(QmlCompositeTypeData *unit) // ### - Fill in error errorDescription = QLatin1String("File error for URL ") + url.toString(); unit->status = QmlCompositeTypeData::Error; - unit->errorDescription = errorDescription; + // ### FIXME + QmlError error; + error.setDescription(errorDescription); + unit->errors << error; doComplete(unit); } @@ -234,7 +239,7 @@ void QmlCompositeTypeManager::setData(QmlCompositeTypeData *unit, if (!unit->data.parse(data, url)) { unit->status = QmlCompositeTypeData::Error; - unit->errorDescription = unit->data.errorDescription(); + unit->errors << unit->data.errors(); doComplete(unit); } else { @@ -273,7 +278,7 @@ void QmlCompositeTypeManager::checkComplete(QmlCompositeTypeData *unit) if (u->status == QmlCompositeTypeData::Error) { unit->status = QmlCompositeTypeData::Error; - unit->errorDescription = u->errorDescription; + unit->errors = u->errors; doComplete(unit); return; } else if (u->status == QmlCompositeTypeData::Waiting) { @@ -334,7 +339,7 @@ void QmlCompositeTypeManager::compile(QmlCompositeTypeData *unit) case QmlCompositeTypeData::Invalid: case QmlCompositeTypeData::Error: unit->status = QmlCompositeTypeData::Error; - unit->errorDescription = urlUnit->errorDescription; + unit->errors = urlUnit->errors; doComplete(unit); return; diff --git a/src/declarative/qml/qmlcompositetypemanager_p.h b/src/declarative/qml/qmlcompositetypemanager_p.h index bc86fcf..e4028d5 100644 --- a/src/declarative/qml/qmlcompositetypemanager_p.h +++ b/src/declarative/qml/qmlcompositetypemanager_p.h @@ -45,6 +45,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -65,7 +66,8 @@ struct QmlCompositeTypeData : public QmlRefCount Waiting }; Status status; - QString errorDescription; + + QList errors; QString url; QList dependants; diff --git a/src/declarative/qml/qmldom.cpp b/src/declarative/qml/qmldom.cpp index cf0a2fb..239aa7b 100644 --- a/src/declarative/qml/qmldom.cpp +++ b/src/declarative/qml/qmldom.cpp @@ -152,11 +152,11 @@ bool QmlDomDocument::load(QmlEngine *engine, const QByteArray &data) { Q_UNUSED(engine); - d->error = QString(); + d->errors.clear(); QmlScriptParser parser; if (!parser.parse(data)) { - d->error = parser.errorDescription(); + d->errors = parser.errors(); return false; } @@ -166,11 +166,13 @@ bool QmlDomDocument::load(QmlEngine *engine, const QByteArray &data) QmlCompositeTypeData *td = ((QmlEnginePrivate *)QmlEnginePrivate::get(engine))->typeManager.getImmediate(data, QUrl());; if(td->status == QmlCompositeTypeData::Error) { - d->error = td->errorDescription; + d->errors = td->errors; td->release(); return false; } else if(td->status == QmlCompositeTypeData::Waiting) { - d->error = QLatin1String("QmlDomDocument supports local types only"); + QmlError error; + error.setDescription(QLatin1String("QmlDomDocument supports local types only")); + d->errors << error; td->release(); return false; } @@ -178,7 +180,7 @@ bool QmlDomDocument::load(QmlEngine *engine, const QByteArray &data) compiler.compile(engine, td, &component); if (compiler.isError()) { - d->error = compiler.errorDescription(); + d->errors = compiler.errors(); td->release(); return false; } @@ -194,14 +196,14 @@ bool QmlDomDocument::load(QmlEngine *engine, const QByteArray &data) /*! - Returns the last load error. The load error will be reset after a + Returns the last load errors. The load errors will be reset after a successful call to load(). \sa load() */ -QString QmlDomDocument::loadError() const +QList QmlDomDocument::errors() const { - return d->error; + return d->errors; } /*! diff --git a/src/declarative/qml/qmldom.h b/src/declarative/qml/qmldom.h index 47a89d9..74ed27c 100644 --- a/src/declarative/qml/qmldom.h +++ b/src/declarative/qml/qmldom.h @@ -44,6 +44,7 @@ #include #include +#include QT_BEGIN_HEADER @@ -71,6 +72,7 @@ public: int version() const; + QList errors() const; QString loadError() const; bool load(QmlEngine *, const QByteArray &); QByteArray save() const; diff --git a/src/declarative/qml/qmldom_p.h b/src/declarative/qml/qmldom_p.h index 8ea56bf..4c3ca44 100644 --- a/src/declarative/qml/qmldom_p.h +++ b/src/declarative/qml/qmldom_p.h @@ -57,7 +57,7 @@ public: QmlDomDocumentPrivate(const QmlDomDocumentPrivate &); ~QmlDomDocumentPrivate(); - QString error; + QList errors; QmlParser::Object *root; }; diff --git a/src/declarative/qml/qmlerror.cpp b/src/declarative/qml/qmlerror.cpp new file mode 100644 index 0000000..66c834f --- /dev/null +++ b/src/declarative/qml/qmlerror.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlerror.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QmlErrorPrivate +{ +public: + QmlErrorPrivate(); + + QUrl url; + QString description; + int line; + int column; +}; + +QmlErrorPrivate::QmlErrorPrivate() +: line(-1), column(-1) +{ +} + +QmlError::QmlError() +: d(new QmlErrorPrivate) +{ +} + +QmlError::QmlError(const QmlError &other) +: d(new QmlErrorPrivate) +{ + *this = other; +} + +QmlError &QmlError::operator=(const QmlError &other) +{ + d->url = other.d->url; + d->description = other.d->description; + d->line = other.d->line; + d->column = other.d->column; + return *this; +} + +QmlError::~QmlError() +{ + delete d; d = 0; +} + +QUrl QmlError::url() const +{ + return d->url; +} + +void QmlError::setUrl(const QUrl &url) +{ + d->url = url; +} + +QString QmlError::description() const +{ + return d->description; +} + +void QmlError::setDescription(const QString &description) +{ + d->description = description; +} + +int QmlError::line() const +{ + return d->line; +} + +void QmlError::setLine(int line) +{ + d->line = line; +} + +int QmlError::column() const +{ + return d->column; +} + +void QmlError::setColumn(int column) +{ + d->column = column; +} + +QDebug operator<<(QDebug debug, const QmlError &error) +{ + QUrl url = error.url(); + + QString output; + + output = url.toString() + QLatin1String(":") + + QString::number(error.line()); + + if(error.column() != -1) + output += QLatin1String(":") + QString::number(error.column()); + + output += QLatin1String(": ") + error.description(); + + debug << qPrintable(output) << "\n"; + + if (error.line() > 0 && error.column() > 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); + const QString code = stream.readAll(); + const QStringList lines = code.split(QLatin1Char('\n')); + + if (lines.count() >= error.line()) { + const QString &line = lines.at(error.line() - 1); + debug << qPrintable(line) << "\n"; + + 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 << ind.constData(); + } + } + } + return debug; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlerror.h b/src/declarative/qml/qmlerror.h new file mode 100644 index 0000000..57d2f8f --- /dev/null +++ b/src/declarative/qml/qmlerror.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLERROR_H +#define QMLERROR_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDebug; +class QmlErrorPrivate; +class Q_DECLARATIVE_EXPORT QmlError +{ +public: + QmlError(); + QmlError(const QmlError &); + QmlError &operator=(const QmlError &); + ~QmlError(); + + 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); +private: + QmlErrorPrivate *d; +}; + +QDebug Q_DECLARATIVE_EXPORT operator<<(QDebug debug, const QmlError &error); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLERROR_H diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index ecb6f0b..87c8434 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE using namespace QmlParser; QmlParser::Object::Object() -: type(-1), metatype(0), extObject(0), defaultProperty(0), line(-1), +: type(-1), metatype(0), extObject(0), defaultProperty(0), line(-1), column(-1), dynamicPropertiesProperty(0), dynamicSignalsProperty(0) { } @@ -127,12 +127,12 @@ QmlParser::Object::DynamicSignal::DynamicSignal(const DynamicSignal &o) } QmlParser::Property::Property() -: type(0), index(-1), value(0), isDefault(true), line(-1) +: type(0), index(-1), value(0), isDefault(true), line(-1), column(-1) { } QmlParser::Property::Property(const QByteArray &n) -: type(0), index(-1), value(0), name(n), isDefault(false), line(-1) +: type(0), index(-1), value(0), name(n), isDefault(false), line(-1), column(-1) { } @@ -161,7 +161,7 @@ void QmlParser::Property::addValue(Value *v) } QmlParser::Value::Value() -: type(Unknown), object(0), line(-1) +: type(Unknown), object(0), line(-1), column(-1) { } diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index e29cdbf..17b367d6 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -103,6 +103,7 @@ namespace QmlParser QHash properties; qint64 line; + qint64 column; struct DynamicProperty { DynamicProperty(); @@ -167,6 +168,7 @@ namespace QmlParser Object *object; qint64 line; + qint64 column; }; class Property : public QmlRefCount @@ -197,6 +199,7 @@ namespace QmlParser bool isDefault; qint64 line; + qint64 column; }; } diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp index 8039b5c..618eb2e 100644 --- a/src/declarative/qml/qmlscriptparser.cpp +++ b/src/declarative/qml/qmlscriptparser.cpp @@ -65,10 +65,12 @@ protected: Object *defineObjectBinding(int line, AST::UiQualifiedId *propertyName, const QString &objectType, + AST::SourceLocation typeLocation, AST::UiObjectInitializer *initializer = 0); Object *defineObjectBinding_helper(int line, AST::UiQualifiedId *propertyName, const QString &objectType, + AST::SourceLocation typeLocation, AST::UiObjectInitializer *initializer = 0); QString getPrimitive(const QByteArray &propertyName, AST::ExpressionNode *expr); void defineProperty(const QString &propertyName, int line, const QString &primitive); @@ -194,11 +196,17 @@ QString ProcessAST::asString(AST::UiQualifiedId *node) const Object *ProcessAST::defineObjectBinding_helper(int line, AST::UiQualifiedId *propertyName, const QString &objectType, + AST::SourceLocation typeLocation, AST::UiObjectInitializer *initializer) { bool isType = !objectType.isEmpty() && objectType.at(0).isUpper() && !objectType.contains(QLatin1Char('.')); + if (!isType) { - qWarning() << "bad name for a class"; // ### FIXME + QmlError error; + error.setDescription("Expected type name"); + error.setLine(typeLocation.startLine); + error.setColumn(typeLocation.startColumn); + _parser->_errors << error; return false; } @@ -235,7 +243,7 @@ Object *ProcessAST::defineObjectBinding_helper(int line, if (!_parser->scriptFile().isEmpty()) { _stateStack.pushObject(obj); - Object *scriptObject= defineObjectBinding(line, 0, QLatin1String("Script")); + Object *scriptObject= defineObjectBinding(line, 0, QLatin1String("Script"), AST::SourceLocation()); _stateStack.pushObject(scriptObject); defineProperty(QLatin1String("src"), line, _parser->scriptFile()); _stateStack.pop(); // scriptObject @@ -264,11 +272,12 @@ Object *ProcessAST::defineObjectBinding_helper(int line, Object *ProcessAST::defineObjectBinding(int line, AST::UiQualifiedId *qualifiedId, const QString &objectType, + AST::SourceLocation typeLocation, AST::UiObjectInitializer *initializer) { if (objectType == QLatin1String("Connection")) { - Object *obj = defineObjectBinding_helper(line, 0, QLatin1String("Connection")); + Object *obj = defineObjectBinding_helper(line, 0, objectType, typeLocation); _stateStack.pushObject(obj); @@ -297,7 +306,7 @@ Object *ProcessAST::defineObjectBinding(int line, return obj; } - return defineObjectBinding_helper(line, qualifiedId, objectType, initializer); + return defineObjectBinding_helper(line, qualifiedId, objectType, typeLocation, initializer); } void ProcessAST::defineProperty(const QString &propertyName, int line, const QString &primitive) @@ -372,7 +381,11 @@ bool ProcessAST::visit(AST::UiPublicMember *node) } if(!typeFound) { - qWarning() << "Unknown property type" << memberType; // ### FIXME + QmlError error; + error.setDescription("Expected property type"); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + _parser->_errors << error; return false; } @@ -402,6 +415,7 @@ bool ProcessAST::visit(AST::UiObjectDefinition *node) defineObjectBinding(node->identifierToken.startLine, 0, node->name->asString(), + node->identifierToken, node->initializer); return false; @@ -414,6 +428,7 @@ bool ProcessAST::visit(AST::UiObjectBinding *node) defineObjectBinding(node->identifierToken.startLine, node->qualifiedId, node->name->asString(), + node->identifierToken, node->initializer); return false; @@ -476,7 +491,8 @@ bool ProcessAST::visit(AST::UiScriptBinding *node) Value *v = new Value; v->primitive = primitive; - v->line = node->colonToken.startLine; + v->line = node->statement->firstSourceLocation().startLine; + v->column = node->statement->firstSourceLocation().startColumn; prop->addValue(v); while (propertyCount--) @@ -535,7 +551,7 @@ bool ProcessAST::visit(AST::UiSourceElement *node) QmlScriptParser::QmlScriptParser() - : root(0), _errorLine(-1) +: root(0) { } @@ -556,8 +572,11 @@ bool QmlScriptParser::parse(const QByteArray &data, const QUrl &url) return true; } - _error = xmlParser.errorDescription(); - _errorLine = 0; // ### FIXME + QmlError error; + error.setUrl(url); + error.setDescription(xmlParser.errorDescription()); + _errors << error; + return false; } @@ -576,61 +595,34 @@ bool QmlScriptParser::parse(const QByteArray &data, const QUrl &url) lexer.setCode(code, /*line = */ 1); driver.setLexer(&lexer); - if (! parser.parse(&driver)) { - _error = parser.errorMessage(); - _errorLine = parser.errorLineNumber(); - - const QStringList lines = code.split(QLatin1Char('\n')); + if (! parser.parse(&driver) || !_errors.isEmpty()) { + // Extract errors from the parser foreach (const JavaScriptParser::DiagnosticMessage &m, parser.diagnosticMessages()) { if (m.isWarning()) continue; - qWarning().nospace() << qPrintable(fileName) << ":" - << m.line << ":" - << m.column << ": " - << "error: " - << qPrintable(m.message); - - const QString textLine = lines.at(m.line - 1); - - qWarning() << qPrintable(textLine); + QmlError error; + error.setUrl(url); + error.setDescription(m.message); + error.setLine(m.line); + error.setColumn(m.column); + _errors << error; - int column = qMax(0, m.column - 1); - column = qMin(column, textLine.length()); // paranoia check - - QByteArray ind; - ind.reserve(column); - - for (int i = 0; i < column; ++i) { - const QChar ch = textLine.at(i); - if (ch.isSpace()) - ind.append(ch.unicode()); - else - ind.append(' '); - } - ind.append('^'); - qWarning() << ind.constData(); } - - return false; } - ProcessAST process(this); - process(code, parser.ast()); + if (_errors.isEmpty()) { + ProcessAST process(this); + process(code, parser.ast()); - return true; -} - -QString QmlScriptParser::errorDescription() const -{ - return _error; -} + // Set the url for process errors + for(int ii = 0; ii < _errors.count(); ++ii) + _errors[ii].setUrl(url); + } -int QmlScriptParser::errorLine() const -{ - return _errorLine; + return _errors.isEmpty(); } QMap QmlScriptParser::nameSpacePaths() const @@ -648,6 +640,11 @@ Object *QmlScriptParser::tree() const return root; } +QList QmlScriptParser::errors() const +{ + return _errors; +} + void QmlScriptParser::clear() { if (root) { @@ -656,9 +653,8 @@ void QmlScriptParser::clear() } _nameSpacePaths.clear(); _typeNames.clear(); - _error.clear(); + _errors.clear(); _scriptFile.clear(); - _errorLine = 0; } int QmlScriptParser::findOrCreateTypeId(const QString &name) diff --git a/src/declarative/qml/qmlscriptparser_p.h b/src/declarative/qml/qmlscriptparser_p.h index 0d89268..4155bba 100644 --- a/src/declarative/qml/qmlscriptparser_p.h +++ b/src/declarative/qml/qmlscriptparser_p.h @@ -3,6 +3,7 @@ #include #include +#include #include QT_BEGIN_HEADER @@ -23,8 +24,6 @@ public: ~QmlScriptParser(); bool parse(const QByteArray &data, const QUrl &url = QUrl()); - QString errorDescription() const; - int errorLine() const; QMap nameSpacePaths() const; QStringList types() const; @@ -33,6 +32,8 @@ public: void clear(); + QList errors() const; + // ### private: int findOrCreateTypeId(const QString &name); void setTree(QmlParser::Object *tree); @@ -42,12 +43,12 @@ public: void addNamespacePath(const QString &path); -private: +// ### private: + QList _errors; + QMap _nameSpacePaths; QmlParser::Object *root; QStringList _typeNames; - QString _error; - int _errorLine; QString _scriptFile; }; diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index e6235e4..a3bfd62 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -176,9 +176,14 @@ QmlVME::QmlVME() #define VME_EXCEPTION(desc) \ { \ - exceptionLine = instr.line; \ - QDebug d(&exceptionDescription); \ - d << desc; \ + QString str; \ + QDebug d(&str); \ + d << desc; \ + QmlError error; \ + error.setDescription(str); \ + error.setLine(instr.line); \ + error.setUrl(comp->url); \ + vmeErrors << error; \ break; \ } @@ -224,6 +229,8 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in QStack pushedProperties; QObject **savedObjects = 0; + vmeErrors.clear(); + if (start == -1) start = 0; if (count == -1) count = comp->bytecode.count(); @@ -1072,17 +1079,12 @@ QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, in bool QmlVME::isError() const { - return exceptionLine != -1; -} - -qint64 QmlVME::errorLine() const -{ - return exceptionLine; + return !vmeErrors.isEmpty(); } -QString QmlVME::errorDescription() const +QList QmlVME::errors() const { - return exceptionDescription; + return vmeErrors; } void QmlVME::runStoreInstruction(QStack &stack, diff --git a/src/declarative/qml/qmlvme_p.h b/src/declarative/qml/qmlvme_p.h index 2a3be06..86cd040 100644 --- a/src/declarative/qml/qmlvme_p.h +++ b/src/declarative/qml/qmlvme_p.h @@ -44,6 +44,7 @@ #include #include +#include class QObject; QT_BEGIN_NAMESPACE @@ -60,13 +61,13 @@ public: QObject *run(QmlContext *, QmlCompiledComponent *, int start = -1, int end = -1); bool isError() const; - qint64 errorLine() const; - QString errorDescription() const; + QList errors() const; private: void runStoreInstruction(QStack &stack, QmlInstruction &, QmlCompiledData *); + QList vmeErrors; qint64 exceptionLine; QString exceptionDescription; }; diff --git a/src/declarative/util/qfxview.cpp b/src/declarative/util/qfxview.cpp index cac73a0..f71b87e 100644 --- a/src/declarative/util/qfxview.cpp +++ b/src/declarative/util/qfxview.cpp @@ -233,8 +233,6 @@ QmlContext* QFxView::rootContext() */ void QFxView::execute() { - rootContext()->activate(); - if (d->qml.isEmpty()) { d->component = new QmlComponent(&d->engine, d->source, this); } else { @@ -249,6 +247,45 @@ void QFxView::execute() } /*! + \internal +*/ +void QFxView::printErrorLine(const QmlError &error) +{ + QUrl url = error.url(); + if (error.line() > 0 && error.column() > 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); + const QString code = stream.readAll(); + const QStringList lines = code.split(QLatin1Char('\n')); + + if (lines.count() >= error.line()) { + const QString &line = lines.at(error.line() - 1); + qWarning() << qPrintable(line); + + 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('^'); + qWarning() << ind.constData(); + } + } + } +} + +/*! \internal */ void QFxView::continueExecute() @@ -260,8 +297,26 @@ void QFxView::continueExecute() return; } + if(d->component->isError()) { + QList errors = d->component->errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + + return; + } + QObject *obj = d->component->create(); - rootContext()->deactivate(); + + if(d->component->isError()) { + QList errors = d->component->errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + + return; + } + if (obj) { if (QFxItem *item = qobject_cast(obj)) { item->QSimpleCanvasItem::setParent(QSimpleCanvas::root()); @@ -330,7 +385,25 @@ QFxItem* QFxView::addItem(const QString &qml, QFxItem* parent) return 0; QmlComponent component(&d->engine, qml.toUtf8(), QUrl()); + if(d->component->isError()) { + QList errors = d->component->errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + + return 0; + } + QObject *obj = component.create(); + if(d->component->isError()) { + QList errors = d->component->errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + + return 0; + } + if (obj){ QFxItem *item = static_cast(obj); if (!parent) diff --git a/src/declarative/util/qfxview.h b/src/declarative/util/qfxview.h index d2cacf4..f575f27 100644 --- a/src/declarative/util/qfxview.h +++ b/src/declarative/util/qfxview.h @@ -57,7 +57,7 @@ QT_MODULE(Declarative) class QFxItem; class QmlEngine; class QmlContext; -class Canvas; +class QmlError; class QFxViewPrivate; class Q_DECLARATIVE_EXPORT QFxView : public QSimpleCanvas @@ -84,6 +84,7 @@ public: void dumpRoot(); + static void printErrorLine(const QmlError &); Q_SIGNALS: void sceneResized(QSize size); diff --git a/tools/qmlviewer/qmlviewer.cpp b/tools/qmlviewer/qmlviewer.cpp index 3c52cfe..00cb7f1 100644 --- a/tools/qmlviewer/qmlviewer.cpp +++ b/tools/qmlviewer/qmlviewer.cpp @@ -85,6 +85,14 @@ void QmlViewer::openQml(const QString& fileName) QmlComponent comp(canvas->engine()); comp.setData(data, QUrl()); QObject *dummyData = comp.create(); + + if(comp.isError()) { + QList errors = comp.errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + } + if (dummyData) { qWarning() << "Loaded dummy data:" << dir.filePath(qml); qml.truncate(qml.length()-4); -- cgit v0.12