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
101
102
103
112 if (filePath() != other.filePath())
119
120
121
124 return !(*
this == other);
128
129
130
131
134 if (m_stkTop->m_lineNo < 1) {
135 m_stkTop->m_lineNo = 1;
136 m_stkTop->m_columnNo = 1;
141
142
143
144
145
146
147
150 if (ch == QLatin1Char(
'\n')) {
151 m_stkTop->m_lineNo++;
152 m_stkTop->m_columnNo = 1;
153 }
else if (ch == QLatin1Char(
'\t')) {
154 m_stkTop->m_columnNo = 1 + s_tabSize * (m_stkTop->m_columnNo + s_tabSize - 1) / s_tabSize;
156 m_stkTop->m_columnNo++;
161
162
163
164
165
168 if (m_stkDepth++ >= 1) {
169 if (m_stk ==
nullptr)
170 m_stk =
new QStack<StackEntry>;
171 m_stk->push(StackEntry());
172 m_stkTop = &m_stk->top();
175 m_stkTop->m_filePath = filePath;
176 m_stkTop->m_lineNo = INT_MIN;
177 m_stkTop->m_columnNo = 1;
181
182
183
184
185
188 if (--m_stkDepth == 0) {
189 m_stkBottom = StackEntry();
194 if (m_stk->isEmpty()) {
197 m_stkTop = &m_stkBottom;
199 m_stkTop = &m_stk->top();
205
206
207
208
209
212
213
214
215
216
219
220
221
224 QFileInfo fi(filePath());
225 return fi.fileName();
229
230
231
234 QString fp = filePath();
235 return (fp.isEmpty() ? fp : fp.mid(fp.lastIndexOf(
'.') + 1));
239
240
241
242
243
246
247
248
249
250
253
254
255
256
259 const auto &config = Config::instance();
260 if (!config.preparing() || config.singleExec())
261 emitMessage(Warning, message, details);
265
266
267
268
271 const auto &config = Config::instance();
272 if (!config.preparing() || config.singleExec())
273 emitMessage(Error, message, details);
277
278
279
280
283 if (s_warningLimit < 0 || s_warningCount <= s_warningLimit)
286 Location().emitMessage(
288 QStringLiteral(
"Documentation warnings (%1) exceeded the limit (%2) for '%3'.")
289 .arg(QString::number(s_warningCount), QString::number(s_warningLimit),
292 return s_warningCount;
296
297
298
299
302 emitMessage(Error, message, details);
303 information(message);
304 information(details);
305 information(
"Aborting");
310
311
312
313
314
315
316
317
318
321 const auto &config = Config::instance();
322 if ((!config.preparing() || config.singleExec()) && !s_reports.contains(message)) {
323 emitMessage(Report, message, details);
324 s_reports << message;
329
330
331
332
333
336 Config &config = Config::instance();
338 s_programName = config.programName();
345 QString qdocProjectRoot = qEnvironmentVariable(
"QDOC_PROJECT_ROOT");
346 if (qdocProjectRoot.isNull())
349 if (!qdocProjectRoot.isEmpty() && QDir(qdocProjectRoot).exists())
350 s_projectRoot = QDir::cleanPath(qdocProjectRoot);
354 if (qEnvironmentVariableIsSet(
"QDOC_ENABLE_WARNINGLIMIT")
359 if (regExp.isValid()) {
360 s_spuriousRegExp =
new QRegularExpression(regExp);
363 .warning(QStringLiteral(
"Invalid regular expression '%1'")
364 .arg(regExp.pattern()));
368 initializeWarningLog(config);
372
373
376 const auto &config = Config::instance();
379 lines <<
"# QDoc Warning Log"_L1;
380 lines <<
"# Project: "_L1 + s_project;
384 const QStringList args = QCoreApplication::arguments();
385 if (!args.isEmpty()) {
386 QStringList quotedArgs;
387 for (
const QString &arg : args) {
389 if (arg.contains(QLatin1Char(
' '))) {
390 quotedArgs <<
'"'_L1 + arg +
'"'_L1;
395 lines <<
"# Command: "_L1 + quotedArgs.join(QLatin1Char(
' '));
399 if (!s_projectRoot.isEmpty()) {
401 if (!qEnvironmentVariable(
"QDOC_PROJECT_ROOT").isNull()) {
402 lines <<
"# Root: QDOC_PROJECT_ROOT"_L1;
404 lines <<
"# Root: projectroot config"_L1;
406 lines <<
"# Path-Format: relative"_L1;
408 lines <<
"# Path-Format: absolute"_L1;
412 return lines.join(
'\n'_L1);
416
417
418
419QString
Location::formatPathForWarningLog(
const QString &path)
421 if (s_projectRoot.isEmpty())
424 QDir projectDir(s_projectRoot);
425 QString relativePath = projectDir.relativeFilePath(path);
428 if (!relativePath.startsWith(
"../"_L1))
435
436
437
438
439
440
443 const QString logFileName = s_project +
"-qdoc-warnings.log";
444 const QString &outputDir = config.getOutputDir();
451 const QString &logFilePath = dir.filePath(logFileName);
453 s_warningLogFile =
std::make_unique<QFile>(logFilePath);
454 if (s_warningLogFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
455 s_warningLogStream =
std::make_unique<QTextStream>(s_warningLogFile.get());
457 *s_warningLogStream << warningLogHeader() << Qt::endl;
458 *s_warningLogStream << Qt::endl;
460 Location().warning(QStringLiteral(
"Failed to open warning log file: %1").arg(logFilePath));
461 s_warningLogFile.reset();
466
467
468
469void Location::writeToWarningLog(MessageType type,
const QString &formattedMessage)
471 if (type != Warning || !s_warningLogStream)
474 *s_warningLogStream << formattedMessage << Qt::endl;
478
479
480
481
484 delete s_spuriousRegExp;
485 s_spuriousRegExp =
nullptr;
487 s_warningLogStream.reset();
488 s_warningLogFile.reset();
492
493
496 printf(
"%s\n", message.toLatin1().data());
501
502
505 Location().fatal(QStringLiteral(
"Internal error (%1)").arg(hint),
506 QStringLiteral(
"There is a bug in %1. Seek advice from your local"
508 .arg(s_programName, s_programName));
512
513
514
515
516void Location::emitMessage(MessageType type,
const QString &message,
const QString &details)
const
518 if (type == Warning && s_spuriousRegExp !=
nullptr) {
519 auto match = s_spuriousRegExp->match(message, 0, QRegularExpression::NormalMatch,
520 QRegularExpression::AnchorAtOffsetMatchOption);
521 if (match.hasMatch() && match.capturedLength() == message.size())
525 QString result = message;
526 if (!details.isEmpty())
527 result +=
"\n[" + details + QLatin1Char(
']');
528 result.replace(
"\n",
"\n ");
531 result.prepend(QStringLiteral(
": error: "));
532 else if (type == Warning) {
533 result.prepend(QStringLiteral(
": warning: "));
538 result.prepend(
": [%1] (qdoc) error: "_L1.arg(s_project));
539 else if (type == Warning) {
540 result.prepend(
": [%1] (qdoc) warning: "_L1.arg(s_project));
545 result.prepend(toString());
547 result.prepend(
"qdoc: '%1': "_L1.arg(s_project));
548 fprintf(stderr,
"%s\n", result.toLatin1().data());
552 QString logMessage = result;
553 if (type == Warning && !s_projectRoot.isEmpty()) {
555 QString locationString = toString();
556 QString formattedLocationString = formatPathForWarningLog(locationString);
557 logMessage.replace(locationString, formattedLocationString);
559 writeToWarningLog(type, logMessage);
563
564
565
577 QString blah = QStringLiteral(
"In file included from ");
584 str += QStringLiteral(
",\n");
587 str += QStringLiteral(
":\n");
596 QDir path(filePath());
597 QString str = path.absolutePath();
599 str += QLatin1Char(
':');
600 str += QString::number(lineNo());
603 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; 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.
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.
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