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 | |
34 | QT_BEGIN_NAMESPACE |
35 | |
36 | /*! |
37 | \internal |
38 | Used to print the line containing the location of a certain error |
39 | */ |
40 | class Q_QMLCOMPILER_PRIVATE_EXPORT IssueLocationWithContext |
41 | { |
42 | public: |
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 | |
68 | private: |
69 | QStringView m_beforeText; |
70 | QStringView m_issueText; |
71 | QStringView m_afterText; |
72 | }; |
73 | |
74 | class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSFixSuggestion |
75 | { |
76 | public: |
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 | |
97 | private: |
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 | |
106 | struct 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 | |
114 | class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSLogger |
115 | { |
116 | Q_DISABLE_COPY_MOVE(QQmlJSLogger) |
117 | public: |
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 | |
201 | private: |
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 | |
235 | QT_END_NAMESPACE |
236 | |
237 | #endif // QQMLJSLOGGER_P_H |
238 | |