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 } else {
340 r[errorId] = StorableMsg{std::move(err.withErrorId(errorId))};
341 }
342 }
343 if (doubleRegister)
344 defaultErrorHandler(myErrors().warning(tr("Double registration of error %1: (%2) vs (%3)").arg(errorId, err.withErrorId(errorId).toString(), old.toString())));
345 return errorId;
346}
347
348void ErrorMessage::visitRegisteredMessages(function_ref<bool(const ErrorMessage &)> visitor)
349{
350 QHash<QLatin1String, StorableMsg> r;
351 {
352 QMutexLocker l(registryMutex());
353 r = registry();
354 }
355 auto it = r.cbegin();
356 auto end = r.cend();
357 while (it != end) {
358 visitor(it->msg);
359 ++it;
360 }
361}
362
363ErrorMessage ErrorMessage::load(QLatin1String errorId)
364{
365 ErrorMessage res = myErrors().error([errorId](const Sink &s){
366 s(u"Unregistered error ");
367 s(QString(errorId)); });
368 {
369 QMutexLocker l(registryMutex());
370 res = registry().value(errorId,res).msg;
371 }
372 return res;
373}
374
375ErrorMessage ErrorMessage::load(const char *errorId)
376{
377 return load(QLatin1String(errorId));
378}
379
380ErrorMessage &ErrorMessage::withErrorId(QLatin1String errorId)
381{
382 this->errorId = errorId;
383 return *this;
384}
385
387{
388 this->path = path;
389 return *this;
390}
391
393{
394 file=f;
395 return *this;
396}
397
399{
400 file = f.toString();
401 return *this;
402}
403
405{
406 location = loc;
407 return *this;
408}
409
411{
412 if (path.length() == 0)
413 path = el.canonicalPath();
414 if (file.isEmpty())
415 file = el.canonicalFilePath();
416 if (location == SourceLocation()) {
417 if (const FileLocations::Tree tree = FileLocations::treeOf(el)) {
418 location = FileLocations::region(tree, MainRegion);
419 }
420 }
421 return *this;
422}
423
425{
426 if (errorHandler)
427 errorHandler(*this);
428 else
430 return *this;
431}
432
433void ErrorMessage::dump(const Sink &sink) const
434{
435 if (!file.isEmpty()) {
436 sink(file);
437 sink(u":");
438 }
439 if (location.length) {
440 sinkInt(sink, location.startLine);
441 sink(u":");
442 sinkInt(sink, location.startColumn);
443 sink(u": ");
444 }
445 errorGroups.dump(sink);
446 sink(u" ");
447 dumpErrorLevel(sink, level);
448 if (! errorId.isEmpty()) {
449 sink(u" ");
450 sink(QString(errorId));
451 }
452 sink(u": ");
453 sink(message);
454 if (path.length()>0) {
455 sink(u" for ");
456 if (!file.isEmpty() && path.length() > 3 && path.headKind() == Path::Kind::Root)
457 path.mid(3).dump(sink);
458 else
459 path.dump(sink);
460 }
461}
462
464{
465 return dumperToString([this](const Sink &sink){ this->dump(sink); });
466}
467
469{
470 return QCborMap({
471 {QStringLiteral(u"errorId"),errorId},
472 {QStringLiteral(u"message"), message},
473 {QStringLiteral(u"errorGroups"), errorGroups.toCbor()},
474 {QStringLiteral(u"level"), int(level)},
475 {QStringLiteral(u"path"), path.toString()},
476 {QStringLiteral(u"file"), file},
477 {QStringLiteral(u"location"), QCborMap({
478 {QStringLiteral(u"offset"),location.offset},
479 {QStringLiteral(u"length"),location.length},
480 {QStringLiteral(u"startLine"),location.startLine},
481 {QStringLiteral(u"startColumn"),location.startColumn}})}
482 });
483}
484
485/*!
486 * \internal
487 * \brief writes an ErrorMessage to QDebug
488 * \param error the error to write
489 */
490void errorToQDebug(const ErrorMessage &error)
491{
492 dumperToQDebug([&error](const Sink &s){ error.dump(s); }, error.level);
493}
494
495/*!
496 * \internal
497 * \brief Error handler that ignores all errors (excluding fatal ones)
498 */
500{
501}
502
503void errorHandlerHandler(const ErrorMessage &msg, const ErrorHandler *h = nullptr)
504{
505 static ErrorHandler handler = &errorToQDebug;
506 if (h) {
507 handler = *h;
508 } else {
509 handler(msg);
510 }
511}
512
513/*!
514 * \internal
515 * \brief Calls the default error handler (by default errorToQDebug)
516 */
518{
520}
521
522/*!
523 * \internal
524 * \brief Sets the default error handler
525 */
527{
528 errorHandlerHandler(ErrorMessage(QString(), ErrorGroups({})), &h);
529}
530
532{
533 switch (msgType) {
534 case QtFatalMsg:
535 return ErrorLevel::Fatal;
536 case QtCriticalMsg:
537 return ErrorLevel::Error;
538 case QtWarningMsg:
539 return ErrorLevel::Warning;
540 case QtInfoMsg:
541 return ErrorLevel::Info;
542 case QtDebugMsg:
543 return ErrorLevel::Debug;
544 default:
545 return ErrorLevel::Error;
546 }
547}
548
549} // end namespace Dom
550} // end namespace QQmlJS
551
552QT_END_NAMESPACE
553
554#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()
Combined button and popup list for selecting options.
#define NewErrorGroup(name)
StorableMsg(const ErrorMessage &e)