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
location.cpp
Go to the documentation of this file.
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#include "location.h"
5
6#include "config.h"
7
8#include <QtCore/qdebug.h>
9#include <QtCore/qdir.h>
10#include <QtCore/qregularexpression.h>
11
12#include <climits>
13#include <cstdio>
14#include <cstdlib>
15
16QT_BEGIN_NAMESPACE
17
18int Location::s_tabSize;
19int Location::s_warningCount = 0;
20int Location::s_warningLimit = -1;
21QString Location::s_programName;
22QString Location::s_project;
23QSet<QString> Location::s_reports;
24QRegularExpression *Location::s_spuriousRegExp = nullptr;
25
26/*!
27 \class Location
28
29 \brief The Location class provides a way to mark a location in a file.
30
31 It maintains a stack of file positions. A file position
32 consists of the file path, line number, and column number.
33 The location is used for printing error messages that are
34 tied to a location in a file.
35 */
36
37/*!
38 Constructs an empty location.
39 */
40Location::Location() : m_stk(nullptr), m_stkTop(&m_stkBottom), m_stkDepth(0), m_etc(false)
41{
42 // nothing.
43}
44
45/*!
46 Constructs a location with (fileName, 1, 1) on its file
47 position stack.
48 */
49Location::Location(const QString &fileName)
50 : m_stk(nullptr), m_stkTop(&m_stkBottom), m_stkDepth(0), m_etc(false)
51{
52 push(fileName);
53}
54
55/*!
56 The copy constructor copies the contents of \a other into
57 this Location using the assignment operator.
58 */
60 : m_stk(nullptr), m_stkTop(&m_stkBottom), m_stkDepth(0), m_etc(false)
61{
62 *this = other;
63}
64
65/*!
66 The assignment operator does a deep copy of the entire
67 state of \a other into this Location.
68 */
70{
71 if (this == &other)
72 return *this;
73
74 QStack<StackEntry> *oldStk = m_stk;
75
76 m_stkBottom = other.m_stkBottom;
77 if (other.m_stk == nullptr) {
78 m_stk = nullptr;
79 m_stkTop = &m_stkBottom;
80 } else {
81 m_stk = new QStack<StackEntry>(*other.m_stk);
82 m_stkTop = &m_stk->top();
83 }
84 m_stkDepth = other.m_stkDepth;
85 m_etc = other.m_etc;
86 delete oldStk;
87 return *this;
88}
89
90/*!
91 If the file position on top of the stack has a line number
92 less than 1, set its line number to 1 and its column number
93 to 1. Otherwise, do nothing.
94 */
96{
97 if (m_stkTop->m_lineNo < 1) {
98 m_stkTop->m_lineNo = 1;
99 m_stkTop->m_columnNo = 1;
100 }
101}
102
103/*!
104 Advance the current file position, using \a ch to decide how to do
105 that. If \a ch is a \c{'\\n'}, increment the current line number and
106 set the column number to 1. If \ch is a \c{'\\t'}, increment to the
107 next tab column. Otherwise, increment the column number by 1.
108
109 The current file position is the one on top of the position stack.
110 */
111void Location::advance(QChar ch)
112{
113 if (ch == QLatin1Char('\n')) {
114 m_stkTop->m_lineNo++;
115 m_stkTop->m_columnNo = 1;
116 } else if (ch == QLatin1Char('\t')) {
117 m_stkTop->m_columnNo = 1 + s_tabSize * (m_stkTop->m_columnNo + s_tabSize - 1) / s_tabSize;
118 } else {
119 m_stkTop->m_columnNo++;
120 }
121}
122
123/*!
124 Pushes \a filePath onto the file position stack. The current
125 file position becomes (\a filePath, 1, 1).
126
127 \sa pop()
128*/
129void Location::push(const QString &filePath)
130{
131 if (m_stkDepth++ >= 1) {
132 if (m_stk == nullptr)
133 m_stk = new QStack<StackEntry>;
134 m_stk->push(StackEntry());
135 m_stkTop = &m_stk->top();
136 }
137
138 m_stkTop->m_filePath = filePath;
139 m_stkTop->m_lineNo = INT_MIN;
140 m_stkTop->m_columnNo = 1;
141}
142
143/*!
144 Pops the top of the internal stack. The current file position
145 becomes the next one in the new top of stack.
146
147 \sa push()
148*/
150{
151 if (--m_stkDepth == 0) {
152 m_stkBottom = StackEntry();
153 } else {
154 if (!m_stk)
155 return;
156 m_stk->pop();
157 if (m_stk->isEmpty()) {
158 delete m_stk;
159 m_stk = nullptr;
160 m_stkTop = &m_stkBottom;
161 } else {
162 m_stkTop = &m_stk->top();
163 }
164 }
165}
166
167/*! \fn bool Location::isEmpty() const
168
169 Returns \c true if there is no file name set yet; returns \c false
170 otherwise. The functions filePath(), lineNo() and columnNo()
171 must not be called on an empty Location object.
172 */
173
174/*! \fn const QString &Location::filePath() const
175 Returns the current path and file name. If the Location is
176 empty, the returned string is null.
177
178 \sa lineNo(), columnNo()
179 */
180
181/*!
182 Returns the file name part of the file path, ie the current
183 file. Returns an empty string if the file path is empty.
184 */
186{
187 QFileInfo fi(filePath());
188 return fi.fileName();
189}
190
191/*!
192 Returns the suffix of the file name. Returns an empty string
193 if the file path is empty.
194 */
196{
197 QString fp = filePath();
198 return (fp.isEmpty() ? fp : fp.mid(fp.lastIndexOf('.') + 1));
199}
200
201/*! \fn int Location::lineNo() const
202 Returns the current line number.
203 Must not be called on an empty Location object.
204
205 \sa filePath(), columnNo()
206*/
207
208/*! \fn int Location::columnNo() const
209 Returns the current column number.
210 Must not be called on an empty Location object.
211
212 \sa filePath(), lineNo()
213*/
214
215/*!
216 Writes \a message and \a details to stderr as a formatted
217 warning message. Does not write the message if qdoc is in
218 the Prepare phase.
219 */
220void Location::warning(const QString &message, const QString &details) const
221{
222 const auto &config = Config::instance();
223 if (!config.preparing() || config.singleExec())
224 emitMessage(Warning, message, details);
225}
226
227/*!
228 Writes \a message and \a details to stderr as a formatted
229 error message. Does not write the message if qdoc is in
230 the Prepare phase.
231 */
232void Location::error(const QString &message, const QString &details) const
233{
234 const auto &config = Config::instance();
235 if (!config.preparing() || config.singleExec())
236 emitMessage(Error, message, details);
237}
238
239/*!
240 Returns the error code QDoc should exit with; EXIT_SUCCESS
241 or the number of documentation warnings if they exceeded
242 the limit set by warninglimit configuration variable.
243 */
245{
246 if (s_warningLimit < 0 || s_warningCount <= s_warningLimit)
247 return EXIT_SUCCESS;
248
249 Location().emitMessage(
250 Error,
251 QStringLiteral("Documentation warnings (%1) exceeded the limit (%2) for '%3'.")
252 .arg(QString::number(s_warningCount), QString::number(s_warningLimit),
253 s_project),
254 QString());
255 return s_warningCount;
256}
257
258/*!
259 Writes \a message and \a details to stderr as a formatted
260 error message and then exits the program. qdoc prints fatal
261 errors in either phase (Prepare or Generate).
262 */
263void Location::fatal(const QString &message, const QString &details) const
264{
265 emitMessage(Error, message, details);
266 information(message);
267 information(details);
268 information("Aborting");
269 exit(EXIT_FAILURE);
270}
271
272/*!
273 Writes \a message and \a details to stderr as a formatted
274 report message.
275
276 A report does not include any filename/line number information.
277 Recurring reports with an identical \a message are ignored.
278
279 A report is generated only in \e generate or \e {single-exec}
280 phase. In \e {prepare} phase, this function does nothing.
281 */
282void Location::report(const QString &message, const QString &details) const
283{
284 const auto &config = Config::instance();
285 if ((!config.preparing() || config.singleExec()) && !s_reports.contains(message)) {
286 emitMessage(Report, message, details);
287 s_reports << message;
288 }
289}
290
291/*!
292 Gets several parameters from the config, including
293 tab size, program name, and a regular expression that
294 appears to be used for matching certain error messages
295 so that emitMessage() can avoid printing them.
296 */
298{
299 Config &config = Config::instance();
300 s_tabSize = config.get(CONFIG_TABSIZE).asInt();
301 s_programName = config.programName();
302 s_project = config.get(CONFIG_PROJECT).asString();
303 if (!config.singleExec())
304 s_warningCount = 0;
305 if (qEnvironmentVariableIsSet("QDOC_ENABLE_WARNINGLIMIT")
306 || config.get(CONFIG_WARNINGLIMIT + Config::dot + "enabled").asBool())
307 s_warningLimit = config.get(CONFIG_WARNINGLIMIT).asInt();
308
309 QRegularExpression regExp = config.getRegExp(CONFIG_SPURIOUS);
310 if (regExp.isValid()) {
311 s_spuriousRegExp = new QRegularExpression(regExp);
312 } else {
313 config.get(CONFIG_SPURIOUS).location()
314 .warning(QStringLiteral("Invalid regular expression '%1'")
315 .arg(regExp.pattern()));
316 }
317}
318
319/*!
320 Apparently, all this does is delete the regular expression
321 used for intercepting certain error messages that should
322 not be emitted by emitMessage().
323 */
325{
326 delete s_spuriousRegExp;
327 s_spuriousRegExp = nullptr;
328}
329
330/*!
331 Prints \a message to \c stdout followed by a \c{'\n'}.
332 */
333void Location::information(const QString &message)
334{
335 printf("%s\n", message.toLatin1().data());
336 fflush(stdout);
337}
338
339/*!
340 Report a program bug, including the \a hint.
341 */
342void Location::internalError(const QString &hint)
343{
344 Location().fatal(QStringLiteral("Internal error (%1)").arg(hint),
345 QStringLiteral("There is a bug in %1. Seek advice from your local"
346 " %2 guru.")
347 .arg(s_programName, s_programName));
348}
349
350/*!
351 Formats \a message and \a details into a single string
352 and outputs that string to \c stderr. \a type specifies
353 whether the \a message is an error or a warning.
354 */
355void Location::emitMessage(MessageType type, const QString &message, const QString &details) const
356{
357 if (type == Warning && s_spuriousRegExp != nullptr) {
358 auto match = s_spuriousRegExp->match(message, 0, QRegularExpression::NormalMatch,
359 QRegularExpression::AnchorAtOffsetMatchOption);
360 if (match.hasMatch() && match.capturedLength() == message.size())
361 return;
362 }
363
364 QString result = message;
365 if (!details.isEmpty())
366 result += "\n[" + details + QLatin1Char(']');
367 result.replace("\n", "\n ");
368 if (isEmpty()) {
369 if (type == Error)
370 result.prepend(QStringLiteral(": error: "));
371 else if (type == Warning) {
372 result.prepend(QStringLiteral(": warning: "));
373 ++s_warningCount;
374 }
375 } else {
376 if (type == Error)
377 result.prepend(QStringLiteral(": (qdoc) error: "));
378 else if (type == Warning) {
379 result.prepend(QStringLiteral(": (qdoc) warning: "));
380 ++s_warningCount;
381 }
382 }
383 if (type != Report)
384 result.prepend(toString());
385 fprintf(stderr, "%s\n", result.toLatin1().data());
386 fflush(stderr);
387}
388
389/*!
390 Converts the location to a string to be prepended to error
391 messages.
392 */
394{
395 QString str;
396
397 if (isEmpty()) {
398 str = s_programName;
399 } else {
400 Location loc2 = *this;
401 loc2.setEtc(false);
402 loc2.pop();
403 if (!loc2.isEmpty()) {
404 QString blah = QStringLiteral("In file included from ");
405 for (;;) {
406 str += blah;
407 str += loc2.top();
408 loc2.pop();
409 if (loc2.isEmpty())
410 break;
411 str += QStringLiteral(",\n");
412 blah.fill(' ');
413 }
414 str += QStringLiteral(":\n");
415 }
416 str += top();
417 }
418 return str;
419}
420
421QString Location::top() const
422{
423 QDir path(filePath());
424 QString str = path.absolutePath();
425 if (lineNo() >= 1) {
426 str += QLatin1Char(':');
427 str += QString::number(lineNo());
428 }
429 if (etc())
430 str += QLatin1String(" (etc.)");
431 return str;
432}
433
434QT_END_NAMESPACE
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:84
bool singleExec() const
Definition config.h:402
The Location class provides a way to mark a location in a file.
Definition location.h:15
QString fileName() const
Returns the file name part of the file path, ie the current file.
Definition location.cpp:185
void fatal(const QString &message, const QString &details=QString()) const
Writes message and details to stderr as a formatted error message and then exits the program.
Definition location.cpp:263
QString fileSuffix() const
Returns the suffix of the file name.
Definition location.cpp:195
Location(const Location &other)
The copy constructor copies the contents of other into this Location using the assignment operator.
Definition location.cpp:59
void error(const QString &message, const QString &details=QString()) const
Writes message and details to stderr as a formatted error message.
Definition location.cpp:232
int lineNo() const
Returns the current line number.
Definition location.h:43
static int exitCode()
Returns the error code QDoc should exit with; EXIT_SUCCESS or the number of documentation warnings if...
Definition location.cpp:244
void report(const QString &message, const QString &details=QString()) const
Writes message and details to stderr as a formatted report message.
Definition location.cpp:282
QString toString() const
Converts the location to a string to be prepended to error messages.
Definition location.cpp:393
static void initialize()
Gets several parameters from the config, including tab size, program name, and a regular expression t...
Definition location.cpp:297
Location()
Constructs an empty location.
Definition location.cpp:40
void push(const QString &filePath)
Pushes filePath onto the file position stack.
Definition location.cpp:129
void start()
If the file position on top of the stack has a line number less than 1, set its line number to 1 and ...
Definition location.cpp:95
void warning(const QString &message, const QString &details=QString()) const
Writes message and details to stderr as a formatted warning message.
Definition location.cpp:220
void setEtc(bool etc)
Definition location.h:34
void advance(QChar ch)
Advance the current file position, using ch to decide how to do that.
Definition location.cpp:111
Location & operator=(const Location &other)
The assignment operator does a deep copy of the entire state of other into this Location.
Definition location.cpp:69
bool isEmpty() const
Returns true if there is no file name set yet; returns false otherwise.
Definition location.h:38
bool etc() const
Definition location.h:45
static void terminate()
Apparently, all this does is delete the regular expression used for intercepting certain error messag...
Definition location.cpp:324
void pop()
Pops the top of the internal stack.
Definition location.cpp:149
Location(const QString &filePath)
Constructs a location with (fileName, 1, 1) on its file position stack.
Definition location.cpp:49
#define CONFIG_TABSIZE
Definition config.h:386
#define CONFIG_WARNINGLIMIT
Definition config.h:400
#define CONFIG_PROJECT
Definition config.h:373
#define CONFIG_SPURIOUS
Definition config.h:383