1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#ifndef QQMLJSLOGGER_P_H
5#define QQMLJSLOGGER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qtqmlcompilerexports_p.h>
19
20#include "qcoloroutput_p.h"
21#include "qqmljsloggingutils_p.h"
22
23#include <private/qqmljsdiagnosticmessage_p.h>
24
25#include <QtCore/qhash.h>
26#include <QtCore/qmap.h>
27#include <QtCore/qstring.h>
28#include <QtCore/qlist.h>
29#include <QtCore/qset.h>
30#include <QtCore/QLoggingCategory>
31
32#include <optional>
33
34QT_BEGIN_NAMESPACE
35
36/*!
37 \internal
38 Used to print the line containing the location of a certain error
39 */
40class Q_QMLCOMPILER_PRIVATE_EXPORT IssueLocationWithContext
41{
42public:
43 /*!
44 \internal
45 \param code: The whole text of a translation unit
46 \param location: The location where an error occurred.
47 */
48 IssueLocationWithContext(QStringView code, const QQmlJS::SourceLocation &location) {
49 quint32 before = qMax(a: 0, b: code.lastIndexOf(c: QLatin1Char('\n'), from: location.offset));
50
51 if (before != 0 && before < location.offset)
52 before++;
53
54 m_beforeText = code.mid(pos: before, n: location.offset - before);
55 m_issueText = code.mid(pos: location.offset, n: location.length);
56 int after = code.indexOf(c: QLatin1Char('\n'), from: location.offset + location.length);
57 m_afterText = code.mid(pos: location.offset + location.length,
58 n: after - (location.offset+location.length));
59 }
60
61 // returns start of the line till first character of location
62 QStringView beforeText() const { return m_beforeText; }
63 // returns the text at location
64 QStringView issueText() const { return m_issueText; }
65 // returns any text after location until the end of the line is reached
66 QStringView afterText() const { return m_afterText; }
67
68private:
69 QStringView m_beforeText;
70 QStringView m_issueText;
71 QStringView m_afterText;
72};
73
74class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSFixSuggestion
75{
76public:
77 QQmlJSFixSuggestion() = default;
78 QQmlJSFixSuggestion(const QString &fixDescription, const QQmlJS::SourceLocation &location,
79 const QString &replacement = QString());
80
81 QString fixDescription() const { return m_fixDescription; }
82 QQmlJS::SourceLocation location() const { return m_location; }
83 QString replacement() const { return m_replacement; }
84
85 void setFilename(const QString &filename) { m_filename = filename; }
86 QString filename() const { return m_filename; }
87
88 void setHint(const QString &hint) { m_hint = hint; }
89 QString hint() const { return m_hint; }
90
91 void setAutoApplicable(bool autoApply = true) { m_autoApplicable = autoApply; }
92 bool isAutoApplicable() const { return m_autoApplicable; }
93
94 bool operator==(const QQmlJSFixSuggestion &) const;
95 bool operator!=(const QQmlJSFixSuggestion &) const;
96
97private:
98 QQmlJS::SourceLocation m_location;
99 QString m_fixDescription;
100 QString m_replacement;
101 QString m_filename;
102 QString m_hint;
103 bool m_autoApplicable = false;
104};
105
106struct Message : public QQmlJS::DiagnosticMessage
107{
108 // This doesn't need to be an owning-reference since the string is expected to outlive any
109 // Message object by virtue of coming from a LoggerWarningId.
110 QAnyStringView id;
111 std::optional<QQmlJSFixSuggestion> fixSuggestion;
112};
113
114class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSLogger
115{
116 Q_DISABLE_COPY_MOVE(QQmlJSLogger)
117public:
118 QList<QQmlJS::LoggerCategory> categories() const;
119 static const QList<QQmlJS::LoggerCategory> &defaultCategories();
120
121 void registerCategory(const QQmlJS::LoggerCategory &category);
122
123 QQmlJSLogger();
124 ~QQmlJSLogger() = default;
125
126 bool hasWarnings() const { return !m_warnings.isEmpty(); }
127 bool hasErrors() const { return !m_errors.isEmpty(); }
128
129 const QList<Message> &infos() const { return m_infos; }
130 const QList<Message> &warnings() const { return m_warnings; }
131 const QList<Message> &errors() const { return m_errors; }
132
133 QtMsgType categoryLevel(QQmlJS::LoggerWarningId id) const
134 {
135 return m_categoryLevels[id.name().toString()];
136 }
137 void setCategoryLevel(QQmlJS::LoggerWarningId id, QtMsgType level)
138 {
139 m_categoryLevels[id.name().toString()] = level;
140 m_categoryChanged[id.name().toString()] = true;
141 }
142
143 bool isCategoryIgnored(QQmlJS::LoggerWarningId id) const
144 {
145 return m_categoryIgnored[id.name().toString()];
146 }
147 void setCategoryIgnored(QQmlJS::LoggerWarningId id, bool error)
148 {
149 m_categoryIgnored[id.name().toString()] = error;
150 m_categoryChanged[id.name().toString()] = true;
151 }
152
153 bool isCategoryFatal(QQmlJS::LoggerWarningId id) const
154 {
155 return m_categoryFatal[id.name().toString()];
156 }
157 void setCategoryFatal(QQmlJS::LoggerWarningId id, bool error)
158 {
159 m_categoryFatal[id.name().toString()] = error;
160 m_categoryChanged[id.name().toString()] = true;
161 }
162
163 bool wasCategoryChanged(QQmlJS::LoggerWarningId id) const
164 {
165 return m_categoryChanged[id.name().toString()];
166 }
167
168 /*! \internal
169
170 Logs \a message with severity deduced from \a category. Prefer using
171 this function in most cases.
172
173 \sa setCategoryLevel
174 */
175 void log(const QString &message, QQmlJS::LoggerWarningId id,
176 const QQmlJS::SourceLocation &srcLocation, bool showContext = true,
177 bool showFileName = true, const std::optional<QQmlJSFixSuggestion> &suggestion = {},
178 const QString overrideFileName = QString())
179 {
180 log(message, id, srcLocation, type: m_categoryLevels[id.name().toString()], showContext,
181 showFileName, suggestion, overrideFileName);
182 }
183
184 void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
185 const QQmlJS::LoggerWarningId id);
186
187 void ignoreWarnings(uint32_t line, const QSet<QString> &categories)
188 {
189 m_ignoredWarnings[line] = categories;
190 }
191
192 void setSilent(bool silent) { m_output.setSilent(silent); }
193 bool isSilent() const { return m_output.isSilent(); }
194
195 void setCode(const QString &code) { m_code = code; }
196 QString code() const { return m_code; }
197
198 void setFileName(const QString &fileName) { m_fileName = fileName; }
199 QString fileName() const { return m_fileName; }
200
201private:
202 QMap<QString, QQmlJS::LoggerCategory> m_categories;
203
204 void printContext(const QString &overrideFileName, const QQmlJS::SourceLocation &location);
205 void printFix(const QQmlJSFixSuggestion &fix);
206
207 void log(const QString &message, QQmlJS::LoggerWarningId id,
208 const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
209 bool showFileName, const std::optional<QQmlJSFixSuggestion> &suggestion,
210 const QString overrideFileName);
211
212 QString m_fileName;
213 QString m_code;
214
215 QColorOutput m_output;
216
217 QHash<QString, QtMsgType> m_categoryLevels;
218 QHash<QString, bool> m_categoryIgnored;
219
220 // If true, triggers qFatal on documents with "pragma Strict"
221 // TODO: Works only for qmlCompiler category so far.
222 QHash<QString, bool> m_categoryFatal;
223
224 QHash<QString, bool> m_categoryChanged;
225
226 QList<Message> m_infos;
227 QList<Message> m_warnings;
228 QList<Message> m_errors;
229 QHash<uint32_t, QSet<QString>> m_ignoredWarnings;
230
231 // the compiler needs private log() function at the moment
232 friend class QQmlJSAotCompiler;
233};
234
235QT_END_NAMESPACE
236
237#endif // QQMLJSLOGGER_P_H
238

source code of qtdeclarative/src/qmlcompiler/qqmljslogger_p.h