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 | |
16 | QT_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 | */ |
43 | class QQmlErrorPrivate |
44 | { |
45 | public: |
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 | */ |
63 | QQmlError::QQmlError() |
64 | : d(nullptr) |
65 | { |
66 | } |
67 | |
68 | /*! |
69 | Creates a copy of \a other. |
70 | */ |
71 | QQmlError::QQmlError(const QQmlError &other) |
72 | : d(nullptr) |
73 | { |
74 | *this = other; |
75 | } |
76 | |
77 | /*! |
78 | Assigns \a other to this error object. |
79 | */ |
80 | QQmlError &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 | */ |
101 | QQmlError::~QQmlError() |
102 | { |
103 | delete d; d = nullptr; |
104 | } |
105 | |
106 | /*! |
107 | Returns true if this error is valid, otherwise false. |
108 | */ |
109 | bool QQmlError::isValid() const |
110 | { |
111 | return d != nullptr; |
112 | } |
113 | |
114 | /*! |
115 | Returns the url for the file that caused this error. |
116 | */ |
117 | QUrl 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 | */ |
127 | void 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 | */ |
137 | QString QQmlError::description() const |
138 | { |
139 | if (d) |
140 | return d->message; |
141 | return QString(); |
142 | } |
143 | |
144 | /*! |
145 | Sets the error \a description. |
146 | */ |
147 | void 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 | */ |
157 | int 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 | */ |
167 | void 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 | */ |
177 | int 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 | */ |
187 | void 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 | */ |
200 | QObject *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 | */ |
210 | void 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 | */ |
222 | QtMsgType 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 | */ |
235 | void 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 | */ |
245 | QString 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 | |
270 | bool 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 | |
282 | QDebug 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 | |
323 | QT_END_NAMESPACE |
324 | |