1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlerror.h"
41#include "qqmlfile.h"
42#include "qqmlsourcecoordinate_p.h"
43#include <private/qqmljsdiagnosticmessage_p.h>
44
45#include <QtCore/qdebug.h>
46#include <QtCore/qfile.h>
47#include <QtCore/qstringlist.h>
48#include <QtCore/qvector.h>
49
50#include <QtCore/qobject.h>
51#include <QtCore/qpointer.h>
52
53QT_BEGIN_NAMESPACE
54
55/*!
56 \class QQmlError
57 \since 5.0
58 \inmodule QtQml
59 \brief The QQmlError class encapsulates a QML error.
60
61 QQmlError includes a textual description of the error, as well
62 as location information (the file, line, and column). The toString()
63 method creates a single-line, human-readable string containing all of
64 this information, for example:
65 \code
66 file:///home/user/test.qml:7:8: Invalid property assignment: double expected
67 \endcode
68
69 You can use qDebug(), qInfo(), or qWarning() to output errors to the console.
70 This method will attempt to open the file indicated by the error
71 and include additional contextual information.
72 \code
73 file:///home/user/test.qml:7:8: Invalid property assignment: double expected
74 y: "hello"
75 ^
76 \endcode
77
78 \sa QQuickView::errors(), QQmlComponent::errors()
79*/
80class QQmlErrorPrivate : public QQmlJS::DiagnosticMessage
81{
82public:
83 QQmlErrorPrivate() { type = QtWarningMsg; }
84 QUrl url;
85 QPointer<QObject> object;
86};
87
88/*!
89 Creates an empty error object.
90*/
91QQmlError::QQmlError()
92: d(nullptr)
93{
94}
95
96/*!
97 Creates a copy of \a other.
98*/
99QQmlError::QQmlError(const QQmlError &other)
100: d(nullptr)
101{
102 *this = other;
103}
104
105/*!
106 Assigns \a other to this error object.
107*/
108QQmlError &QQmlError::operator=(const QQmlError &other)
109{
110 if (!other.d) {
111 delete d;
112 d = nullptr;
113 } else {
114 if (!d)
115 d = new QQmlErrorPrivate;
116 d->url = other.d->url;
117 d->message = other.d->message;
118 d->line = other.d->line;
119 d->column = other.d->column;
120 d->object = other.d->object;
121 d->type = other.d->type;
122 }
123 return *this;
124}
125
126/*!
127 \internal
128*/
129QQmlError::~QQmlError()
130{
131 delete d; d = nullptr;
132}
133
134/*!
135 Returns true if this error is valid, otherwise false.
136*/
137bool QQmlError::isValid() const
138{
139 return d != nullptr;
140}
141
142/*!
143 Returns the url for the file that caused this error.
144*/
145QUrl QQmlError::url() const
146{
147 if (d)
148 return d->url;
149 return QUrl();
150}
151
152/*!
153 Sets the \a url for the file that caused this error.
154*/
155void QQmlError::setUrl(const QUrl &url)
156{
157 if (!d)
158 d = new QQmlErrorPrivate;
159 d->url = url;
160}
161
162/*!
163 Returns the error description.
164*/
165QString QQmlError::description() const
166{
167 if (d)
168 return d->message;
169 return QString();
170}
171
172/*!
173 Sets the error \a description.
174*/
175void QQmlError::setDescription(const QString &description)
176{
177 if (!d)
178 d = new QQmlErrorPrivate;
179 d->message = description;
180}
181
182/*!
183 Returns the error line number.
184*/
185int QQmlError::line() const
186{
187 if (d)
188 return qmlConvertSourceCoordinate<quint32, int>(d->line);
189 return -1;
190}
191
192/*!
193 Sets the error \a line number.
194*/
195void QQmlError::setLine(int line)
196{
197 if (!d)
198 d = new QQmlErrorPrivate;
199 d->line = qmlConvertSourceCoordinate<int, quint32>(line);
200}
201
202/*!
203 Returns the error column number.
204*/
205int QQmlError::column() const
206{
207 if (d)
208 return qmlConvertSourceCoordinate<quint32, int>(d->column);
209 return -1;
210}
211
212/*!
213 Sets the error \a column number.
214*/
215void QQmlError::setColumn(int column)
216{
217 if (!d)
218 d = new QQmlErrorPrivate;
219 d->column = qmlConvertSourceCoordinate<int, quint32>(column);
220}
221
222/*!
223 Returns the nearest object where this error occurred.
224 Exceptions in bound property expressions set this to the object
225 to which the property belongs. It will be 0 for all
226 other exceptions.
227 */
228QObject *QQmlError::object() const
229{
230 if (d)
231 return d->object;
232 return nullptr;
233}
234
235/*!
236 Sets the nearest \a object where this error occurred.
237 */
238void QQmlError::setObject(QObject *object)
239{
240 if (!d)
241 d = new QQmlErrorPrivate;
242 d->object = object;
243}
244
245/*!
246 \since 5.9
247
248 Returns the message type.
249 */
250QtMsgType QQmlError::messageType() const
251{
252 if (d)
253 return d->type;
254 return QtMsgType::QtWarningMsg;
255}
256
257/*!
258 \since 5.9
259
260 Sets the \a messageType for this message. The message type determines which
261 QDebug handlers are responsible for receiving the message.
262 */
263void QQmlError::setMessageType(QtMsgType messageType)
264{
265 if (!d)
266 d = new QQmlErrorPrivate;
267 d->type = messageType;
268}
269
270/*!
271 Returns the error as a human readable string.
272*/
273QString QQmlError::toString() const
274{
275 QString rv;
276
277 QUrl u(url());
278 int l(line());
279
280 if (u.isEmpty() || (u.isLocalFile() && u.path().isEmpty()))
281 rv += QLatin1String("<Unknown File>");
282 else
283 rv += u.toString();
284
285 if (l != -1) {
286 rv += QLatin1Char(':') + QString::number(l);
287
288 int c(column());
289 if (c != -1)
290 rv += QLatin1Char(':') + QString::number(c);
291 }
292
293 rv += QLatin1String(": ") + description();
294
295 return rv;
296}
297
298/*!
299 \relates QQmlError
300 \fn QDebug operator<<(QDebug debug, const QQmlError &error)
301
302 Outputs a human readable version of \a error to \a debug.
303*/
304
305QDebug operator<<(QDebug debug, const QQmlError &error)
306{
307 debug << qPrintable(error.toString());
308
309 QUrl url = error.url();
310
311 if (error.line() > 0 && (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))) {
312 QString file = QQmlFile::urlToLocalFileOrQrc(url);
313 QFile f(file);
314 if (f.open(QIODevice::ReadOnly)) {
315 QByteArray data = f.readAll();
316 QTextStream stream(data, QIODevice::ReadOnly);
317#if QT_CONFIG(textcodec)
318 stream.setCodec("UTF-8");
319#endif
320 const QString code = stream.readAll();
321 const auto lines = code.splitRef(QLatin1Char('\n'));
322
323 if (lines.count() >= error.line()) {
324 const QStringRef &line = lines.at(error.line() - 1);
325 debug << "\n " << line.toLocal8Bit().constData();
326
327 if(error.column() > 0) {
328 int column = qMax(0, error.column() - 1);
329 column = qMin(column, line.length());
330
331 QByteArray ind;
332 ind.reserve(column);
333 for (int i = 0; i < column; ++i) {
334 const QChar ch = line.at(i);
335 if (ch.isSpace())
336 ind.append(ch.unicode());
337 else
338 ind.append(' ');
339 }
340 ind.append('^');
341 debug << "\n " << ind.constData();
342 }
343 }
344 }
345 }
346 return debug;
347}
348
349QT_END_NAMESPACE
350