9#include <QtCore/qdebug.h>
10#include <QtCore/qdir.h>
11#include <QtCore/qfile.h>
12#include <QtCore/qregularexpression.h>
13#include <QtCore/qtextstream.h>
19using namespace Qt::Literals::StringLiterals;
31QSet<QString>
Location::s_emittedWarnings;
32QRegularExpression *
Location::s_spuriousRegExp =
nullptr;
33std::unique_ptr<QFile>
Location::s_warningLogFile;
34std::unique_ptr<QTextStream>
Location::s_warningLogStream;
37
38
39
40
41
42
43
44
45
48
49
56
57
58
66
67
68
76
77
78
84 QStack<StackEntry> *oldStk = m_stk;
86 m_stkBottom = other.m_stkBottom;
87 if (other.m_stk ==
nullptr) {
89 m_stkTop = &m_stkBottom;
91 m_stk =
new QStack<StackEntry>(*other.m_stk);
92 m_stkTop = &m_stk->top();
94 m_stkDepth = other.m_stkDepth;
101
102
103
104
105
106
107
116 if (filePath() != other.filePath())
123
124
125
128 return !(*
this == other);
132
133
134
135
138 if (m_stkTop->m_lineNo < 1) {
139 m_stkTop->m_lineNo = 1;
140 m_stkTop->m_columnNo = 1;
145
146
147
148
149
150
151
154 if (ch == QLatin1Char(
'\n')) {
155 m_stkTop->m_lineNo++;
156 m_stkTop->m_columnNo = 1;
157 }
else if (ch == QLatin1Char(
'\t')) {
158 m_stkTop->m_columnNo = 1 + s_tabSize * (m_stkTop->m_columnNo + s_tabSize - 1) / s_tabSize;
160 m_stkTop->m_columnNo++;
165
166
167
168
169
172 if (m_stkDepth++ >= 1) {
173 if (m_stk ==
nullptr)
174 m_stk =
new QStack<StackEntry>;
175 m_stk->push(StackEntry());
176 m_stkTop = &m_stk->top();
179 m_stkTop->m_filePath = filePath;
180 m_stkTop->m_lineNo = INT_MIN;
181 m_stkTop->m_columnNo = 1;
185
186
187
188
189
192 if (--m_stkDepth == 0) {
193 m_stkBottom = StackEntry();
198 if (m_stk->isEmpty()) {
201 m_stkTop = &m_stkBottom;
203 m_stkTop = &m_stk->top();
209
210
211
212
213
216
217
218
219
220
223
224
225
228 QFileInfo fi(filePath());
229 return fi.fileName();
233
234
235
238 QString fp = filePath();
239 return (fp.isEmpty() ? fp : fp.mid(fp.lastIndexOf(
'.') + 1));
243
244
245
246
247
250
251
252
253
254
257
258
259
260
261
262
263
264
265
268 const auto &config = Config::instance();
269 if (!config.preparing() || config.singleExec())
270 emitMessage(Warning, message, details);
274
275
276
277
280 const auto &config = Config::instance();
281 if (!config.preparing() || config.singleExec())
282 emitMessage(Error, message, details);
286
287
288
295
296
297
300 if (s_warningLimit >= 0 && s_warningCount > s_warningLimit) {
303 QStringLiteral(
"Documentation warnings (%1) exceeded the limit (%2) for '%3'.")
304 .arg(QString::number(s_warningCount), QString::number(s_warningLimit),
307 if (s_errorCount == 0)
308 return s_warningCount;
311 return s_errorCount > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
315
316
317
318
321 emitMessage(Error, message, details);
322 information(message);
323 information(details);
324 information(
"Aborting");
329
330
331
332
333
334
335
336
337
338
339
340
343 const auto &config = Config::instance();
344 if (!config.preparing() || config.singleExec()) {
347 QString key = message;
349 key = filePath() + QChar(0) + QString::number(lineNo()) + QChar(0) + message;
350 if (!s_reports.contains(key)) {
351 emitMessage(Report, message, details);
352 s_reports.insert(key);
358
359
360
361
362
365 Config &config = Config::instance();
367 s_programName = config.programName();
374 QString qdocProjectRoot = qEnvironmentVariable(
"QDOC_PROJECT_ROOT");
375 if (qdocProjectRoot.isNull())
378 if (!qdocProjectRoot.isEmpty() && QDir(qdocProjectRoot).exists())
379 s_projectRoot = QDir::cleanPath(qdocProjectRoot);
383 s_emittedWarnings.clear();
384 if (qEnvironmentVariableIsSet(
"QDOC_ENABLE_WARNINGLIMIT")
389 if (regExp.isValid()) {
390 s_spuriousRegExp =
new QRegularExpression(regExp);
393 .warning(QStringLiteral(
"Invalid regular expression '%1'")
394 .arg(regExp.pattern()));
398 initializeWarningLog(config);
402
403
406 const auto &config = Config::instance();
409 lines <<
"# QDoc Warning Log"_L1;
410 lines <<
"# Project: "_L1 + s_project;
414 const QStringList args = QCoreApplication::arguments();
415 if (!args.isEmpty()) {
416 QStringList quotedArgs;
417 for (
const QString &arg : args) {
419 if (arg.contains(QLatin1Char(
' '))) {
420 quotedArgs <<
'"'_L1 + arg +
'"'_L1;
425 lines <<
"# Command: "_L1 + quotedArgs.join(QLatin1Char(
' '));
429 if (!s_projectRoot.isEmpty()) {
431 if (!qEnvironmentVariable(
"QDOC_PROJECT_ROOT").isNull()) {
432 lines <<
"# Root: QDOC_PROJECT_ROOT"_L1;
434 lines <<
"# Root: projectroot config"_L1;
436 lines <<
"# Path-Format: relative"_L1;
438 lines <<
"# Path-Format: absolute"_L1;
442 return lines.join(
'\n'_L1);
446
447
448
449QString
Location::formatPathForWarningLog(
const QString &path)
451 if (s_projectRoot.isEmpty())
454 QDir projectDir(s_projectRoot);
455 QString relativePath = projectDir.relativeFilePath(path);
458 if (!relativePath.startsWith(
"../"_L1))
465
466
467
468
469
470
473 const QString logFileName = s_project +
"-qdoc-warnings.log";
474 const QString &outputDir = config.getOutputDir();
477 OutputDirectory::ensure(outputDir,
Location());
479 const QString &logFilePath = dir.absoluteFilePath(logFileName);
481 s_warningLogFile =
std::make_unique<QFile>(logFilePath);
482 if (s_warningLogFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
483 s_warningLogStream =
std::make_unique<QTextStream>(s_warningLogFile.get());
485 *s_warningLogStream << warningLogHeader() << Qt::endl;
486 *s_warningLogStream << Qt::endl;
488 Location().warning(QStringLiteral(
"Failed to open warning log file: %1").arg(logFilePath));
489 s_warningLogFile.reset();
494
495
496
497void Location::writeToWarningLog(MessageType type,
const QString &formattedMessage)
499 if (type != Warning || !s_warningLogStream)
502 *s_warningLogStream << formattedMessage << Qt::endl;
506
507
508
509
512 delete s_spuriousRegExp;
513 s_spuriousRegExp =
nullptr;
515 s_emittedWarnings.clear();
517 s_warningLogStream.reset();
518 s_warningLogFile.reset();
522
523
526 printf(
"%s\n", message.toLatin1().data());
531
532
535 Location().fatal(QStringLiteral(
"Internal error (%1)").arg(hint),
536 QStringLiteral(
"There is a bug in %1. Seek advice from your local"
538 .arg(s_programName, s_programName));
542
543
544
545
546
547
548
549
550
551
552
553
554void Location::emitMessage(MessageType type,
const QString &message,
const QString &details)
const
556 if (type == Warning && s_spuriousRegExp !=
nullptr) {
557 auto match = s_spuriousRegExp->match(message, 0, QRegularExpression::NormalMatch,
558 QRegularExpression::AnchorAtOffsetMatchOption);
559 if (match.hasMatch() && match.capturedLength() == message.size())
563 if (type == Warning) {
564 const QString key = toString() + QChar(0) + s_project + QChar(0)
565 + message + QChar(0) + details;
566 if (s_emittedWarnings.contains(key))
568 s_emittedWarnings.insert(key);
571 QString result = message;
572 if (!details.isEmpty())
573 result +=
"\n[" + details + QLatin1Char(
']');
574 result.replace(
"\n",
"\n ");
577 result.prepend(QStringLiteral(
": error: "));
578 else if (type == Warning) {
579 result.prepend(QStringLiteral(
": warning: "));
581 }
else if (type == Report)
582 result.prepend(
"qdoc: '%1': "_L1.arg(s_project));
585 result.prepend(
": [%1] (qdoc) error: "_L1.arg(s_project));
586 else if (type == Warning) {
587 result.prepend(
": [%1] (qdoc) warning: "_L1.arg(s_project));
589 }
else if (type == Report)
590 result.prepend(
": [%1] (qdoc) report: "_L1.arg(s_project));
593 result.prepend(toString());
594 fprintf(stderr,
"%s\n", result.toLatin1().data());
598 QString logMessage =
std::move(result);
599 if (type == Warning && !s_projectRoot.isEmpty()) {
601 QString locationString = toString();
602 QString formattedLocationString = formatPathForWarningLog(locationString);
603 logMessage.replace(locationString, formattedLocationString);
605 writeToWarningLog(type, logMessage);
609
610
611
623 QString blah = QStringLiteral(
"In file included from ");
630 str += QStringLiteral(
",\n");
633 str += QStringLiteral(
":\n");
642 QDir path(filePath());
643 QString str = path.absolutePath();
645 str += QLatin1Char(
':');
646 str += QString::number(lineNo());
649 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.
bool operator==(const Location &other) const
Returns true if this instance points to the same location as other.
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.
void report(const QString &message, const QString &details=QString()) const
Writes message and details to {stderr} as an informational report.
QString toString() const
Converts the location to a string to be prepended to error messages.
bool operator!=(const Location &other) const
Returns true if this instance does not point to the same location as other.
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.
static void increaseErrorCount()
Records a failure that must make the process exit non-zero, independently of the warning limit.
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.
Represents an output directory that has been verified to exist.
#define CONFIG_LOGWARNINGSDISABLECLIARGS
#define CONFIG_WARNINGLIMIT
#define CONFIG_LOGWARNINGS
#define CONFIG_PROJECTROOT