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

source code of qtdeclarative/src/qml/qml/qqmlerror.cpp