Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldomerrormessage.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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// Qt-Security score:significant
4
8
9#include <QtCore/QCborMap>
10#include <QtCore/QMutex>
11#include <QtCore/QMutexLocker>
12
14
15Q_LOGGING_CATEGORY(domLog, "qt.qmldom", QtWarningMsg);
16
17namespace QQmlJS {
18namespace Dom {
19
20enum {
22};
23
24/*!
25\internal
26\macro NewErrorGroup
27
28\param groupId a double qouted string giving the groupId for this group
29
30\brief convenience macro creating a new ErrorGroup and registering its groupId as translatable string
31*/
32
33/*!
34\internal
35\class QQmlJS::Dom::ErrorGroup
36\brief Represents a tag grouping a set of related error messages, it can be used to disable them
37
38Every group has a unique string identifying it (the \l{groupId}), and it should be a string that can
39be translated to get the local name. The best way to acheive this is to create new groups using
40the NewErrorGroup macro.
41 */
42void ErrorGroup::dump(const Sink &sink) const
43{
44 sink(u"[");
45 sink(groupName());
46 sink(u"]");
47}
48
49void ErrorGroup::dumpId(const Sink &sink) const
50{
51 sink(u"[");
52 sink(QString(groupId()));
53 sink(u"]");
54}
55
57{
58 return QLatin1String(m_groupId);
59}
60
62{
63 return tr(m_groupId);
64}
65
66/*!
67\internal
68\class QQmlJS::Dom::ErrorGroups
69\brief Represents a set of tags grouping a set of related error messages
70
71The simplest way to create new ErrorMessages is to have an ErrorGroups instance,
72and use it to create new ErrorMessages using its debug, warning, error,... methods
73 */
74
75void ErrorGroups::dump(const Sink &sink) const
76{
77 for (int i = 0; i < groups.size(); ++i)
78 groups.at(i).dump(sink);
79}
80
81void ErrorGroups::dumpId(const Sink &sink) const
82{
83 for (int i = 0; i < groups.size(); ++i)
84 groups.at(i).dumpId(sink);
85}
86
88{
89 QCborArray res;
90 for (int i = 0; i < groups.size(); ++i)
91 res.append(QCborValue(groups.at(i).groupId()));
92 return res;
93}
94
95/*!
96\internal
97\class QQmlJS::Dom::ErrorMessage
98\brief Represents an error message connected to the dom
99
100The error messages *should* be translated, but they do not need to be pre registered.
101To give a meaningful handling of error messages ErrorMessages have "tags" (ErrorGroup) that are
102grouped toghether in ErrorGroups.
103
104To create an ErrorMessage from scratch the best way is to use one of the methods provided by
105an ErrorGroups object.
106For example create an ErrorGroups called myErrors and use it to create all your errors.
107\code
108static ErrorGroups myErrors(){
109 static ErrorGroups res({NewErrorGroup("StaticAnalysis"), NewErrorGroup("FancyDetector")});
110 return res;
111}
112\endcode
113
114You can preregister the errors giving them a unique name (reverse dns notation is encouraged) with
115the msg function.
116This unique name (errorId) is a const char* (QLatin1String) to integrate better with the tr function.
117Ideally you create variables to store the errorId either by creating variables with plain strings
118that you use to initialize the error messages
119\code
120// in .h file
121constexpr const char *myError0 = "my.company.error0";
122// in some initialization function
123ErrorMessage::msg(myError0, myErrors().warning(tr("Error number 0")));
124\endcode
125or using the result of the msg function
126\code
127// in cpp file
128static auto myError1 = ErrorMessage::msg("my.company.error1", myErrors().warning(tr("Error number 1")));
129static auto myError2 = ErrorMessage::msg("my.company.error2", myErrors().error(tr("Error number 2 on %1")));
130\endcode
131and then use them like this
132\code
133ErrorMessage::load(myError2, QLatin1String("extra info")).handle(errorHandler);
134\endcode
135or using directly the string (more error prone)
136\code
137errorHandler(ErrorMessage::load(QLatin1String("my.company.error1")));
138\endcode
139
140The \l{withItem} method can be used to set the path file and location if not aready set.
141 */
142
144 const Dumper &msg, ErrorLevel level, const Path &element, const QString &canonicalFilePath,
145 SourceLocation location) const
146{
147 if (level == ErrorLevel::Fatal)
148 fatal(msg, element, canonicalFilePath, location);
149 return ErrorMessage(dumperToString(msg), *this, level, element, canonicalFilePath, location);
150}
151
152ErrorMessage ErrorGroups::errorMessage(const DiagnosticMessage &msg, const Path &element, const QString &canonicalFilePath) const
153{
154 ErrorMessage res(*this, msg, element, canonicalFilePath);
155 if (res.location == SourceLocation()
156 && (res.location.startLine != 0 || res.location.startColumn != 0)) {
157 res.location.offset = -1;
158 res.location.length = 1;
159 }
160 return res;
161}
162
164 const Dumper &msg, const Path &element, QStringView canonicalFilePath,
165 SourceLocation location) const
166{
167 enum { FatalMsgMaxLen = 1023 };
168 char buf[FatalMsgMaxLen+1];
169 int ibuf = 0;
170 auto sink = [&ibuf, &buf](QStringView s) {
171 int is = 0;
172 while (ibuf < FatalMsgMaxLen && is < s.size()) {
173 QChar c = s.at(is);
174 if (c == QChar::fromLatin1('\n') || c == QChar::fromLatin1('\r') || (c >= QChar::fromLatin1(' ') && c <= QChar::fromLatin1('~')))
175 buf[ibuf++] = c.toLatin1();
176 else
177 buf[ibuf++] = '~';
178 ++is;
179 }
180 };
181 if (!canonicalFilePath.isEmpty()) {
182 sink(canonicalFilePath);
183 sink(u":");
184 }
185 if (location.length) {
186 sinkInt(sink, location.startLine);
187 sink(u":");
188 sinkInt(sink, location.startColumn);
189 sink(u":");
190 }
191 dump(sink);
192 msg(sink);
193 if (element.length()>0) {
194 sink(u" for ");
195 element.dump(sink);
196 }
197 buf[ibuf] = 0;
198 qFatal("%s", buf);
199}
200
201ErrorMessage ErrorGroups::debug(const QString &message) const
202{
203 return ErrorMessage(message, *this, ErrorLevel::Debug);
204}
205
206ErrorMessage ErrorGroups::debug(const Dumper &message) const
207{
208 return ErrorMessage(dumperToString(message), *this, ErrorLevel::Debug);
209}
210
211ErrorMessage ErrorGroups::info(const QString &message) const
212{
213 return ErrorMessage(message, *this, ErrorLevel::Info);
214}
215
216ErrorMessage ErrorGroups::info(const Dumper &message) const
217{
218 return ErrorMessage(dumperToString(message), *this, ErrorLevel::Info);
219}
220
221ErrorMessage ErrorGroups::warning(const QString &message) const
222{
223 return ErrorMessage(message, *this, ErrorLevel::Warning);
224}
225
227{
228 return ErrorMessage(dumperToString(message), *this, ErrorLevel::Warning);
229}
230
231ErrorMessage ErrorGroups::error(const QString &message) const
232{
233 return ErrorMessage(message, *this, ErrorLevel::Error);
234}
235
236ErrorMessage ErrorGroups::error(const Dumper &message) const
237{
238 return ErrorMessage(dumperToString(message), *this, ErrorLevel::Error);
239}
240
241int ErrorGroups::cmp(const ErrorGroups &o1, const ErrorGroups &o2)
242{
243 auto &g1 = o1.groups;
244 auto &g2 = o2.groups;
245 if (g1.size() < g2.size())
246 return -1;
247 if (g1.size() < g2.size())
248 return 1;
249 for (int i = 0; i < g1.size(); ++i) {
250 int c = std::strcmp(g1.at(i).groupId().data(), g2.at(i).groupId().data());
251 if (c != 0)
252 return c;
253 }
254 return 0;
255}
256
258 const QString &msg, const ErrorGroups &errorGroups, Level level, const Path &element,
259 const QString &canonicalFilePath, SourceLocation location, QLatin1String errorId)
261 , message(msg)
263 , level(level)
264 , path(element)
267{
268 if (level == Level::Fatal) // we should not end up here, it should have been handled at a higher level already
269 errorGroups.fatal(msg, element, canonicalFilePath, location);
270}
271
273 const ErrorGroups &errorGroups, const DiagnosticMessage &msg, const Path &element,
274 const QString &canonicalFilePath, QLatin1String errorId)
279 , path(element)
281 , location(msg.loc)
282{
283 if (level == Level::Fatal) // we should not end up here, it should have been handled at a higher level already
284 errorGroups.fatal(msg.message, element, canonicalFilePath, location);
285}
286
287
289{
290 static QBasicMutex rMutex{};
291 return &rMutex;
292}
293
294
296{
297 static ErrorGroups g = {{NewErrorGroup("ErrorMessage")}};
298 return g;
299}
300
304 {}
305
307 msg(e)
308 {}
309
311 msg(std::move(e))
312 {}
313
315};
316
318{
319 static QHash<QLatin1String, StorableMsg> r;
320 return r;
321}
322
323QLatin1String ErrorMessage::msg(const char *errorId, ErrorMessage &&err)
324{
325 return msg(QLatin1String(errorId), std::move(err));
326}
327
328QLatin1String ErrorMessage::msg(QLatin1String errorId, ErrorMessage &&err)
329{
330 using namespace Qt::StringLiterals;
331 bool doubleRegister = false;
332 ErrorMessage old = myErrors().debug(u"dummy"_sv);
333 {
334 QMutexLocker l(registryMutex());
335 auto &r = registry();
336 if (r.contains(err.errorId)) {
337 old = r[err.errorId].msg;
338 doubleRegister = true;
339 }
340 r[errorId] = StorableMsg{std::move(err.withErrorId(errorId))};
341 }
342 if (doubleRegister)
343 defaultErrorHandler(myErrors().warning(tr("Double registration of error %1: (%2) vs (%3)").arg(errorId, err.withErrorId(errorId).toString(), old.toString())));
344 return errorId;
345}
346
347void ErrorMessage::visitRegisteredMessages(function_ref<bool(const ErrorMessage &)> visitor)
348{
349 QHash<QLatin1String, StorableMsg> r;
350 {
351 QMutexLocker l(registryMutex());
352 r = registry();
353 }
354 auto it = r.cbegin();
355 auto end = r.cend();
356 while (it != end) {
357 visitor(it->msg);
358 ++it;
359 }
360}
361
362ErrorMessage ErrorMessage::load(QLatin1String errorId)
363{
364 ErrorMessage res = myErrors().error([errorId](const Sink &s){
365 s(u"Unregistered error ");
366 s(QString(errorId)); });
367 {
368 QMutexLocker l(registryMutex());
369 res = registry().value(errorId,res).msg;
370 }
371 return res;
372}
373
374ErrorMessage ErrorMessage::load(const char *errorId)
375{
376 return load(QLatin1String(errorId));
377}
378
379ErrorMessage &ErrorMessage::withErrorId(QLatin1String errorId)
380{
381 this->errorId = errorId;
382 return *this;
383}
384
386{
387 this->path = path;
388 return *this;
389}
390
392{
393 file=f;
394 return *this;
395}
396
398{
399 file = f.toString();
400 return *this;
401}
402
404{
405 location = loc;
406 return *this;
407}
408
410{
411 if (path.length() == 0)
412 path = el.canonicalPath();
413 if (file.isEmpty())
414 file = el.canonicalFilePath();
415 if (location == SourceLocation()) {
416 if (const FileLocations::Tree tree = FileLocations::treeOf(el)) {
417 location = FileLocations::region(tree, MainRegion);
418 }
419 }
420 return *this;
421}
422
424{
425 if (errorHandler)
426 errorHandler(*this);
427 else
429 return *this;
430}
431
432void ErrorMessage::dump(const Sink &sink) const
433{
434 if (!file.isEmpty()) {
435 sink(file);
436 sink(u":");
437 }
438 if (location.length) {
439 sinkInt(sink, location.startLine);
440 sink(u":");
441 sinkInt(sink, location.startColumn);
442 sink(u": ");
443 }
444 errorGroups.dump(sink);
445 sink(u" ");
446 dumpErrorLevel(sink, level);
447 if (! errorId.isEmpty()) {
448 sink(u" ");
449 sink(QString(errorId));
450 }
451 sink(u": ");
452 sink(message);
453 if (path.length()>0) {
454 sink(u" for ");
455 if (!file.isEmpty() && path.length() > 3 && path.headKind() == Path::Kind::Root)
456 path.mid(3).dump(sink);
457 else
458 path.dump(sink);
459 }
460}
461
463{
464 return dumperToString([this](const Sink &sink){ this->dump(sink); });
465}
466
468{
469 return QCborMap({
470 {QStringLiteral(u"errorId"),errorId},
471 {QStringLiteral(u"message"), message},
472 {QStringLiteral(u"errorGroups"), errorGroups.toCbor()},
473 {QStringLiteral(u"level"), int(level)},
474 {QStringLiteral(u"path"), path.toString()},
475 {QStringLiteral(u"file"), file},
476 {QStringLiteral(u"location"), QCborMap({
477 {QStringLiteral(u"offset"),location.offset},
478 {QStringLiteral(u"length"),location.length},
479 {QStringLiteral(u"startLine"),location.startLine},
480 {QStringLiteral(u"startColumn"),location.startColumn}})}
481 });
482}
483
484/*!
485 * \internal
486 * \brief writes an ErrorMessage to QDebug
487 * \param error the error to write
488 */
489void errorToQDebug(const ErrorMessage &error)
490{
491 dumperToQDebug([&error](const Sink &s){ error.dump(s); }, error.level);
492}
493
494/*!
495 * \internal
496 * \brief Error handler that ignores all errors (excluding fatal ones)
497 */
499{
500}
501
502void errorHandlerHandler(const ErrorMessage &msg, const ErrorHandler *h = nullptr)
503{
504 static ErrorHandler handler = &errorToQDebug;
505 if (h) {
506 handler = *h;
507 } else {
508 handler(msg);
509 }
510}
511
512/*!
513 * \internal
514 * \brief Calls the default error handler (by default errorToQDebug)
515 */
517{
519}
520
521/*!
522 * \internal
523 * \brief Sets the default error handler
524 */
526{
527 errorHandlerHandler(ErrorMessage(QString(), ErrorGroups({})), &h);
528}
529
531{
532 switch (msgType) {
533 case QtFatalMsg:
534 return ErrorLevel::Fatal;
535 case QtCriticalMsg:
536 return ErrorLevel::Error;
537 case QtWarningMsg:
538 return ErrorLevel::Warning;
539 case QtInfoMsg:
540 return ErrorLevel::Info;
541 case QtDebugMsg:
542 return ErrorLevel::Debug;
543 default:
544 return ErrorLevel::Error;
545 }
546}
547
548} // end namespace Dom
549} // end namespace QQmlJS
550
551QT_END_NAMESPACE
552
553#include "moc_qqmldomerrormessage_p.cpp"
A value type that references any element of the Dom.
Helper class to accept eithe a string or a dumper (a function that writes to a sink)
convenience macro creating a new ErrorGroup and registering its groupId as translatable string
QLatin1String groupId() const
void dumpId(const Sink &sink) const
Represents a set of tags grouping a set of related error messages.
ErrorMessage debug(const Dumper &message) const
ErrorMessage error(const QString &message) const
ErrorMessage errorMessage(const DiagnosticMessage &msg, const Path &element=Path(), const QString &canonicalFilePath=QString()) const
ErrorMessage warning(const Dumper &message) const
static int cmp(const ErrorGroups &g1, const ErrorGroups &g2)
ErrorMessage debug(const QString &message) const
void dumpId(const Sink &sink) const
ErrorMessage info(const QString &message) const
ErrorMessage warning(const QString &message) const
ErrorMessage errorMessage(const Dumper &msg, ErrorLevel level, const Path &element=Path(), const QString &canonicalFilePath=QString(), SourceLocation location=SourceLocation()) const
ErrorMessage info(const Dumper &message) const
void fatal(const Dumper &msg, const Path &element=Path(), QStringView canonicalFilePath=u"", SourceLocation location=SourceLocation()) const
ErrorMessage error(const Dumper &message) const
Represents an error message connected to the dom.
ErrorMessage & withPath(const Path &)
ErrorMessage & withErrorId(QLatin1String errorId)
ErrorMessage(const ErrorGroups &errorGroups, const DiagnosticMessage &msg, const Path &path=Path(), const QString &file=QString(), QLatin1String errorId=QLatin1String(""))
ErrorMessage & withLocation(SourceLocation)
ErrorMessage handle(const ErrorHandler &errorHandler=nullptr)
ErrorMessage & withFile(const QString &)
ErrorMessage & withItem(const DomItem &)
static ErrorMessage load(const char *errorId)
ErrorMessage(const QString &message, const ErrorGroups &errorGroups, Level level=Level::Warning, const Path &path=Path(), const QString &file=QString(), SourceLocation location=SourceLocation(), QLatin1String errorId=QLatin1String(""))
void dump(const Sink &s) const
ErrorMessage & withFile(QStringView)
QMLDOM_EXPORT void errorToQDebug(const ErrorMessage &)
writes an ErrorMessage to QDebug
static QHash< QLatin1String, StorableMsg > & registry()
QMLDOM_EXPORT void silentError(const ErrorMessage &)
Error handler that ignores all errors (excluding fatal ones)
QMLDOM_EXPORT void setDefaultErrorHandler(const ErrorHandler &h)
Sets the default error handler.
QMLDOM_EXPORT ErrorLevel errorLevelFromQtMsgType(QtMsgType msgType)
static ErrorGroups myErrors()
void errorHandlerHandler(const ErrorMessage &msg, const ErrorHandler *h=nullptr)
std::function< void(const ErrorMessage &)> ErrorHandler
QMLDOM_EXPORT void defaultErrorHandler(const ErrorMessage &)
Calls the default error handler (by default errorToQDebug)
static QBasicMutex * registryMutex()
#define NewErrorGroup(name)
StorableMsg(const ErrorMessage &e)