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;
30QRegularExpression *
Location::s_spuriousRegExp =
nullptr;
31std::unique_ptr<QFile>
Location::s_warningLogFile;
32std::unique_ptr<QTextStream>
Location::s_warningLogStream;
35
36
37
38
39
40
41
42
43
46
47
54
55
56
64
65
66
74
75
76
82 QStack<StackEntry> *oldStk = m_stk;
84 m_stkBottom = other.m_stkBottom;
85 if (other.m_stk ==
nullptr) {
87 m_stkTop = &m_stkBottom;
89 m_stk =
new QStack<StackEntry>(*other.m_stk);
90 m_stkTop = &m_stk->top();
92 m_stkDepth = other.m_stkDepth;
99
100
101
102
103
104
105
114 if (filePath() != other.filePath())
121
122
123
126 return !(*
this == other);
130
131
132
133
136 if (m_stkTop->m_lineNo < 1) {
137 m_stkTop->m_lineNo = 1;
138 m_stkTop->m_columnNo = 1;
143
144
145
146
147
148
149
152 if (ch == QLatin1Char(
'\n')) {
153 m_stkTop->m_lineNo++;
154 m_stkTop->m_columnNo = 1;
155 }
else if (ch == QLatin1Char(
'\t')) {
156 m_stkTop->m_columnNo = 1 + s_tabSize * (m_stkTop->m_columnNo + s_tabSize - 1) / s_tabSize;
158 m_stkTop->m_columnNo++;
163
164
165
166
167
170 if (m_stkDepth++ >= 1) {
171 if (m_stk ==
nullptr)
172 m_stk =
new QStack<StackEntry>;
173 m_stk->push(StackEntry());
174 m_stkTop = &m_stk->top();
177 m_stkTop->m_filePath = filePath;
178 m_stkTop->m_lineNo = INT_MIN;
179 m_stkTop->m_columnNo = 1;
183
184
185
186
187
190 if (--m_stkDepth == 0) {
191 m_stkBottom = StackEntry();
196 if (m_stk->isEmpty()) {
199 m_stkTop = &m_stkBottom;
201 m_stkTop = &m_stk->top();
207
208
209
210
211
214
215
216
217
218
221
222
223
226 QFileInfo fi(filePath());
227 return fi.fileName();
231
232
233
236 QString fp = filePath();
237 return (fp.isEmpty() ? fp : fp.mid(fp.lastIndexOf(
'.') + 1));
241
242
243
244
245
248
249
250
251
252
255
256
257
258
261 const auto &config = Config::instance();
262 if (!config.preparing() || config.singleExec())
263 emitMessage(Warning, message, details);
267
268
269
270
273 const auto &config = Config::instance();
274 if (!config.preparing() || config.singleExec())
275 emitMessage(Error, message, details);
279
280
281
282
285 if (s_warningLimit < 0 || s_warningCount <= s_warningLimit)
290 QStringLiteral(
"Documentation warnings (%1) exceeded the limit (%2) for '%3'.")
291 .arg(QString::number(s_warningCount), QString::number(s_warningLimit),
294 return s_warningCount;
298
299
300
301
304 emitMessage(Error, message, details);
305 information(message);
306 information(details);
307 information(
"Aborting");
312
313
314
315
316
317
318
319
320
323 const auto &config = Config::instance();
324 if ((!config.preparing() || config.singleExec()) && !s_reports.contains(message)) {
325 emitMessage(Report, message, details);
326 s_reports << message;
331
332
333
334
335
338 Config &config = Config::instance();
340 s_programName = config.programName();
347 QString qdocProjectRoot = qEnvironmentVariable(
"QDOC_PROJECT_ROOT");
348 if (qdocProjectRoot.isNull())
351 if (!qdocProjectRoot.isEmpty() && QDir(qdocProjectRoot).exists())
352 s_projectRoot = QDir::cleanPath(qdocProjectRoot);
356 if (qEnvironmentVariableIsSet(
"QDOC_ENABLE_WARNINGLIMIT")
361 if (regExp.isValid()) {
362 s_spuriousRegExp =
new QRegularExpression(regExp);
365 .warning(QStringLiteral(
"Invalid regular expression '%1'")
366 .arg(regExp.pattern()));
370 initializeWarningLog(config);
374
375
378 const auto &config = Config::instance();
381 lines <<
"# QDoc Warning Log"_L1;
382 lines <<
"# Project: "_L1 + s_project;
386 const QStringList args = QCoreApplication::arguments();
387 if (!args.isEmpty()) {
388 QStringList quotedArgs;
389 for (
const QString &arg : args) {
391 if (arg.contains(QLatin1Char(
' '))) {
392 quotedArgs <<
'"'_L1 + arg +
'"'_L1;
397 lines <<
"# Command: "_L1 + quotedArgs.join(QLatin1Char(
' '));
401 if (!s_projectRoot.isEmpty()) {
403 if (!qEnvironmentVariable(
"QDOC_PROJECT_ROOT").isNull()) {
404 lines <<
"# Root: QDOC_PROJECT_ROOT"_L1;
406 lines <<
"# Root: projectroot config"_L1;
408 lines <<
"# Path-Format: relative"_L1;
410 lines <<
"# Path-Format: absolute"_L1;
414 return lines.join(
'\n'_L1);
418
419
420
421QString
Location::formatPathForWarningLog(
const QString &path)
423 if (s_projectRoot.isEmpty())
426 QDir projectDir(s_projectRoot);
427 QString relativePath = projectDir.relativeFilePath(path);
430 if (!relativePath.startsWith(
"../"_L1))
437
438
439
440
441
442
445 const QString logFileName = s_project +
"-qdoc-warnings.log";
446 const QString &outputDir = config.getOutputDir();
449 OutputDirectory::ensure(outputDir,
Location());
451 const QString &logFilePath = dir.absoluteFilePath(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 =
std::move(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.
Represents an output directory that has been verified to exist.
#define CONFIG_LOGWARNINGSDISABLECLIARGS
#define CONFIG_WARNINGLIMIT
#define CONFIG_LOGWARNINGS
#define CONFIG_PROJECTROOT