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 
53 QT_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 */
80 class QQmlErrorPrivate
81 {
82 public:
83     QUrl url;
84     QPointer<QObject> object;
85     QString message;
86     QtMsgType type = QtWarningMsg;
87     int line = -1;
88     int column = -1;
89 };
90 
91 /*!
92     Creates an empty error object.
93 */
QQmlError()94 QQmlError::QQmlError()
95 : d(nullptr)
96 {
97 }
98 
99 /*!
100     Creates a copy of \a other.
101 */
QQmlError(const QQmlError & other)102 QQmlError::QQmlError(const QQmlError &other)
103 : d(nullptr)
104 {
105     *this = other;
106 }
107 
108 /*!
109     Assigns \a other to this error object.
110 */
operator =(const QQmlError & other)111 QQmlError &QQmlError::operator=(const QQmlError &other)
112 {
113     if (!other.d) {
114         delete d;
115         d = nullptr;
116     } else {
117         if (!d)
118             d = new QQmlErrorPrivate;
119         d->url = other.d->url;
120         d->message = other.d->message;
121         d->line = other.d->line;
122         d->column = other.d->column;
123         d->object = other.d->object;
124         d->type = other.d->type;
125     }
126     return *this;
127 }
128 
129 /*!
130     \internal
131 */
~QQmlError()132 QQmlError::~QQmlError()
133 {
134     delete d; d = nullptr;
135 }
136 
137 /*!
138     Returns true if this error is valid, otherwise false.
139 */
isValid() const140 bool QQmlError::isValid() const
141 {
142     return d != nullptr;
143 }
144 
145 /*!
146     Returns the url for the file that caused this error.
147 */
url() const148 QUrl QQmlError::url() const
149 {
150     if (d)
151         return d->url;
152     return QUrl();
153 }
154 
155 /*!
156     Sets the \a url for the file that caused this error.
157 */
setUrl(const QUrl & url)158 void QQmlError::setUrl(const QUrl &url)
159 {
160     if (!d)
161         d = new QQmlErrorPrivate;
162     d->url = url;
163 }
164 
165 /*!
166     Returns the error description.
167 */
description() const168 QString QQmlError::description() const
169 {
170     if (d)
171         return d->message;
172     return QString();
173 }
174 
175 /*!
176     Sets the error \a description.
177 */
setDescription(const QString & description)178 void QQmlError::setDescription(const QString &description)
179 {
180     if (!d)
181         d = new QQmlErrorPrivate;
182     d->message = description;
183 }
184 
185 /*!
186     Returns the error line number.
187 */
line() const188 int QQmlError::line() const
189 {
190     if (d)
191         return d->line;
192     return -1;
193 }
194 
195 /*!
196     Sets the error \a line number.
197 */
setLine(int line)198 void QQmlError::setLine(int line)
199 {
200     if (!d)
201         d = new QQmlErrorPrivate;
202     d->line = line;
203 }
204 
205 /*!
206     Returns the error column number.
207 */
column() const208 int QQmlError::column() const
209 {
210     if (d)
211         return d->column;
212     return -1;
213 }
214 
215 /*!
216     Sets the error \a column number.
217 */
setColumn(int column)218 void QQmlError::setColumn(int column)
219 {
220     if (!d)
221         d = new QQmlErrorPrivate;
222     d->column = column;
223 }
224 
225 /*!
226     Returns the nearest object where this error occurred.
227     Exceptions in bound property expressions set this to the object
228     to which the property belongs. It will be 0 for all
229     other exceptions.
230  */
object() const231 QObject *QQmlError::object() const
232 {
233     if (d)
234         return d->object;
235     return nullptr;
236 }
237 
238 /*!
239     Sets the nearest \a object where this error occurred.
240  */
setObject(QObject * object)241 void QQmlError::setObject(QObject *object)
242 {
243     if (!d)
244         d = new QQmlErrorPrivate;
245     d->object = object;
246 }
247 
248 /*!
249     \since 5.9
250 
251     Returns the message type.
252  */
messageType() const253 QtMsgType QQmlError::messageType() const
254 {
255     if (d)
256         return d->type;
257     return QtMsgType::QtWarningMsg;
258 }
259 
260 /*!
261     \since 5.9
262 
263     Sets the \a messageType for this message. The message type determines which
264     QDebug handlers are responsible for receiving the message.
265  */
setMessageType(QtMsgType messageType)266 void QQmlError::setMessageType(QtMsgType messageType)
267 {
268     if (!d)
269         d = new QQmlErrorPrivate;
270     d->type = messageType;
271 }
272 
273 /*!
274     Returns the error as a human readable string.
275 */
toString() const276 QString QQmlError::toString() const
277 {
278     QString rv;
279 
280     QUrl u(url());
281     int l(line());
282 
283     if (u.isEmpty() || (u.isLocalFile() && u.path().isEmpty()))
284         rv += QLatin1String("<Unknown File>");
285     else
286         rv += u.toString();
287 
288     if (l != -1) {
289         rv += QLatin1Char(':') + QString::number(l);
290 
291         int c(column());
292         if (c != -1)
293             rv += QLatin1Char(':') + QString::number(c);
294     }
295 
296     rv += QLatin1String(": ") + description();
297 
298     return rv;
299 }
300 
301 /*!
302     \relates QQmlError
303     \fn QDebug operator<<(QDebug debug, const QQmlError &error)
304 
305     Outputs a human readable version of \a error to \a debug.
306 */
307 
operator <<(QDebug debug,const QQmlError & error)308 QDebug operator<<(QDebug debug, const QQmlError &error)
309 {
310     debug << qPrintable(error.toString());
311 
312     QUrl url = error.url();
313 
314     if (error.line() > 0 && (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))) {
315         QString file = QQmlFile::urlToLocalFileOrQrc(url);
316         QFile f(file);
317         if (f.open(QIODevice::ReadOnly)) {
318             QByteArray data = f.readAll();
319             QTextStream stream(data, QIODevice::ReadOnly);
320 #if QT_CONFIG(textcodec)
321             stream.setCodec("UTF-8");
322 #endif
323             const QString code = stream.readAll();
324             const auto lines = code.splitRef(QLatin1Char('\n'));
325 
326             if (lines.count() >= error.line()) {
327                 const QStringRef &line = lines.at(error.line() - 1);
328                 debug << "\n    " << line.toLocal8Bit().constData();
329 
330                 if(error.column() > 0) {
331                     int column = qMax(0, error.column() - 1);
332                     column = qMin(column, line.length());
333 
334                     QByteArray ind;
335                     ind.reserve(column);
336                     for (int i = 0; i < column; ++i) {
337                         const QChar ch = line.at(i);
338                         if (ch.isSpace())
339                             ind.append(ch.unicode());
340                         else
341                             ind.append(' ');
342                     }
343                     ind.append('^');
344                     debug << "\n    " << ind.constData();
345                 }
346             }
347         }
348     }
349     return debug;
350 }
351 
352 QT_END_NAMESPACE
353