8#include <QtCore/qdebug.h>
9#include <QtCore/qdir.h>
10#include <QtCore/qfile.h>
11#include <QtCore/qregularexpression.h>
12#include <QtCore/qtextstream.h>
17using namespace Qt::Literals::StringLiterals;
28QRegularExpression *
Location::s_spuriousRegExp =
nullptr;
29std::unique_ptr<QFile>
Location::s_warningLogFile;
30std::unique_ptr<QTextStream>
Location::s_warningLogStream;
33
34
35
36
37
38
39
40
41
44
45
52
53
54
62
63
64
72
73
74
80 QStack<StackEntry> *oldStk = m_stk;
82 m_stkBottom = other.m_stkBottom;
83 if (other.m_stk ==
nullptr) {
85 m_stkTop = &m_stkBottom;
87 m_stk =
new QStack<StackEntry>(*other.m_stk);
88 m_stkTop = &m_stk->top();
90 m_stkDepth = other.m_stkDepth;
97
98
99
100
103 if (m_stkTop->m_lineNo < 1) {
104 m_stkTop->m_lineNo = 1;
105 m_stkTop->m_columnNo = 1;
110
111
112
113
114
115
116
119 if (ch == QLatin1Char(
'\n')) {
120 m_stkTop->m_lineNo++;
121 m_stkTop->m_columnNo = 1;
122 }
else if (ch == QLatin1Char(
'\t')) {
123 m_stkTop->m_columnNo = 1 + s_tabSize * (m_stkTop->m_columnNo + s_tabSize - 1) / s_tabSize;
125 m_stkTop->m_columnNo++;
130
131
132
133
134
137 if (m_stkDepth++ >= 1) {
138 if (m_stk ==
nullptr)
139 m_stk =
new QStack<StackEntry>;
140 m_stk->push(StackEntry());
141 m_stkTop = &m_stk->top();
144 m_stkTop->m_filePath = filePath;
145 m_stkTop->m_lineNo = INT_MIN;
146 m_stkTop->m_columnNo = 1;
150
151
152
153
154
157 if (--m_stkDepth == 0) {
158 m_stkBottom = StackEntry();
163 if (m_stk->isEmpty()) {
166 m_stkTop = &m_stkBottom;
168 m_stkTop = &m_stk->top();
174
175
176
177
178
181
182
183
184
185
188
189
190
193 QFileInfo fi(filePath());
194 return fi.fileName();
198
199
200
203 QString fp = filePath();
204 return (fp.isEmpty() ? fp : fp.mid(fp.lastIndexOf(
'.') + 1));
208
209
210
211
212
215
216
217
218
219
222
223
224
225
228 const auto &config = Config::instance();
229 if (!config.preparing() || config.singleExec())
230 emitMessage(Warning, message, details);
234
235
236
237
240 const auto &config = Config::instance();
241 if (!config.preparing() || config.singleExec())
242 emitMessage(Error, message, details);
246
247
248
249
252 if (s_warningLimit < 0 || s_warningCount <= s_warningLimit)
255 Location().emitMessage(
257 QStringLiteral(
"Documentation warnings (%1) exceeded the limit (%2) for '%3'.")
258 .arg(QString::number(s_warningCount), QString::number(s_warningLimit),
261 return s_warningCount;
265
266
267
268
271 emitMessage(Error, message, details);
272 information(message);
273 information(details);
274 information(
"Aborting");
279
280
281
282
283
284
285
286
287
290 const auto &config = Config::instance();
291 if ((!config.preparing() || config.singleExec()) && !s_reports.contains(message)) {
292 emitMessage(Report, message, details);
293 s_reports << message;
298
299
300
301
302
305 Config &config = Config::instance();
307 s_programName = config.programName();
314 QString qdocProjectRoot = qEnvironmentVariable(
"QDOC_PROJECT_ROOT");
315 if (qdocProjectRoot.isNull())
318 if (!qdocProjectRoot.isEmpty() && QDir(qdocProjectRoot).exists())
319 s_projectRoot = QDir::cleanPath(qdocProjectRoot);
323 if (qEnvironmentVariableIsSet(
"QDOC_ENABLE_WARNINGLIMIT")
328 if (regExp.isValid()) {
329 s_spuriousRegExp =
new QRegularExpression(regExp);
332 .warning(QStringLiteral(
"Invalid regular expression '%1'")
333 .arg(regExp.pattern()));
337 initializeWarningLog(config);
341
342
345 const auto &config = Config::instance();
348 lines <<
"# QDoc Warning Log"_L1;
349 lines <<
"# Project: "_L1 + s_project;
353 const QStringList args = QCoreApplication::arguments();
354 if (!args.isEmpty()) {
355 QStringList quotedArgs;
356 for (
const QString &arg : args) {
358 if (arg.contains(QLatin1Char(
' '))) {
359 quotedArgs <<
'"'_L1 + arg +
'"'_L1;
364 lines <<
"# Command: "_L1 + quotedArgs.join(QLatin1Char(
' '));
368 if (!s_projectRoot.isEmpty()) {
370 if (!qEnvironmentVariable(
"QDOC_PROJECT_ROOT").isNull()) {
371 lines <<
"# Root: QDOC_PROJECT_ROOT"_L1;
373 lines <<
"# Root: projectroot config"_L1;
375 lines <<
"# Path-Format: relative"_L1;
377 lines <<
"# Path-Format: absolute"_L1;
381 return lines.join(
'\n'_L1);
385
386
387
388QString
Location::formatPathForWarningLog(
const QString &path)
390 if (s_projectRoot.isEmpty())
393 QDir projectDir(s_projectRoot);
394 QString relativePath = projectDir.relativeFilePath(path);
397 if (!relativePath.startsWith(
"../"_L1))
404
405
406
407
408
409
412 const QString logFileName = s_project +
"-qdoc-warnings.log";
413 const QString &outputDir = config.getOutputDir();
420 const QString &logFilePath = dir.filePath(logFileName);
422 s_warningLogFile =
std::make_unique<QFile>(logFilePath);
423 if (s_warningLogFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
424 s_warningLogStream =
std::make_unique<QTextStream>(s_warningLogFile.get());
426 *s_warningLogStream << warningLogHeader() << Qt::endl;
427 *s_warningLogStream << Qt::endl;
429 Location().warning(QStringLiteral(
"Failed to open warning log file: %1").arg(logFilePath));
430 s_warningLogFile.reset();
435
436
437
438void Location::writeToWarningLog(MessageType type,
const QString &formattedMessage)
440 if (type != Warning || !s_warningLogStream)
443 *s_warningLogStream << formattedMessage << Qt::endl;
447
448
449
450
453 delete s_spuriousRegExp;
454 s_spuriousRegExp =
nullptr;
456 s_warningLogStream.reset();
457 s_warningLogFile.reset();
461
462
465 printf(
"%s\n", message.toLatin1().data());
470
471
474 Location().fatal(QStringLiteral(
"Internal error (%1)").arg(hint),
475 QStringLiteral(
"There is a bug in %1. Seek advice from your local"
477 .arg(s_programName, s_programName));
481
482
483
484
485void Location::emitMessage(MessageType type,
const QString &message,
const QString &details)
const
487 if (type == Warning && s_spuriousRegExp !=
nullptr) {
488 auto match = s_spuriousRegExp->match(message, 0, QRegularExpression::NormalMatch,
489 QRegularExpression::AnchorAtOffsetMatchOption);
490 if (match.hasMatch() && match.capturedLength() == message.size())
494 QString result = message;
495 if (!details.isEmpty())
496 result +=
"\n[" + details + QLatin1Char(
']');
497 result.replace(
"\n",
"\n ");
500 result.prepend(QStringLiteral(
": error: "));
501 else if (type == Warning) {
502 result.prepend(QStringLiteral(
": warning: "));
507 result.prepend(QStringLiteral(
": (qdoc) error: "));
508 else if (type == Warning) {
509 result.prepend(QStringLiteral(
": (qdoc) warning: "));
514 result.prepend(toString());
516 result.prepend(
"qdoc: '%1': "_L1.arg(s_project));
517 fprintf(stderr,
"%s\n", result.toLatin1().data());
521 QString logMessage = result;
522 if (type == Warning && !s_projectRoot.isEmpty()) {
524 QString locationString = toString();
525 QString formattedLocationString = formatPathForWarningLog(locationString);
526 logMessage.replace(locationString, formattedLocationString);
528 writeToWarningLog(type, logMessage);
532
533
534
546 QString blah = QStringLiteral(
"In file included from ");
553 str += QStringLiteral(
",\n");
556 str += QStringLiteral(
":\n");
565 QDir path(filePath());
566 QString str = path.absolutePath();
568 str += QLatin1Char(
':');
569 str += QString::number(lineNo());
572 str += QLatin1String(
" (etc.)");
The Config class contains the configuration variables for controlling how qdoc produces documentation...
The Location class provides a way to mark a location in a file.
QString fileName() const
Returns the file name part of the file path, ie the current file.
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.
QString fileSuffix() const
Returns the suffix of the file name.
Location(const Location &other)
The copy constructor copies the contents of other into this Location using the assignment operator.
void error(const QString &message, const QString &details=QString()) const
Writes message and details to stderr as a formatted error message.
int lineNo() const
Returns the current line number.
static int exitCode()
Returns the error code QDoc should exit with; EXIT_SUCCESS or the number of documentation warnings if...
void report(const QString &message, const QString &details=QString()) const
Writes message and details to stderr as a formatted report message.
QString toString() const
Converts the location to a string to be prepended to error messages.
static void initialize()
Gets several parameters from the config, including tab size, program name, and a regular expression t...
Location()
Constructs an empty location.
void push(const QString &filePath)
Pushes filePath onto the file position stack.
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 ...
void warning(const QString &message, const QString &details=QString()) const
Writes message and details to stderr as a formatted warning message.
void advance(QChar ch)
Advance the current file position, using ch to decide how to do that.
Location & operator=(const Location &other)
The assignment operator does a deep copy of the entire state of other into this Location.
bool isEmpty() const
Returns true if there is no file name set yet; returns false otherwise.
static void terminate()
Apparently, all this does is delete the regular expression used for intercepting certain error messag...
void pop()
Pops the top of the internal stack.
Location(const QString &filePath)
Constructs a location with (fileName, 1, 1) on its file position stack.
#define CONFIG_LOGWARNINGSDISABLECLIARGS
#define CONFIG_WARNINGLIMIT
#define CONFIG_LOGWARNINGS
#define CONFIG_PROJECTROOT