1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "../cplusplus_global.h"
27 #include <cplusplus/pp.h>
28 #include <QtTest>
29 #include <QFile>
30 #include <QHash>
31 #include <QSet>
32 
33 //TESTED_COMPONENT=src/libs/cplusplus
34 using namespace CPlusPlus;
35 
36 typedef QByteArray _;
37 
38 #define DUMP_OUTPUT(x)     {QByteArray b(x);qDebug("output: [[%s]]", b.replace("\n", "<<\n").constData());}
39 
40 
loadSource(const QString & fileName)41 QByteArray loadSource(const QString &fileName)
42 {
43     QFile inf(QLatin1String(SRCDIR) + QLatin1Char('/') + fileName);
44     if (!inf.open(QIODevice::ReadOnly | QIODevice::Text)) {
45         qDebug("Cannot open \"%s\"", fileName.toUtf8().constData());
46         return QByteArray();
47     }
48 
49     QTextStream ins(&inf);
50     QString source = ins.readAll();
51     inf.close();
52     return source.toUtf8();
53 }
54 
saveData(const QByteArray & data,const QString & fileName)55 void saveData(const QByteArray &data, const QString &fileName)
56 {
57     QFile inf(QLatin1String(SRCDIR) + QLatin1Char('/') + fileName);
58     if (!inf.open(QIODevice::WriteOnly | QIODevice::Text)) {
59         qDebug("Cannot open \"%s\"", fileName.toUtf8().constData());
60         return;
61     }
62 
63     inf.write(data);
64     inf.close();
65 }
66 
stringify(QByteArray code)67 static QByteArray stringify(QByteArray code)
68 {
69     QByteArray res = code.replace('"', "\\\"").replace("\n", "\\n\"\n\"");
70     if (res.endsWith("\"\n\""))
71         res.chop(3);
72     return res;
73 }
74 
compare(const QByteArray & actual,const QByteArray & expected)75 static bool compare(const QByteArray &actual, const QByteArray &expected)
76 {
77     if (actual == expected)
78         return true;
79     qDebug() << "Compared strings are not the same\n"
80                 "   Actual:\n" << stringify(actual)
81              << "   Expected:\n" << stringify(expected);
82     return false;
83 }
84 
85 struct Include
86 {
IncludeInclude87     Include(const QString &fileName, Client::IncludeType type, int line)
88         : fileName(fileName), type(type), line(line)
89     {}
90 
91     QString fileName;
92     Client::IncludeType type;
93     int line;
94 };
95 
operator <<(QDebug & d,const Include & i)96 QDebug &operator<<(QDebug& d, const Include &i)
97 {
98     d << '[' << i.fileName
99       << ',' << (i.type == Client::IncludeGlobal ? "Global"
100             : (i.type == Client::IncludeLocal ? "Local" : "Unknown"))
101       << ',' << i.line
102       << ']';
103     return d;
104 }
105 
106 class MockClient : public Client
107 {
108 public:
109     struct Block {
BlockMockClient::Block110         Block() : start(0), end(0) {}
BlockMockClient::Block111         Block(int start) : start(start), end(0) {}
112 
113         int start;
114         int end;
115     };
116 
117 public:
MockClient(Environment * env,QByteArray * output)118     MockClient(Environment *env, QByteArray *output)
119         : m_env(env)
120         , m_output(output)
121         , m_pp(this, env)
122         , m_includeDepth(0)
123     {}
124 
~MockClient()125     virtual ~MockClient() {}
126 
macroAdded(const Macro & macro)127     virtual void macroAdded(const Macro & macro)
128     {
129         m_definedMacros.append(macro.name());
130         m_definedMacrosLine.append(macro.line());
131     }
132 
passedMacroDefinitionCheck(int,int,int line,const Macro & macro)133     virtual void passedMacroDefinitionCheck(int /*bytesOffset*/,
134                                             int /*utf16charsOffset*/,
135                                             int line,
136                                             const Macro &macro)
137     {
138         m_definitionsResolvedFromLines[macro.name()].append(line);
139     }
140 
failedMacroDefinitionCheck(int,int,const ByteArrayRef & name)141     virtual void failedMacroDefinitionCheck(int /*offset*/,
142                                             int /*utf16charsOffset*/,
143                                             const ByteArrayRef &name)
144     {
145         m_unresolvedDefines.insert(name.toByteArray());
146     }
147 
notifyMacroReference(int bytesOffset,int,int line,const Macro & macro)148     virtual void notifyMacroReference(int bytesOffset, int /*utf16charsOffset*/,
149                                       int line, const Macro &macro)
150     {
151         m_macroUsesLine[macro.name()].append(line);
152         m_expandedMacrosOffset.append(bytesOffset);
153     }
154 
startExpandingMacro(int bytesOffset,int,int line,const Macro & macro,const QVector<MacroArgumentReference> & actuals=QVector<MacroArgumentReference> ())155     virtual void startExpandingMacro(int bytesOffset,
156                                      int /*utf16charsOffset*/,
157                                      int line,
158                                      const Macro &macro,
159                                      const QVector<MacroArgumentReference> &actuals
160                                             = QVector<MacroArgumentReference>())
161     {
162         m_expandedMacros.append(macro.name());
163         m_expandedMacrosOffset.append(bytesOffset);
164         m_macroUsesLine[macro.name()].append(line);
165         m_macroArgsCount.append(actuals.size());
166         m_usedMacros.insert(macro.name(), actuals);
167     }
168 
stopExpandingMacro(int,const Macro &)169     virtual void stopExpandingMacro(int /*offset*/, const Macro &/*macro*/) {}
170 
startSkippingBlocks(int utf16charsOffset)171     virtual void startSkippingBlocks(int utf16charsOffset)
172     { m_skippedBlocks.append(Block(utf16charsOffset)); }
173 
stopSkippingBlocks(int utf16charsOffset)174     virtual void stopSkippingBlocks(int utf16charsOffset)
175     { m_skippedBlocks.last().end = utf16charsOffset; }
176 
sourceNeeded(int line,const QString & includedFileName,IncludeType mode,const QStringList & initialIncludes=QStringList ())177     virtual void sourceNeeded(int line, const QString &includedFileName, IncludeType mode,
178                               const QStringList &initialIncludes = QStringList())
179     {
180         Q_UNUSED(initialIncludes)
181 #if 1
182         m_recordedIncludes.append(Include(includedFileName, mode, line));
183         Q_UNUSED(m_env)
184         Q_UNUSED(m_includeDepth)
185 #else
186         Q_UNUSED(line)
187 
188         QString resolvedFileName;
189         if (mode == IncludeLocal)
190             resolvedFileName = resolveLocally(m_env->currentFile, includedFileName);
191         else
192             resolvedFileName = resolveGlobally(includedFileName);
193 
194     //    qDebug("resolved [[%s]] to [[%s]] from [[%s]] (%s)\n",
195     //           includedFileName.toUtf8().constData(),
196     //           resolvedFileName.toUtf8().constData(),
197     //           currentFileName.toUtf8().constData(),
198     //           (mode == IncludeLocal) ? "locally" : "globally");
199 
200         if (resolvedFileName.isEmpty())
201             return;
202 
203         ++m_includeDepth;
204         //    qDebug("%5d %s %s", m_includeDepth, QByteArray(m_includeDepth, '+').constData(), resolvedFileName.toUtf8().constData());
205         sourceNeeded(resolvedFileName);
206         --m_includeDepth;
207 #endif
208     }
209 
resolveLocally(const QString & currentFileName,const QString & includedFileName) const210     QString resolveLocally(const QString &currentFileName,
211                            const QString &includedFileName) const
212     {
213         QDir dir;
214         if (currentFileName.isEmpty())
215             dir = QDir::current();
216         else
217             dir = QFileInfo(currentFileName).dir();
218         const QFileInfo inc(dir, includedFileName);
219         if (inc.exists()) {
220             return inc.filePath();
221         } else {
222             return QString();
223         }
224     }
225 
resolveGlobally(const QString & currentFileName) const226     QString resolveGlobally(const QString &currentFileName) const
227     {
228         foreach (const QDir &dir, m_includePaths) {
229             QFileInfo f(dir, currentFileName);
230             if (f.exists())
231                 return f.filePath();
232         }
233 
234         return QString();
235     }
236 
setIncludePaths(const QStringList & includePaths)237     void setIncludePaths(const QStringList &includePaths)
238     {
239         foreach (const QString &path, includePaths) {
240             QDir dir(path);
241             if (dir.exists())
242                 m_includePaths.append(dir);
243         }
244     }
245 
sourceNeeded(const QString & fileName,bool nolines)246     void sourceNeeded(const QString &fileName, bool nolines)
247     {
248         QByteArray src = loadSource(fileName);
249         QVERIFY(!src.isEmpty());
250         *m_output = m_pp.run(fileName, src, nolines, true);
251     }
252 
markAsIncludeGuard(const QByteArray & macroName)253     virtual void markAsIncludeGuard(const QByteArray &macroName)
254     { m_includeGuardMacro = macroName; }
255 
includeGuard() const256     QByteArray includeGuard() const
257     { return m_includeGuardMacro; }
258 
skippedBlocks() const259     QList<Block> skippedBlocks() const
260     { return m_skippedBlocks; }
261 
recordedIncludes() const262     QList<Include> recordedIncludes() const
263     { return m_recordedIncludes; }
264 
expandedMacros() const265     QList<QByteArray> expandedMacros() const
266     { return m_expandedMacros; }
267 
expandedMacrosOffset() const268     QList<int> expandedMacrosOffset() const
269     { return m_expandedMacrosOffset; }
270 
definedMacros() const271     QList<QByteArray> definedMacros() const
272     { return m_definedMacros; }
273 
definedMacrosLine() const274     QList<int> definedMacrosLine() const
275     { return m_definedMacrosLine; }
276 
macroUsesLine() const277     QHash<QByteArray, QList<int> > macroUsesLine() const
278     { return m_macroUsesLine; }
279 
definitionsResolvedFromLines() const280     QHash<QByteArray, QList<int> > definitionsResolvedFromLines() const
281     { return m_definitionsResolvedFromLines; }
282 
unresolvedDefines() const283     QSet<QByteArray> unresolvedDefines() const
284     { return m_unresolvedDefines; }
285 
macroArgsCount() const286     const QList<int> macroArgsCount() const
287     { return m_macroArgsCount; }
288 
usedMacros() const289     const QMap<QByteArray, QVector<MacroArgumentReference >> usedMacros() const
290     { return m_usedMacros; }
291 
292 private:
293     Environment *m_env;
294     QByteArray *m_output;
295     Preprocessor m_pp;
296     QList<QDir> m_includePaths;
297     int m_includeDepth;
298     QByteArray m_includeGuardMacro;
299     QList<Block> m_skippedBlocks;
300     QList<Include> m_recordedIncludes;
301     QList<QByteArray> m_expandedMacros;
302     QList<int> m_expandedMacrosOffset;
303     QList<QByteArray> m_definedMacros;
304     QList<int> m_definedMacrosLine;
305     QHash<QByteArray, QList<int> > m_macroUsesLine;
306     QHash<QByteArray, QList<int> > m_definitionsResolvedFromLines;
307     QSet<QByteArray> m_unresolvedDefines;
308     QList<int> m_macroArgsCount;
309     QMap<QByteArray, QVector<MacroArgumentReference >> m_usedMacros;
310 };
311 
312 QT_BEGIN_NAMESPACE
313 namespace QTest {
toString(const QList<int> & list)314     template<> char *toString(const QList<int> &list)
315     {
316         QByteArray ba = "QList<int>(";
317         foreach (const int& item, list) {
318             ba += QTest::toString(item);
319             ba += ',';
320         }
321         if (!list.isEmpty())
322             ba[ba.size() - 1] = ')';
323         return qstrdup(ba.data());
324     }
toString(const QList<QByteArray> & list)325     template<> char *toString(const QList<QByteArray> &list)
326     {
327         QByteArray ba = "QList<QByteArray>(";
328         foreach (const QByteArray& item, list) {
329             ba += QTest::toString(item);
330             ba += ',';
331         }
332         if (!list.isEmpty())
333             ba[ba.size() - 1] = ')';
334         return qstrdup(ba.data());
335     }
336 }
337 QT_END_NAMESPACE
338 
operator <<(QDebug & d,const MockClient::Block & b)339 QDebug &operator<<(QDebug& d, const MockClient::Block &b)
340 {
341     d << '[' << b.start << ',' << b.end << ']';
342     return d;
343 }
344 
345 class tst_Preprocessor : public QObject
346 {
347     Q_OBJECT
348 
349 protected:
preprocess(const QString & fileName,QByteArray *,bool nolines)350     QByteArray preprocess(const QString &fileName, QByteArray * /*errors*/, bool nolines) {
351         //### TODO: hook up errors
352         QByteArray output;
353         Environment env;
354         MockClient client(&env, &output);
355         client.sourceNeeded("data/" + fileName, nolines);
356         return output;
357     }
358     static QByteArray simplified(const QByteArray &buf);
359 
360 private:
361     void compare_input_output(bool keepComments = false);
362 
363 private slots:
364     void va_args();
365     void named_va_args();
366     void extra_va_args();
367     void defined();
368     void defined_data();
369     void defined_usage();
370     void empty_macro_args();
371     void macro_args_count();
372     void macro_args_offsets();
373     void macro_args_offsets_data();
374     void invalid_param_count();
375     void objmacro_expanding_as_fnmacro_notification();
376     void macro_uses();
377     void macro_uses_lines();
378     void macro_arguments_notificatin();
379     void unfinished_function_like_macro_call();
380     void nasty_macro_expansion();
381     void glib_attribute();
382     void builtin__FILE__();
383     void blockSkipping();
384     void includes_1();
385     void dont_eagerly_expand();
386     void dont_eagerly_expand_data();
387     void comparisons_data();
388     void comparisons();
389     void comments_before_args();
390     void comments_within();
391     void comments_within_data();
392     void multitokens_argument();
393     void multitokens_argument_data();
394     void multiline_strings();
395     void multiline_strings_data();
396     void skip_unknown_directives();
397     void skip_unknown_directives_data();
398     void include_guard();
399     void include_guard_data();
400     void empty_trailing_lines();
401     void empty_trailing_lines_data();
402     void undef();
403     void concat();
404     void excessive_nesting();
405     void multi_byte_code_point_in_expansion();
406     void trigraph();
407     void nested_arguments_expansion();
408     void preprocessorSymbolsAsMacroArguments();
409 };
410 
411 // Remove all #... lines, and 'simplify' string, to allow easily comparing the result
412 // Also, remove all unneeded spaces: keep only to ensure identifiers are separated.
413 // NOTE: may not correctly handle underscore in identifiers
simplified(const QByteArray & buf)414 QByteArray tst_Preprocessor::simplified(const QByteArray &buf)
415 {
416     QString out;
417     QList<QByteArray> lines = buf.split('\n');
418     foreach (const QByteArray &line, lines) {
419         if (!line.startsWith('#')) {
420             out.append(" ");
421             out.append(QString::fromUtf8(line));
422         }
423     }
424 
425     out = out.simplified();
426     for (int i = 1; i < out.length() - 1; ) {
427         if (out.at(i).isSpace()
428                 && !(out.at(i-1).isLetterOrNumber()
429                 && out.at(i+1).isLetterOrNumber()))
430             out.remove(i,1);
431         else
432             i++;
433     }
434 
435     return out.toUtf8();
436 }
437 
va_args()438 void tst_Preprocessor::va_args()
439 {
440     Client *client = 0; // no client.
441     Environment env;
442 
443     Preprocessor preprocess(client, &env);
444     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
445                                                 "#define foo(...) int f(__VA_ARGS__);\n"
446                                                 "\nfoo(  )\n"
447                                                 "\nfoo(int a)\n"
448                                                 "\nfoo(int a,int b)\n",
449                                              true, false);
450 
451     preprocessed = preprocessed.simplified();
452 //    DUMP_OUTPUT(preprocessed);
453     QVERIFY(compare(simplified(preprocessed), "int f();int f(int a);int f(int a,int b);"));
454 }
455 
named_va_args()456 void tst_Preprocessor::named_va_args()
457 {
458     Client *client = 0; // no client.
459     Environment env;
460 
461     Preprocessor preprocess(client, &env);
462     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
463                                                 "\n#define foo(ARGS...) int f(ARGS);"
464                                                 "\nfoo(  )\n"
465                                                 "\nfoo(int a)\n"
466                                                 "\nfoo(int a,int b)\n",
467                                              true, false);
468 
469     preprocessed = preprocessed.simplified();
470     QVERIFY(compare(simplified(preprocessed), "int f();int f(int a);int f(int a,int b);"));
471 }
472 
extra_va_args()473 void tst_Preprocessor::extra_va_args()
474 {
475     Client *client = 0; // no client.
476     Environment env;
477 
478     Preprocessor preprocess(client, &env);
479     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
480                                                 "#define foo(ret, ...) ret f(__VA_ARGS__);\n"
481                                                 "\nfoo(int)\n"
482                                                 "\nfoo(float,int b)\n"
483                                                 "\nfoo(long,int b,int c)\n",
484                                              true, false);
485 
486     preprocessed = preprocessed.simplified();
487     QVERIFY(compare(simplified(preprocessed), "int f();float f(int b);long f(int b,int c);"));
488 }
489 
empty_macro_args()490 void tst_Preprocessor::empty_macro_args()
491 {
492     Client *client = 0; // no client.
493     Environment env;
494 
495     Preprocessor preprocess(client, &env);
496     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
497                                                 "\n#define foo(a,b) a int b;"
498                                                 "\nfoo(const,cVal)\n"
499                                                 "\nfoo(,Val)\n"
500                                                 "\nfoo( ,Val2)\n"
501                                                 "\nfoo(,)\n"
502                                                 "\nfoo(, )\n",
503                                              true, false);
504 
505     preprocessed = preprocessed.simplified();
506 //    DUMP_OUTPUT(preprocessed);
507     QVERIFY(compare(simplified(preprocessed), "const int cVal;int Val;int Val2;int;int;"));
508 }
509 
macro_args_count()510 void tst_Preprocessor::macro_args_count()
511 {
512     Environment env;
513     QByteArray output;
514     MockClient client(&env, &output);
515     Preprocessor preprocess(&client, &env);
516     preprocess.run(QLatin1String("<stdin>"),
517                    "#define foo(a,b) a int b;\n"
518                    "foo(const,cVal)\n"
519                    "foo(, i)\n"
520                    "foo(,Val)\n"
521                    "foo( ,Val2)\n"
522                    "foo(,)\n"
523                    "foo(, )\n"
524                    "#define bar(a)\n"
525                    "bar()\n"
526                    "bar(i)\n",
527                    true, false);
528 
529     QCOMPARE(client.macroArgsCount(),
530              QList<int>() << 2 // foo(const,cVal)
531                           << 2 // foo(, i)
532                           << 2 // foo(,Val)
533                           << 2 // foo( , Val2)
534                           << 2 // foo(,)
535                           << 2 // foo(, )
536                           << 1 // bar()
537                           << 1 // bar(i)
538             );
539 
540 }
541 
macro_args_offsets()542 void tst_Preprocessor::macro_args_offsets()
543 {
544     QFETCH(QString, fileName);
545     QFETCH(QByteArray, source);
546     QFETCH(QByteArray, macroName);
547     QFETCH(int, bytesOffset);
548     QFETCH(int, bytesLength);
549     QFETCH(int, utf16charsOffset);
550     QFETCH(int, utf16charsLength);
551 
552     Environment env;
553     QByteArray output;
554     MockClient client(&env, &output);
555     Preprocessor preprocess(&client, &env);
556     preprocess.run(fileName, source, true, false);
557 
558     QMap<QByteArray, QVector<MacroArgumentReference >> usedMacros = client.usedMacros();
559     QCOMPARE(usedMacros.size(), 1);
560     QVERIFY(usedMacros.contains(macroName));
561     MacroArgumentReference argRef = usedMacros.value(macroName).at(0);
562     QCOMPARE(argRef.bytesOffset(), bytesOffset);
563     QCOMPARE(argRef.bytesLength(), bytesLength);
564     QCOMPARE(argRef.utf16charsOffset(), utf16charsOffset);
565     QCOMPARE(argRef.utf16charsLength(), utf16charsLength);
566 
567 
568 }
569 
macro_args_offsets_data()570 void tst_Preprocessor::macro_args_offsets_data()
571 {
572     QTest::addColumn<QString>("fileName");
573     QTest::addColumn<QByteArray>("source");
574     QTest::addColumn<QByteArray>("macroName");
575     QTest::addColumn<int>("bytesOffset");
576     QTest::addColumn<int>("bytesLength");
577     QTest::addColumn<int>("utf16charsOffset");
578     QTest::addColumn<int>("utf16charsLength");
579 
580     QString fN = QLatin1String("<stdin>");
581     QByteArray src = QByteArray("#define SQR(a) ( a * a )\n"
582                                 "void f(){\n"
583                                 "int i = 10;\n"
584                                 "int j = SQR(10);\n"
585                                 "}");
586     QTest::newRow("ascii_only_before_ref") << fN << src << QByteArray("SQR")
587                                            << 59 << 2 << 59 << 2;
588     src.replace("int i", "int äöü");
589     QTest::newRow("ascii_with_umlauts_before_ref") << fN << src << QByteArray("SQR")
590                                                    << 64 << 2 << 61 << 2;
591     src.clear();
592     src.append("#define OUT(format, ...) printf(\"%s %d: \" format, __FILE__, __LINE__)\n"
593                "void f(){\n"
594                "OUT(\"Hei verden!\\n\");\n"
595                "}\n");
596     QTest::newRow("arg_ascii_only") << fN << src << QByteArray("OUT")
597                                     << 84 << 15 << 84 << 15;
598     src.replace("Hei verden", UC_U00FC);
599     QTest::newRow("arg_ascii_with_unicode_00fc") << fN << src << QByteArray("OUT")
600                                                  << 84 << 7 << 84 << 6;
601     src.replace(UC_U00FC, UC_U4E8C);
602     QTest::newRow("arg_ascii_with_unicode_4e8c") << fN << src << QByteArray("OUT")
603                                                  << 84 << 8 << 84 << 6;
604     src.replace(UC_U4E8C, UC_U10302);
605     QTest::newRow("arg_ascii_with_unicode_10302") << fN << src << QByteArray("OUT")
606                                                   << 84 << 9 << 84 << 7;
607 }
608 
invalid_param_count()609 void tst_Preprocessor::invalid_param_count()
610 {
611     Environment env;
612     QByteArray output;
613     MockClient client(&env, &output);
614     Preprocessor preprocess(&client, &env);
615     // The following are illegal, but shouldn't crash the preprocessor.
616     preprocess.run(QLatin1String("<stdin>"),
617                    "\n#define foo(a,b) int f(a,b);"
618                    "\n#define ARGS(t)  t a,t b"
619                    "\nfoo(ARGS(int))"
620                    "\nfoo()"
621                    "\nfoo(int a, int b, int c)",
622                    true, false);
623 
624     // Output is not that relevant but check that nothing triggered expansion.
625     QCOMPARE(client.macroArgsCount(), QList<int>());
626 }
627 
macro_uses()628 void tst_Preprocessor::macro_uses()
629 {
630     QByteArray buffer = QByteArray("\n#define FOO 8"
631                                    "\n#define BAR 9"
632                                    "\nvoid test(){"
633                                    "\n\tint x=FOO;"
634                                    "\n\tint y=BAR;"
635                                    "\n}");
636 
637     QByteArray output;
638     Environment env;
639     MockClient client(&env, &output);
640 
641     Preprocessor preprocess(&client, &env);
642     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), buffer);
643     QVERIFY(compare(simplified(preprocessed), "void test(){int x=8;int y=9;}"));
644     QCOMPARE(client.expandedMacros(), QList<QByteArray>() << QByteArray("FOO") << QByteArray("BAR"));
645     QCOMPARE(client.expandedMacrosOffset(), QList<int>() << buffer.indexOf("FOO;") << buffer.indexOf("BAR;"));
646     QCOMPARE(client.definedMacros(), QList<QByteArray>() << QByteArray("FOO") << QByteArray("BAR"));
647     QCOMPARE(client.definedMacrosLine(), QList<int>() << 2 << 3);
648 }
649 
macro_uses_lines()650 void tst_Preprocessor::macro_uses_lines()
651 {
652     QByteArray buffer("#define FOO\n"
653                       "FOO\n"
654                       "\n"
655                       "#define HEADER <test>\n"
656                       "#include HEADER\n"
657                       "\n"
658                       "#define DECLARE(C, V) struct C {}; C V;\n"
659                       "#define ABC X\n"
660                       "DECLARE(Test, test)\n"
661                       "\n"
662                       "int abc;\n"
663                       "#define NOTHING(C)\n"
664                       "NOTHING(abc)\n"
665                       "\n"
666                       "#define ENABLE(FEATURE) (defined ENABLE_##FEATURE && ENABLE_##FEATURE)\n"
667                       "#define ENABLE_COOL 1\n"
668                       "void fill();\n"
669                       "#if ENABLE(COOL)\n"
670                       "class Cool {};\n"
671                       "#endif\n"
672                       "int cool = ENABLE_COOL;\n"
673                       "#define OTHER_ENABLE(FEATURE) ENABLE(FEATURE)\n"
674                       "#define MORE(LESS) FOO ENABLE(LESS)\n");
675 
676     QByteArray output;
677     Environment env;
678     MockClient client(&env, &output);
679     Preprocessor preprocess(&client, &env);
680     preprocess.run(QLatin1String("<stdin>"), buffer);
681 
682     QCOMPARE(client.macroUsesLine().value("FOO"), QList<int>() << 2 << 23);
683     QCOMPARE(client.macroUsesLine().value("HEADER"), QList<int>() << 5);
684     QCOMPARE(client.macroUsesLine().value("DECLARE"), QList<int>() << 9);
685     QCOMPARE(client.macroUsesLine().value("NOTHING"), QList<int>() << 13);
686     QCOMPARE(client.macroUsesLine().value("ENABLE"), QList<int>() << 18 << 22 << 23);
687     QCOMPARE(client.macroUsesLine().value("ENABLE_COOL"), QList<int>() << 21);
688     QCOMPARE(client.definitionsResolvedFromLines().value("ENABLE_COOL"), QList<int>() << 18);
689     QCOMPARE(client.expandedMacrosOffset(), QList<int>()
690              << buffer.lastIndexOf("FOO\n")
691              << buffer.lastIndexOf("HEADER")
692              << buffer.lastIndexOf("DECLARE")
693              << buffer.lastIndexOf("NOTHING")
694              << buffer.lastIndexOf("ENABLE(COOL)")
695              << buffer.lastIndexOf("ENABLE_COOL")
696              << buffer.lastIndexOf("ENABLE(FEATURE)")
697              << buffer.lastIndexOf("FOO ")
698              << buffer.lastIndexOf("ENABLE(LESS)"));
699 }
700 
multitokens_argument_data()701 void tst_Preprocessor::multitokens_argument_data()
702 {
703     QTest::addColumn<QByteArray>("input");
704     QTest::addColumn<QByteArray>("output");
705 
706     QTest::newRow("case 1") << _(
707             "#define foo(ARGS) int f(ARGS)\n"
708             "foo(int a);\n"
709         ) << _(
710             "# 1 \"<stdin>\"\n"
711             "\n"
712             "# expansion begin 30,3 ~3 2:4 2:8 ~1\n"
713             "int f(int a)\n"
714             "# expansion end\n"
715             "# 2 \"<stdin>\"\n"
716             "          ;\n"
717     );
718 
719     QTest::newRow("case 2") << _(
720             "#define foo(ARGS) int f(ARGS)\n"
721             "foo(int   \n"
722             "    a);\n"
723         ) << _(
724             "# 1 \"<stdin>\"\n"
725             "\n"
726             "# expansion begin 30,3 ~3 2:4 3:4 ~1\n"
727             "int f(int a)\n"
728             "# expansion end\n"
729             "# 3 \"<stdin>\"\n"
730             "      ;\n"
731     );
732 
733     QTest::newRow("case 3") << _(
734             "#define foo(ARGS) int f(ARGS)\n"
735             "foo(int a = 0);\n"
736         ) << _(
737             "# 1 \"<stdin>\"\n"
738             "\n"
739             "# expansion begin 30,3 ~3 2:4 2:8 2:10 2:12 ~1\n"
740             "int f(int a = 0)\n"
741             "# expansion end\n"
742             "# 2 \"<stdin>\"\n"
743             "              ;\n"
744     );
745 
746     QTest::newRow("case 4") << _(
747             "#define foo(X) int f(X = 0)\n"
748             "foo(int \n"
749             "    a);\n"
750         ) << _(
751             "# 1 \"<stdin>\"\n"
752             "\n"
753             "# expansion begin 28,3 ~3 2:4 3:4 ~3\n"
754             "int f(int a = 0)\n"
755             "# expansion end\n"
756             "# 3 \"<stdin>\"\n"
757             "      ;\n"
758     );
759 }
760 
multitokens_argument()761 void tst_Preprocessor::multitokens_argument()
762 {
763     compare_input_output();
764 }
765 
objmacro_expanding_as_fnmacro_notification()766 void tst_Preprocessor::objmacro_expanding_as_fnmacro_notification()
767 {
768     QByteArray output;
769     Environment env;
770     MockClient client(&env, &output);
771 
772     Preprocessor preprocess(&client, &env);
773     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
774                                          QByteArray("\n#define bar(a,b) a + b"
775                                                     "\n#define foo bar"
776                                                     "\nfoo(1, 2)\n"));
777 
778     QVERIFY(client.expandedMacros() == (QList<QByteArray>() << QByteArray("foo")));
779 }
780 
macro_arguments_notificatin()781 void tst_Preprocessor::macro_arguments_notificatin()
782 {
783     QByteArray output;
784     Environment env;
785     MockClient client(&env, &output);
786 
787     Preprocessor preprocess(&client, &env);
788     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
789                                          QByteArray("\n#define foo(a,b) a + b"
790                                                     "\n#define arg(a) a"
791                                                     "\n#define value  2"
792                                                     "\nfoo(arg(1), value)\n"));
793 
794     QVERIFY(client.expandedMacros() == (QList<QByteArray>() << QByteArray("foo")
795                                                             << QByteArray("arg")
796                                                             << QByteArray("value")));
797 }
798 
unfinished_function_like_macro_call()799 void tst_Preprocessor::unfinished_function_like_macro_call()
800 {
801     Client *client = 0; // no client.
802     Environment env;
803 
804     Preprocessor preprocess(client, &env);
805     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
806                                              QByteArray("\n"
807                                                         "#define foo(a,b) a + b\n"
808                                                         "foo(1, 2\n"));
809     QByteArray expected__("# 1 \"<stdin>\"\n"
810                           "\n"
811                           "\n"
812                           "foo\n");
813 
814 //    DUMP_OUTPUT(preprocessed);
815     QVERIFY(compare(preprocessed, expected__));
816 }
817 
nasty_macro_expansion()818 void tst_Preprocessor::nasty_macro_expansion()
819 {
820     QByteArray input("\n"
821                      "#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))\n"
822                      "#define is_power_of_two(x)      ( !((x) & ((x)-1)) )\n"
823                      "#define low_bit_mask(x)         ( ((x)-1) & ~(x) )\n"
824                      "#define is_valid_mask(x)        is_power_of_two(1LU + (x) + low_bit_mask(x))\n"
825                      "#define compile_ffs2(__x) \\\n"
826                      "        __builtin_choose_expr(((__x) & 0x1), 0, 1)\n"
827                      "#define compile_ffs4(__x) \\\n"
828                      "        __builtin_choose_expr(((__x) & 0x3), \\\n"
829                      "                              (compile_ffs2((__x))), \\\n"
830                      "                              (compile_ffs2((__x) >> 2) + 2))\n"
831                      "#define compile_ffs8(__x) \\\n"
832                      "        __builtin_choose_expr(((__x) & 0xf), \\\n"
833                      "                              (compile_ffs4((__x))), \\\n"
834                      "                              (compile_ffs4((__x) >> 4) + 4))\n"
835                      "#define compile_ffs16(__x) \\\n"
836                      "        __builtin_choose_expr(((__x) & 0xff), \\\n"
837                      "                              (compile_ffs8((__x))), \\\n"
838                      "                              (compile_ffs8((__x) >> 8) + 8))\n"
839                      "#define compile_ffs32(__x) \\\n"
840                      "        __builtin_choose_expr(((__x) & 0xffff), \\\n"
841                      "                              (compile_ffs16((__x))), \\\n"
842                      "                              (compile_ffs16((__x) >> 16) + 16))\n"
843                      "#define FIELD_CHECK(__mask, __type)                     \\\n"
844                      "        BUILD_BUG_ON(!(__mask) ||                       \\\n"
845                      "                     !is_valid_mask(__mask) ||          \\\n"
846                      "                     (__mask) != (__type)(__mask))      \\\n"
847                      "\n"
848                      "#define FIELD32(__mask)                         \\\n"
849                      "({                                              \\\n"
850                      "        FIELD_CHECK(__mask, u32);               \\\n"
851                      "        (struct rt2x00_field32) {               \\\n"
852                      "                compile_ffs32(__mask), (__mask) \\\n"
853                      "        };                                      \\\n"
854                      "})\n"
855                      "#define BBPCSR                          0x00f0\n"
856                      "#define BBPCSR_BUSY                     FIELD32(0x00008000)\n"
857                      "#define WAIT_FOR_BBP(__dev, __reg)  \\\n"
858                      "        rt2x00pci_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg))\n"
859                      "if (WAIT_FOR_BBP(rt2x00dev, &reg)) {}\n"
860                      );
861 
862     Client *client = 0; // no client.
863     Environment env;
864 
865     Preprocessor preprocess(client, &env);
866     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), input);
867 
868     QVERIFY(!preprocessed.contains("FIELD32"));
869 }
870 
glib_attribute()871 void tst_Preprocessor::glib_attribute()
872 {
873     Environment env;
874     Preprocessor preprocess(0, &env);
875     QByteArray preprocessed = preprocess.run(
876                 QLatin1String("<stdin>"),
877                 QByteArray("\n"
878                            "# define _GLIBCXX_VISIBILITY(V) __attribute__ ((__visibility__ (#V)))\n"
879                            "namespace std _GLIBCXX_VISIBILITY(default) {\n"
880                            "}\n"
881                            ));
882     const QByteArray result____ =
883             "# 1 \"<stdin>\"\n"
884             "\n"
885             "\n"
886             "namespace std\n"
887             "# expansion begin 85,19 ~9\n"
888             "__attribute__ ((__visibility__ (\"default\")))\n"
889             "# expansion end\n"
890             "# 3 \"<stdin>\"\n"
891             "                                           {\n"
892             "}\n";
893 
894 //    DUMP_OUTPUT(preprocessed);
895     QVERIFY(compare(preprocessed, result____));
896 }
897 
builtin__FILE__()898 void tst_Preprocessor::builtin__FILE__()
899 {
900     Client *client = 0; // no client.
901     Environment env;
902 
903     Preprocessor preprocess(client, &env);
904     QByteArray preprocessed = preprocess.run(
905                 QLatin1String("some-file.c"),
906                 QByteArray("const char *f = __FILE__\n"
907                            ));
908     const QByteArray result____ =
909             "# 1 \"some-file.c\"\n"
910             "const char *f = \"some-file.c\"\n";
911 
912     QVERIFY(compare(preprocessed, result____));
913 }
914 
comparisons_data()915 void tst_Preprocessor::comparisons_data()
916 {
917     QTest::addColumn<QString>("infile");
918     QTest::addColumn<QString>("outfile");
919     QTest::addColumn<QString>("errorfile");
920 
921     QTest::newRow("do nothing") << "noPP.1.cpp" << "noPP.1.cpp" << "";
922     QTest::newRow("no PP 2") << "noPP.2.cpp" << "noPP.2.cpp" << "";
923     QTest::newRow("identifier-expansion 1")
924         << "identifier-expansion.1.cpp" << "identifier-expansion.1.out.cpp" << "";
925     QTest::newRow("identifier-expansion 2")
926         << "identifier-expansion.2.cpp" << "identifier-expansion.2.out.cpp" << "";
927     QTest::newRow("identifier-expansion 3")
928         << "identifier-expansion.3.cpp" << "identifier-expansion.3.out.cpp" << "";
929     QTest::newRow("identifier-expansion 4")
930         << "identifier-expansion.4.cpp" << "identifier-expansion.4.out.cpp" << "";
931     QTest::newRow("identifier-expansion 5")
932         << "identifier-expansion.5.cpp" << "identifier-expansion.5.out.cpp" << "";
933     QTest::newRow("reserved 1")
934         << "reserved.1.cpp" << "reserved.1.out.cpp" << "";
935     QTest::newRow("recursive 1")
936         << "recursive.1.cpp" << "recursive.1.out.cpp" << "";
937     QTest::newRow("recursive 2")
938         << "recursive.2.cpp" << "recursive.2.out.cpp" << "";
939     QTest::newRow("macro_pounder_fn")
940         << "macro_pounder_fn.c" << "" << "";
941     QTest::newRow("macro_expand")
942         << "macro_expand.c" << "macro_expand.out.c" << "";
943     QTest::newRow("macro_expand_1")
944         << "macro_expand_1.cpp" << "macro_expand_1.out.cpp" << "";
945     QTest::newRow("macro-test")
946         << "macro-test.cpp" << "macro-test.out.cpp" << "";
947     QTest::newRow("empty-macro")
948         << "empty-macro.cpp" << "empty-macro.out.cpp" << "";
949     QTest::newRow("empty-macro 2")
950         << "empty-macro.2.cpp" << "empty-macro.2.out.cpp" << "";
951     QTest::newRow("poundpound 1")
952         << "poundpound.1.cpp" << "poundpound.1.out.cpp" << "";
953 }
954 
comparisons()955 void tst_Preprocessor::comparisons()
956 {
957     QFETCH(QString, infile);
958     QFETCH(QString, outfile);
959     QFETCH(QString, errorfile);
960 
961     QByteArray errors;
962     QByteArray preprocessed = preprocess(infile, &errors, infile == outfile);
963 
964 
965     // DUMP_OUTPUT(preprocessed);
966 
967     if (!outfile.isEmpty()) {
968         // These weird underscores are here to make the name as long as
969         // "preprocessed", so the QCOMPARE error messages are nicely aligned.
970         QByteArray output____ = loadSource("data/" + outfile);
971         QVERIFY(compare(preprocessed, output____));
972     }
973 
974     if (!errorfile.isEmpty()) {
975         QByteArray errorFileContents = loadSource("data/" + errorfile);
976         QVERIFY(compare(errors, errorFileContents));
977     }
978 }
979 
blockSkipping()980 void tst_Preprocessor::blockSkipping()
981 {
982     QByteArray output;
983     Environment env;
984     MockClient client(&env, &output);
985     Preprocessor pp(&client, &env);
986     /*QByteArray preprocessed =*/ pp.run(
987                 QLatin1String("<stdin>"),
988                 QByteArray("#if 0\n"
989                            "\n"
990                            "int yes;\n"
991                            "\n"
992                            "#elif 0\n"
993                            "\n"
994                            "int no;\n"
995                            "\n"
996                            "#else // foobar\n"
997                            "\n"
998                            "void also_not;\n"
999                            "\n"
1000                            "#endif\n"
1001                            ));
1002 
1003     QList<MockClient::Block> blocks = client.skippedBlocks();
1004     QCOMPARE(blocks.size(), 1);
1005     MockClient::Block b = blocks.at(0);
1006     QCOMPARE(b.start, 6);
1007     QCOMPARE(b.end, 34);
1008 }
1009 
includes_1()1010 void tst_Preprocessor::includes_1()
1011 {
1012     QByteArray output;
1013     Environment env;
1014     MockClient client(&env, &output);
1015     Preprocessor pp(&client, &env);
1016     /*QByteArray preprocessed =*/ pp.run(
1017                 QLatin1String("<stdin>"),
1018                 QByteArray("#define FOO <foo.h>\n"
1019                            "#define BAR \"bar.h\"\n"
1020                            "\n"
1021                            "#include FOO\n"
1022                            "#include BAR\n"
1023                            "\n"
1024                            "#include <zoo.h>\n"
1025                            "#include \"mooze.h\"\n"
1026                            ));
1027 
1028     QList<Include> incs = client.recordedIncludes();
1029 //    qDebug()<<incs;
1030     QCOMPARE(incs.size(), 4);
1031     QCOMPARE(incs.at(0).fileName, QLatin1String("foo.h"));
1032     QCOMPARE(incs.at(0).type, Client::IncludeGlobal);
1033     QCOMPARE(incs.at(0).line, 4);
1034     QCOMPARE(incs.at(1).fileName, QLatin1String("bar.h"));
1035     QCOMPARE(incs.at(1).type, Client::IncludeLocal);
1036     QCOMPARE(incs.at(1).line, 5);
1037     QCOMPARE(incs.at(2).fileName, QLatin1String("zoo.h"));
1038     QCOMPARE(incs.at(2).type, Client::IncludeGlobal);
1039     QCOMPARE(incs.at(2).line, 7);
1040     QCOMPARE(incs.at(3).fileName, QLatin1String("mooze.h"));
1041     QCOMPARE(incs.at(3).type, Client::IncludeLocal);
1042     QCOMPARE(incs.at(3).line, 8);
1043 }
1044 
defined()1045 void tst_Preprocessor::defined()
1046 {
1047     QFETCH(bool, xdefined);
1048     QFETCH(bool, ydefined);
1049     QFETCH(QString, input);
1050     QByteArray output;
1051     Environment env;
1052     MockClient client(&env, &output);
1053     Preprocessor pp(&client, &env);
1054     pp.run(QLatin1String("<stdin>"), input.toLatin1(), false, true);
1055     QList<QByteArray> expected;
1056     if (xdefined)
1057         expected.append("X");
1058     if (ydefined)
1059         expected.append("Y");
1060     if (client.definedMacros() != expected)
1061         qWarning() << "\nSource: " << input.replace('\n', "    ");
1062     QCOMPARE(client.definedMacros(), expected);
1063 }
1064 
defined_data()1065 void tst_Preprocessor::defined_data()
1066 {
1067     QTest::addColumn<bool>("xdefined");
1068     QTest::addColumn<bool>("ydefined");
1069     QTest::addColumn<QString>("input");
1070 
1071     QTest::newRow("1a") << true << true <<
1072         "#define X\n#if defined(X)\n#define Y\n#endif";
1073     QTest::newRow("1b") << true << true <<
1074         "#define X\n#if defined X \n#define Y\n#endif";
1075     QTest::newRow("1c") << true << true <<
1076         "#define X\n#ifdef X \n#define Y\n#endif";
1077 
1078     QTest::newRow("2a") << false << false <<
1079         "#if defined(X)\n#define Y\n#endif";
1080     QTest::newRow("2b") << false << false <<
1081         "#if defined X \n#define Y\n#endif";
1082     QTest::newRow("2c") << false << false <<
1083         "#ifdef X \n#define Y\n#endif";
1084 
1085     QTest::newRow("3a") << true << false <<
1086         "#define X\n#if !defined(X)\n#define Y\n#endif";
1087     QTest::newRow("3b") << true << false <<
1088         "#define X\n#if !defined X \n#define Y\n#endif";
1089     QTest::newRow("3c") << true << false <<
1090         "#define X\n#ifndef X \n#define Y\n#endif";
1091 
1092     QTest::newRow("4a") << false << true <<
1093         "#if !defined(X)\n#define Y\n#endif";
1094     QTest::newRow("4b") << false << true <<
1095         "#if !defined X \n#define Y\n#endif";
1096     QTest::newRow("4c") << false << true <<
1097         "#ifndef X \n#define Y\n#endif";
1098 
1099     QTest::newRow("5a") << false << false <<
1100         "#if !defined(X) && (defined(Y))\n"
1101         "#define X\n"
1102         "#endif\n";
1103     QTest::newRow("5b") << false << false <<
1104         "#if !defined(X) && defined(Y)\n"
1105         "#define X\n"
1106         "#endif\n";
1107     QTest::newRow("5c") << false << false <<
1108         "#if !defined(X) && 0"
1109         "#define X\n"
1110         "#endif\n";
1111     QTest::newRow("5d") << false << false <<
1112         "#if (!defined(X)) && defined(Y)\n"
1113         "#define X\n"
1114         "#endif\n";
1115     QTest::newRow("5d") << false << false <<
1116         "#if (define(Y))\n"
1117         "#define X\n"
1118         "#endif\n";
1119 
1120     QTest::newRow("6a") << true << true <<
1121         "#define X 0x040500\n"
1122         "#if X > 0x040000\n"
1123         "#define Y 1\n"
1124         "#endif\n";
1125     QTest::newRow("6b") << true << true <<
1126         "#define X 0x040500\n"
1127         "#if X >= 0x040000\n"
1128         "#define Y 1\n"
1129         "#endif\n";
1130     QTest::newRow("6c") << true << false <<
1131         "#define X 0x040500\n"
1132         "#if X == 0x040000\n"
1133         "#define Y 1\n"
1134         "#endif\n";
1135     QTest::newRow("6d") << true << true <<
1136         "#define X 0x040500\n"
1137         "#if X == 0x040500\n"
1138         "#define Y 1\n"
1139         "#endif\n";
1140     QTest::newRow("6e") << true << false <<
1141         "#define X 0x040500\n"
1142         "#if X < 0x040000\n"
1143         "#define Y 1\n"
1144         "#endif\n";
1145     QTest::newRow("6f") << true << false <<
1146         "#define X 0x040500\n"
1147         "#if X <= 0x040000\n"
1148         "#define Y 1\n"
1149         "#endif\n";
1150 
1151     QTest::newRow("incomplete defined 1") << true << true <<
1152         "#define X 0x040500\n"
1153         "#if defined(X\n"
1154         "#define Y 1\n"
1155         "#endif\n";
1156     QTest::newRow("incomplete defined 2") << false << false <<
1157         "#if defined(X\n"
1158         "#define Y 1\n"
1159         "#endif\n";
1160     QTest::newRow("complete defined 1") << true << true <<
1161         "#define X 0x040500\n"
1162         "#if defined(X )\n"
1163         "#define Y 1\n"
1164         "#endif\n";
1165     QTest::newRow("complete defined 2") << true << true <<
1166         "#define X 0x040500\n"
1167         "#if defined(X/*xxx*/)\n"
1168         "#define Y 1\n"
1169         "#endif\n";
1170 }
1171 
defined_usage()1172 void tst_Preprocessor::defined_usage()
1173 {
1174     QByteArray output;
1175     Environment env;
1176     MockClient client(&env, &output);
1177     Preprocessor pp(&client, &env);
1178     QByteArray source =
1179             "#define X\n"
1180             "#define Y\n"
1181             "#ifdef X\n"
1182             "#endif\n"
1183             "#ifdef Y\n"
1184             "#endif\n"
1185             "#ifndef X\n"
1186             "#endif\n"
1187             "#ifndef Y\n"
1188             "#endif\n"
1189             "#ifdef ABSENT\n"
1190             "#endif\n"
1191             "#ifndef ABSENT2\n"
1192             "#endif\n"
1193             "#if defined(ABSENT3)\n"
1194             "#endif\n"
1195             "#if defined(X)\n"
1196             "#endif\n"
1197             "#if defined(X) || defined(Y)\n"
1198             "#endif\n"
1199             ;
1200     pp.run(QLatin1String("<stdin>"), source);
1201     QHash<QByteArray, QList<int> > definitionsResolvedFromLines =
1202             client.definitionsResolvedFromLines();
1203     QCOMPARE(definitionsResolvedFromLines["X"], QList<int>() << 3 << 7 << 17 << 19);
1204     QCOMPARE(definitionsResolvedFromLines["Y"], QList<int>() << 5 << 9 << 19);
1205     QCOMPARE(client.unresolvedDefines(), QSet<QByteArray>() << "ABSENT" << "ABSENT2" << "ABSENT3");
1206 }
1207 
dont_eagerly_expand_data()1208 void tst_Preprocessor::dont_eagerly_expand_data()
1209 {
1210     QTest::addColumn<QByteArray>("input");
1211     QTest::addColumn<QByteArray>("output");
1212 
1213     // Expansion must be processed upon invocation of the macro. Therefore a particular
1214     // identifier within a define must not be expanded (in the case it matches an
1215     // already known macro) during the processor directive handling, but only when
1216     // it's actually "used". Naturally, if it's still not replaced after an invocation
1217     // it should then be expanded. This is consistent with clang and gcc for example.
1218 
1219     QTest::newRow("case 1") << _(
1220             "#define T int\n"
1221             "#define FOO(T) T\n"
1222             "FOO(double)\n"
1223         ) << _(
1224             "# 1 \"<stdin>\"\n"
1225             "\n"
1226             "\n"
1227             "# expansion begin 31,3 3:4\n"
1228             "double\n"
1229             "# expansion end\n"
1230             "# 4 \"<stdin>\"\n"
1231     );
1232 
1233     QTest::newRow("case 2") << _(
1234             "#define T int\n"
1235             "#define FOO(X) T\n"
1236             "FOO(double)\n"
1237         ) << _(
1238             "# 1 \"<stdin>\"\n"
1239             "\n"
1240             "\n"
1241             "# expansion begin 31,3 ~1\n"
1242             "int\n"
1243             "# expansion end\n"
1244             "# 4 \"<stdin>\"\n"
1245     );
1246 }
1247 
dont_eagerly_expand()1248 void tst_Preprocessor::dont_eagerly_expand()
1249 {
1250     compare_input_output();
1251 }
1252 
comments_within()1253 void tst_Preprocessor::comments_within()
1254 {
1255     QFETCH(QByteArray, input);
1256     QFETCH(QByteArray, without_comments);
1257     QFETCH(QByteArray, with_comments);
1258 
1259     Environment env;
1260     Preprocessor preprocess(0, &env);
1261     preprocess.setKeepComments(false);
1262     QByteArray prep = preprocess.run(QLatin1String("<stdin>"), input);
1263     QVERIFY(compare(prep, without_comments));
1264     preprocess.setKeepComments(true);
1265     prep = preprocess.run(QLatin1String("<stdin>"), input);
1266     QVERIFY(compare(prep, with_comments));
1267 }
1268 
comments_within_data()1269 void tst_Preprocessor::comments_within_data()
1270 {
1271     QTest::addColumn<QByteArray>("input");
1272     QTest::addColumn<QByteArray>("without_comments");
1273     QTest::addColumn<QByteArray>("with_comments");
1274 
1275     QTest::newRow("case 1") << _(
1276             "#define FOO int x;\n"
1277             "\n"
1278             "   // comment\n"
1279             "   // comment\n"
1280             "   // comment\n"
1281             "   // comment\n"
1282             "FOO\n"
1283             "x = 10\n"
1284         ) << _(
1285             "# 1 \"<stdin>\"\n"
1286             "\n"
1287             "\n"
1288             "\n"
1289             "\n"
1290             "\n"
1291             "\n"
1292             "# expansion begin 76,3 ~3\n"
1293             "int x;\n"
1294             "# expansion end\n"
1295             "# 8 \"<stdin>\"\n"
1296             "x = 10\n"
1297         ) << _(
1298             "# 1 \"<stdin>\"\n"
1299             "\n"
1300             "\n"
1301             "   // comment\n"
1302             "   // comment\n"
1303             "   // comment\n"
1304             "   // comment\n"
1305             "# expansion begin 76,3 ~3\n"
1306             "int x;\n"
1307             "# expansion end\n"
1308             "# 8 \"<stdin>\"\n"
1309             "x = 10\n"
1310     );
1311 
1312     QTest::newRow("case 2") << _(
1313             "#define FOO int x;\n"
1314             "\n"
1315             "   /* comment\n"
1316             "      comment\n"
1317             "      comment\n"
1318             "      comment */\n"
1319             "FOO\n"
1320             "x = 10\n"
1321         ) << _(
1322             "# 1 \"<stdin>\"\n"
1323             "\n"
1324             "\n"
1325             "\n"
1326             "\n"
1327             "\n"
1328             "\n"
1329             "# expansion begin 79,3 ~3\n"
1330             "int x;\n"
1331             "# expansion end\n"
1332             "# 8 \"<stdin>\"\n"
1333             "x = 10\n"
1334         ) << _(
1335             "# 1 \"<stdin>\"\n"
1336             "\n"
1337             "\n"
1338             "   /* comment\n"
1339             "      comment\n"
1340             "      comment\n"
1341             "      comment */\n"
1342             "# expansion begin 79,3 ~3\n"
1343             "int x;\n"
1344             "# expansion end\n"
1345             "# 8 \"<stdin>\"\n"
1346             "x = 10\n"
1347     );
1348 
1349     QTest::newRow("case 3") << _(
1350             "#define FOO int x;\n"
1351             "\n"
1352             "   // comment\n"
1353             "   // comment\n"
1354             "   // comment\n"
1355             "   // comment\n"
1356             "FOO\n"
1357             "// test\n"
1358             "// test again\n"
1359             "x = 10\n"
1360         ) << _(
1361             "# 1 \"<stdin>\"\n"
1362             "\n"
1363             "\n"
1364             "\n"
1365             "\n"
1366             "\n"
1367             "\n"
1368             "# expansion begin 76,3 ~3\n"
1369             "int x;\n"
1370             "# expansion end\n"
1371             "# 10 \"<stdin>\"\n"
1372             "x = 10\n"
1373         ) << _(
1374             "# 1 \"<stdin>\"\n"
1375             "\n"
1376             "\n"
1377             "   // comment\n"
1378             "   // comment\n"
1379             "   // comment\n"
1380             "   // comment\n"
1381             "# expansion begin 76,3 ~3\n"
1382             "int x;\n"
1383             "# expansion end\n"
1384             "# 8 \"<stdin>\"\n"
1385             "// test\n"
1386             "// test again\n"
1387             "x = 10\n"
1388     );
1389 
1390     QTest::newRow("case 4") << _(
1391             "#define FOO int x;\n"
1392             "\n"
1393             "   /* comment\n"
1394             "      comment\n"
1395             "      comment\n"
1396             "      comment */\n"
1397             "FOO\n"
1398             "/*  \n"
1399             "*/\n"
1400             "x = 10\n"
1401         ) << _(
1402             "# 1 \"<stdin>\"\n"
1403             "\n"
1404             "\n"
1405             "\n"
1406             "\n"
1407             "\n"
1408             "\n"
1409             "# expansion begin 79,3 ~3\n"
1410             "int x;\n"
1411             "# expansion end\n"
1412             "# 10 \"<stdin>\"\n"
1413             "x = 10\n"
1414         ) << _(
1415             "# 1 \"<stdin>\"\n"
1416             "\n"
1417             "\n"
1418             "   /* comment\n"
1419             "      comment\n"
1420             "      comment\n"
1421             "      comment */\n"
1422             "# expansion begin 79,3 ~3\n"
1423             "int x;\n"
1424             "# expansion end\n"
1425             "# 8 \"<stdin>\"\n"
1426             "/*  \n"
1427             "*/\n"
1428             "x = 10\n"
1429     );
1430 
1431     QTest::newRow("case 5") << _(
1432             "#define FOO(x, y) { (void)x; (void)y; }\n"
1433             "\n"
1434             "void foo() {\n"
1435             "   FOO(10,\n"
1436             "       //comment\n"
1437             "       12)\n"
1438             "}\n"
1439         ) << _(
1440             "# 1 \"<stdin>\"\n"
1441             "\n"
1442             "\n"
1443             "void foo() {\n"
1444             "# expansion begin 57,3 ~4 4:7 ~4 6:7 ~2\n"
1445             "{ (void)10; (void)12; }\n"
1446             "# expansion end\n"
1447             "# 7 \"<stdin>\"\n"
1448             "}\n"
1449         ) << _(
1450             "# 1 \"<stdin>\"\n"
1451             "\n"
1452             "\n"
1453             "void foo() {\n"
1454             "# expansion begin 57,3 ~4 4:7 ~5 6:7 ~2\n"
1455             "{ (void)10; (void)/*comment*/ 12; }\n"
1456             "# expansion end\n"
1457             "# 7 \"<stdin>\"\n"
1458             "}\n"
1459     );
1460 
1461     QTest::newRow("case 6") << _(
1462             "#define FOO(x, y) { (void)x; (void)y; }\n"
1463             "\n"
1464             "void foo() {\n"
1465             "   FOO(10,\n"
1466             "       //tricky*/comment\n"
1467             "       12)\n"
1468             "}\n"
1469         ) << _(
1470             "# 1 \"<stdin>\"\n"
1471             "\n"
1472             "\n"
1473             "void foo() {\n"
1474             "# expansion begin 57,3 ~4 4:7 ~4 6:7 ~2\n"
1475             "{ (void)10; (void)12; }\n"
1476             "# expansion end\n"
1477             "# 7 \"<stdin>\"\n"
1478             "}\n"
1479         ) << _(
1480             "# 1 \"<stdin>\"\n"
1481             "\n"
1482             "\n"
1483             "void foo() {\n"
1484             "# expansion begin 57,3 ~4 4:7 ~5 6:7 ~2\n"
1485             "{ (void)10; (void)/*tricky*|comment*/ 12; }\n"
1486             "# expansion end\n"
1487             "# 7 \"<stdin>\"\n"
1488             "}\n"
1489     );
1490 
1491     QTest::newRow("case 7") << _(
1492             "#define FOO 0 //comment\n"
1493             "#define BAR (1 == FOO)\n"
1494             "void foo() {\n"
1495             "    if (BAR) {}\n"
1496             "}\n"
1497         ) << _(
1498             "# 1 \"<stdin>\"\n"
1499             "\n"
1500             "\n"
1501             "void foo() {\n"
1502             "    if (\n"
1503             "# expansion begin 68,3 ~5\n"
1504             "(1 == 0)\n"
1505             "# expansion end\n"
1506             "# 4 \"<stdin>\"\n"
1507             "           ) {}\n"
1508             "}\n"
1509         ) << _(
1510             "# 1 \"<stdin>\"\n"
1511             "              //comment\n"
1512             "\n"
1513             "void foo() {\n"
1514             "    if (\n"
1515             "# expansion begin 68,3 ~5\n"
1516             "(1 == 0)\n"
1517             "# expansion end\n"
1518             "# 4 \"<stdin>\"\n"
1519             "           ) {}\n"
1520             "}\n"
1521     );
1522     QTest::newRow("case 8") << _(
1523             "#define FOO /* comment */ 0\n"
1524             "FOO\n"
1525         ) << _(
1526             "# 1 \"<stdin>\"\n"
1527             "\n"
1528             "# expansion begin 28,3 ~1\n"
1529             "0\n"
1530             "# expansion end\n"
1531             "# 3 \"<stdin>\"\n"
1532         ) << _(
1533             "# 1 \"<stdin>\"\n"
1534             "            /* comment */\n"
1535             "# expansion begin 28,3 ~1\n"
1536             "0\n"
1537             "# expansion end\n"
1538             "# 3 \"<stdin>\"\n"
1539     );
1540 
1541     QTest::newRow("case 9") << _(
1542             "#define FOO /* comment1 */ /* comment2 */ 0\n"
1543             "FOO\n"
1544         ) << _(
1545             "# 1 \"<stdin>\"\n"
1546             "\n"
1547             "# expansion begin 44,3 ~1\n"
1548             "0\n"
1549             "# expansion end\n"
1550             "# 3 \"<stdin>\"\n"
1551         ) << _(
1552             "# 1 \"<stdin>\"\n"
1553             "            /* comment1 */ /* comment2 */\n"
1554             "# expansion begin 44,3 ~1\n"
1555             "0\n"
1556             "# expansion end\n"
1557             "# 3 \"<stdin>\"\n"
1558     );
1559 
1560     QTest::newRow("case 10") << _(
1561             "#define FOO /* comment1 */   /* comment2 */ 0 /* comment3\n"
1562             "comment4 */\n"
1563             "FOO\n"
1564         ) << _(
1565             "# 1 \"<stdin>\"\n"
1566             "\n"
1567             "\n"
1568             "# expansion begin 70,3 ~1\n"
1569             "0\n"
1570             "# expansion end\n"
1571             "# 4 \"<stdin>\"\n"
1572         ) << _(
1573             "# 1 \"<stdin>\"\n"
1574             "            /* comment1 */ /* comment2 */ /* comment3\n"
1575             "comment4 */\n"
1576             "# expansion begin 70,3 ~1\n"
1577             "0\n"
1578             "# expansion end\n"
1579             "# 4 \"<stdin>\"\n"
1580     );
1581 
1582     QTest::newRow("case 11") << _(
1583             "#include <foo.h> // comment\n"
1584         ) << _(
1585             "# 1 \"<stdin>\"\n"
1586             "\n"
1587         ) << _(
1588             "# 1 \"<stdin>\"\n"
1589             "                 // comment\n"
1590     );
1591 
1592     QTest::newRow("joined") << _(
1593             "// comment \\\n"
1594             "\n"
1595             "int foo = 4;"
1596         ) << _(
1597             "# 1 \"<stdin>\"\n"
1598             "\n"
1599             "\n"
1600             "int foo = 4;"
1601         ) << _(
1602             "# 1 \"<stdin>\"\n"
1603             "// comment \\\n"
1604             "\n"
1605             "int foo = 4;"
1606     );
1607 
1608     QTest::newRow("joined_unterminated") << _(
1609             "// comment \\\n"
1610             "\n"
1611             "\n"
1612             "\n"
1613             "\n"
1614             "\n"
1615             "\n"
1616             "\n"
1617             "\n"
1618             "\n"
1619             "\n"
1620             "int foo = 4;"
1621         ) << _(
1622             "# 1 \"<stdin>\"\n"
1623             "# 12 \"<stdin>\"\n"
1624             "int foo = 4;"
1625         ) << _(
1626             "# 1 \"<stdin>\"\n"
1627             "// comment \\\n"
1628             "\n"
1629             "# 12 \"<stdin>\"\n"
1630             "int foo = 4;"
1631     );
1632 
1633     QTest::newRow("inside_function_like_macro") << _(
1634             "#define /* comment */ ASSIGN1(VAR, VALUE) VAR = VALUE\n"
1635             "#define ASSIGN2(/* comment */ VAR, VALUE) VAR = VALUE\n"
1636             "#define ASSIGN3(VAR /* comment */, VALUE) VAR = VALUE\n"
1637             "#define ASSIGN4(VAR, /* comment */ VALUE) VAR = VALUE\n"
1638             "#define ASSIGN5(VAR, VALUE /* comment */) VAR = VALUE\n"
1639             "#define ASSIGN6(VAR, VALUE) /* comment */ VAR = VALUE\n"
1640             "#define ASSIGN7(VAR, ... /* comment */) VAR\n"
1641             "void func()\n"
1642             "{\n"
1643             "    int i;\n"
1644             "    ASSIGN1(i, 3);\n"
1645             "    ASSIGN2(i, 3);\n"
1646             "    ASSIGN3(i, 3);\n"
1647             "    ASSIGN4(i, 3);\n"
1648             "    ASSIGN5(i, 3);\n"
1649             "    ASSIGN6(i, 3);\n"
1650             "    ASSIGN7(i, 3);\n"
1651             "}\n"
1652         ) << _(
1653             "# 1 \"<stdin>\"\n"
1654             "\n"
1655             "\n"
1656             "\n"
1657             "\n"
1658             "\n"
1659             "\n"
1660             "\n"
1661             "void func()\n"
1662             "{\n"
1663             "    int i;\n"
1664             "# expansion begin 397,7 11:12 ~1 11:15\n"
1665             "i = 3\n"
1666             "# expansion end\n"
1667             "# 11 \"<stdin>\"\n"
1668             "                 ;\n"
1669             "# expansion begin 416,7 12:12 ~1 12:15\n"
1670             "i = 3\n"
1671             "# expansion end\n"
1672             "# 12 \"<stdin>\"\n"
1673             "                 ;\n"
1674             "# expansion begin 435,7 13:12 ~1 13:15\n"
1675             "i = 3\n"
1676             "# expansion end\n"
1677             "# 13 \"<stdin>\"\n"
1678             "                 ;\n"
1679             "# expansion begin 454,7 14:12 ~1 14:15\n"
1680             "i = 3\n"
1681             "# expansion end\n"
1682             "# 14 \"<stdin>\"\n"
1683             "                 ;\n"
1684             "# expansion begin 473,7 15:12 ~1 15:15\n"
1685             "i = 3\n"
1686             "# expansion end\n"
1687             "# 15 \"<stdin>\"\n"
1688             "                 ;\n"
1689             "# expansion begin 492,7 16:12 ~1 16:15\n"
1690             "i = 3\n"
1691             "# expansion end\n"
1692             "# 16 \"<stdin>\"\n"
1693             "                 ;\n"
1694             "# expansion begin 511,7 17:12\n"
1695             "i\n"
1696             "# expansion end\n"
1697             "# 17 \"<stdin>\"\n"
1698             "                 ;\n"
1699             "}\n"
1700         ) << _(
1701             "# 1 \"<stdin>\"\n"
1702             "        /* comment */\n"
1703             "                /* comment */\n"
1704             "                    /* comment */\n"
1705             "                     /* comment */\n"
1706             "                           /* comment */\n"
1707             "                            /* comment */\n"
1708             "                         /* comment */\n"
1709             "void func()\n"
1710             "{\n"
1711             "    int i;\n"
1712             "# expansion begin 397,7 11:12 ~1 11:15\n"
1713             "i = 3\n"
1714             "# expansion end\n"
1715             "# 11 \"<stdin>\"\n"
1716             "                 ;\n"
1717             "# expansion begin 416,7 12:12 ~1 12:15\n"
1718             "i = 3\n"
1719             "# expansion end\n"
1720             "# 12 \"<stdin>\"\n"
1721             "                 ;\n"
1722             "# expansion begin 435,7 13:12 ~1 13:15\n"
1723             "i = 3\n"
1724             "# expansion end\n"
1725             "# 13 \"<stdin>\"\n"
1726             "                 ;\n"
1727             "# expansion begin 454,7 14:12 ~1 14:15\n"
1728             "i = 3\n"
1729             "# expansion end\n"
1730             "# 14 \"<stdin>\"\n"
1731             "                 ;\n"
1732             "# expansion begin 473,7 15:12 ~1 15:15\n"
1733             "i = 3\n"
1734             "# expansion end\n"
1735             "# 15 \"<stdin>\"\n"
1736             "                 ;\n"
1737             "# expansion begin 492,7 16:12 ~1 16:15\n"
1738             "i = 3\n"
1739             "# expansion end\n"
1740             "# 16 \"<stdin>\"\n"
1741             "                 ;\n"
1742             "# expansion begin 511,7 17:12\n"
1743             "i\n"
1744             "# expansion end\n"
1745             "# 17 \"<stdin>\"\n"
1746             "                 ;\n"
1747             "}\n"
1748     );
1749 }
1750 
comments_before_args()1751 void tst_Preprocessor::comments_before_args()
1752 {
1753     Client *client = 0; // no client.
1754     Environment env;
1755 
1756     Preprocessor preprocess(client, &env);
1757     preprocess.setKeepComments(true);
1758     QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"),
1759                                                 "#define foo(a,b) int a = b;\n"
1760                                                 "foo/*C comment*/(a,1)\n"
1761                                                 "foo/**Doxygen comment*/(b,2)\n"
1762                                                 "foo//C++ comment\n"
1763                                                 "(c,3)\n"
1764                                                 "foo///Doxygen C++ comment\n"
1765                                                 "(d,4)\n"
1766                                                 "foo/*multiple*///comments\n"
1767                                                 "/**as well*/(e,5)\n",
1768                                              true, false);
1769 
1770 //    DUMP_OUTPUT(preprocessed);
1771     QByteArray expected =
1772             "\n"
1773             "   /*C comment*/int a = 1;\n"
1774             "   /**Doxygen comment*/int b = 2;\n"
1775             "   //C++ comment\n"
1776             "int\n"
1777             "c = 3;\n"
1778             "   ///Doxygen C++ comment\n"
1779             "int\n"
1780             "d = 4;\n"
1781             "   /*multiple*/               //comments\n"
1782             "/**as well*/ int\n"
1783             "e = 5;\n";
1784     QVERIFY(compare(preprocessed, expected));
1785 }
1786 
multiline_strings()1787 void tst_Preprocessor::multiline_strings()
1788 {
1789     compare_input_output();
1790 }
1791 
multiline_strings_data()1792 void tst_Preprocessor::multiline_strings_data()
1793 {
1794     QTest::addColumn<QByteArray>("input");
1795     QTest::addColumn<QByteArray>("output");
1796 
1797     QTest::newRow("case 1") << _(
1798             "const char *s = \"abc\\\n"
1799             "xyz\";\n"
1800         ) << _(
1801             "# 1 \"<stdin>\"\n"
1802             "const char *s = \"abc\\\n"
1803             "xyz\";\n"
1804     );
1805 }
1806 
skip_unknown_directives()1807 void tst_Preprocessor::skip_unknown_directives()
1808 {
1809     compare_input_output();
1810 }
1811 
skip_unknown_directives_data()1812 void tst_Preprocessor::skip_unknown_directives_data()
1813 {
1814     QTest::addColumn<QByteArray>("input");
1815     QTest::addColumn<QByteArray>("output");
1816 
1817     // We should skip "weird" things when preprocessing. Particularly useful when we preprocess
1818     // a particular expression from a document which has already been processed.
1819 
1820     QTest::newRow("case 1") << _(
1821             "# foo\n"
1822             "# 10 \"file.cpp\"\n"
1823             "# ()\n"
1824             "#\n"
1825         ) << _(
1826             "# 1 \"<stdin>\"\n"
1827             "\n"
1828             "\n"
1829             "\n"
1830             "\n"
1831     );
1832 }
1833 
include_guard()1834 void tst_Preprocessor::include_guard()
1835 {
1836     QFETCH(QString, includeGuard);
1837     QFETCH(QString, input);
1838 
1839     QByteArray output;
1840     Environment env;
1841     MockClient client(&env, &output);
1842     Preprocessor preprocess(&client, &env);
1843     preprocess.setKeepComments(true);
1844     /*QByteArray prep =*/ preprocess.run(QLatin1String("<test-case>"), input);
1845     QCOMPARE(QString::fromUtf8(client.includeGuard()), includeGuard);
1846 }
1847 
include_guard_data()1848 void tst_Preprocessor::include_guard_data()
1849 {
1850     QTest::addColumn<QString>("includeGuard");
1851     QTest::addColumn<QString>("input");
1852 
1853     QTest::newRow("basic-test") << "BASIC_TEST"
1854                                 << "#ifndef BASIC_TEST\n"
1855                                    "#define BASIC_TEST\n"
1856                                    "\n"
1857                                    "#endif // BASIC_TEST\n";
1858     QTest::newRow("comments-1") << "GUARD"
1859                                 << "/* some\n"
1860                                    " * copyright\n"
1861                                    " * header.\n"
1862                                    " */\n"
1863                                    "#ifndef GUARD\n"
1864                                    "#define GUARD\n"
1865                                    "\n"
1866                                    "#endif // GUARD\n";
1867     QTest::newRow("comments-2") << "GUARD"
1868                                 << "#ifndef GUARD\n"
1869                                    "#define GUARD\n"
1870                                    "\n"
1871                                    "#endif // GUARD\n"
1872                                    "/* some\n"
1873                                    " * trailing\n"
1874                                    " * comments.\n"
1875                                    " */\n"
1876                                    ;
1877     QTest::newRow("nested-ifdef") << "GUARD"
1878                                   << "#ifndef GUARD\n"
1879                                      "#define GUARD\n"
1880                                      "#ifndef NOT_GUARD\n"
1881                                      "#define NOT_GUARD\n"
1882                                      "#endif // NOT_GUARD\n"
1883                                      "\n"
1884                                      "#endif // GUARD\n"
1885                                      ;
1886     QTest::newRow("leading-tokens") << ""
1887                                     << "int i;\n"
1888                                        "#ifndef GUARD\n"
1889                                        "#define GUARD\n"
1890                                        "\n"
1891                                        "#endif // GUARD\n"
1892                                        ;
1893     QTest::newRow("trailing-tokens") << ""
1894                                      << "#ifndef GUARD\n"
1895                                         "#define GUARD\n"
1896                                         "\n"
1897                                         "#endif // GUARD\n"
1898                                         "int i;\n"
1899                                         ;
1900     QTest::newRow("surprising-but-correct") << "GUARD"
1901                                             << "#ifndef GUARD\n"
1902                                                "int i;\n"
1903                                                "\n"
1904                                                "#define GUARD\n"
1905                                                "#endif // GUARD\n"
1906                                                ;
1907     QTest::newRow("incomplete-1") << ""
1908                                   << "#ifndef GUARD\n"
1909                                      ;
1910     QTest::newRow("incomplete-2") << "GUARD"
1911                                   << "#ifndef GUARD\n"
1912                                      "#define GUARD\n"
1913                                      ;
1914 }
1915 
empty_trailing_lines()1916 void tst_Preprocessor::empty_trailing_lines()
1917 {
1918     compare_input_output();
1919 }
1920 
empty_trailing_lines_data()1921 void tst_Preprocessor::empty_trailing_lines_data()
1922 {
1923     // Test if the number of lines at the end of a file is correct. This is important to make the
1924     // EOF token for the end up at the correct line.
1925 
1926     QTest::addColumn<QByteArray>("input");
1927     QTest::addColumn<QByteArray>("output");
1928 
1929     const QByteArray original =
1930             "\n"
1931             "\n"
1932             "\n"
1933             "\n"
1934             "\n"
1935             "\n"
1936             "\n"
1937             "\n";
1938 
1939     QTest::newRow("9 empty lines")
1940             << original
1941             << _("# 1 \"<stdin>\"\n") + original;
1942 
1943     QTest::newRow("11 empty lines") << _(
1944             "\n"
1945             "\n"
1946             "\n"
1947             "\n"
1948             "\n"
1949             "\n"
1950             "\n"
1951             "\n"
1952             "\n"
1953             "\n"
1954         ) << _(
1955             "# 1 \"<stdin>\"\n"
1956             "# 11 \"<stdin>\"\n"
1957     );
1958 
1959     QTest::newRow("1 include") << _(
1960             "#include <something>\n"
1961         ) << _(
1962             "# 1 \"<stdin>\"\n"
1963             "\n"
1964     );
1965 
1966     QTest::newRow("1 empty line with 1 include") << _(
1967             "#include <something>\n"
1968             "\n"
1969         ) << _(
1970             "# 1 \"<stdin>\"\n"
1971             "\n"
1972             "\n"
1973     );
1974 }
1975 
undef()1976 void tst_Preprocessor::undef()
1977 {
1978     Environment env;
1979     QByteArray output;
1980     MockClient client(&env, &output);
1981     Preprocessor preprocess(&client, &env);
1982     QByteArray input =
1983             "#define FOO\n"
1984             "#define FOO2\n"
1985             "#undef FOO\n"
1986             "#undef BAR\n";
1987     preprocess.run(QLatin1String("<stdin>"), input);
1988     QCOMPARE(env.macroCount(), 4U);
1989     Macro *macro = env.macroAt(0);
1990     QCOMPARE(macro->name(), QByteArray("FOO"));
1991     QCOMPARE(macro->bytesOffset(), 8U);
1992     QCOMPARE(macro->line(), 1);
1993     QVERIFY(!macro->isHidden());
1994     macro = env.macroAt(1);
1995     QCOMPARE(macro->name(), QByteArray("FOO2"));
1996     QCOMPARE(macro->bytesOffset(), 20U);
1997     QCOMPARE(macro->line(), 2);
1998     QVERIFY(!macro->isHidden());
1999     macro = env.macroAt(2);
2000     QCOMPARE(macro->name(), QByteArray("FOO"));
2001     QCOMPARE(macro->bytesOffset(), 32U);
2002     QCOMPARE(macro->line(), 3);
2003     QVERIFY(macro->isHidden());
2004     macro = env.macroAt(3);
2005     QCOMPARE(macro->name(), QByteArray("BAR"));
2006     QCOMPARE(macro->bytesOffset(), 43U);
2007     QCOMPARE(macro->line(), 4);
2008     QVERIFY(macro->isHidden());
2009     QList<QByteArray> macros = client.definedMacros();
2010     QVERIFY(macros.contains("FOO"));
2011     QVERIFY(macros.contains("FOO2"));
2012     QCOMPARE(client.macroUsesLine()["FOO"], (QList<int>() << 3));
2013     QVERIFY(client.macroUsesLine()["BAR"].isEmpty());
2014 }
2015 
concat()2016 void tst_Preprocessor::concat()
2017 {
2018     Environment env;
2019     Preprocessor preprocess(0, &env);
2020     QByteArray input =
2021             "#define concat(x,y) x ## y\n"
2022             "#define xconcat(x, y) concat(x, y)\n"
2023             "#define FOO 42\n"
2024             "int var1 = concat(0x, FOO);\n"
2025             "int var2 = xconcat(0x, FOO);\n";
2026     QByteArray prep = preprocess.run(QLatin1String("<stdin>"), input);
2027     const QByteArray output = _(
2028         "# 1 \"<stdin>\"\n"
2029         "\n"
2030         "\n"
2031         "\n"
2032         "int var1 =\n"
2033         "# expansion begin 88,6 ~1\n"
2034         "0xFOO\n"
2035         "# expansion end\n"
2036         "# 4 \"<stdin>\"\n"
2037         "                          ;\n"
2038         "int var2 =\n"
2039         "# expansion begin 116,7 ~1\n"
2040         "0x42\n"
2041         "# expansion end\n"
2042         "# 5 \"<stdin>\"\n"
2043         "                           ;\n"
2044     );
2045     QVERIFY(compare(prep, output));
2046 }
2047 
nested_arguments_expansion()2048 void tst_Preprocessor::nested_arguments_expansion()
2049 {
2050     Environment env;
2051     Preprocessor preprocess(nullptr, &env);
2052     QByteArray input = "#define LPL_CAT_IMPL(x, y) x##y\n"
2053                        "#define LPL_CAT(x, y) LPL_CAT_IMPL(x, y)\n"
2054                        "#define LPL_COMPL_1 0\n"
2055                        "#define LPL_COMPL_0 1\n"
2056                        "#define LPL_COMPL(x) LPL_CAT(LPL_COMPL_, x)\n"
2057                        "#define LPL_DEC_1 0\n"
2058                        "#define LPL_DEC_2 1\n"
2059                        "#define LPL_DEC(x) LPL_CAT(LPL_DEC_, x)\n"
2060                        "#define LPL_CHECK_IMPL(unused, n, ...) n\n"
2061                        "#define LPL_CHECK(expressionToTest) LPL_CHECK_IMPL(expressionToTest, 0, 0)\n"
2062                        "#define LPL_IS_0_0 LPL_PROBE\n"
2063                        "#define LPL_IS_0(x) LPL_CHECK(LPL_CAT(LPL_IS_0_, x))\n"
2064                        "#define LPL_IS_NOT_0(x) LPL_COMPL(LPL_IS_0(x))\n"
2065                        "#define EMPTY()\n"
2066                        "#define LPL_EXPAND(id) id\n"
2067                        "#define LPL_DEFER(id) id EMPTY()\n"
2068                        "#define LPL_DEFER_TWICE(id) id EMPTY EMPTY()()\n"
2069                        "#define LPL_EVAL1(id) LPL_EXPAND(LPL_EXPAND(id))\n"
2070                        "#define LPL_EVAL2(id) LPL_EVAL1(LPL_EVAL1(id))\n"
2071                        "#define LPL_EVAL3(id) LPL_EVAL2(LPL_EVAL2(id))\n"
2072                        "#define LPL_EVAL4(id) LPL_EVAL3(LPL_EVAL3(id))\n"
2073                        "#define LPL_EVAL5(id) LPL_EVAL4(LPL_EVAL4(id))\n"
2074                        "#define LPL_EVAL6(id) LPL_EVAL5(LPL_EVAL5(id))\n"
2075                        "#define LPL_EVAL(id) LPL_EVAL6(LPL_EVAL6(id))\n"
2076                        "#define LPL_IF_0(t, f) f\n"
2077                        "#define LPL_IF_1(t, f) t\n"
2078                        "#define LPL_IF(c) LPL_CAT(LPL_IF_, LPL_IS_NOT_0(c))\n"
2079                        "#define LPL_WHEN(c) LPL_IF(c)(LPL_EXPAND, LPL_EAT)\n"
2080                        "#define LPL_WHILE_IMPL(x, predicat, macroToApply) \\\n"
2081                        "  LPL_WHEN(predicat(x)) \\\n"
2082                        "  (x LPL_DEFER_TWICE(LPL_WHILE_IMPL_I)()(macroToApply(x), predicat, \\\n"
2083                        "                                         macroToApply))\n"
2084                        "#define LPL_WHILE_IMPL_I() LPL_WHILE_IMPL\n"
2085                        "#define LPL_WHILE(x, predicat, macroToApply) \\\n"
2086                        "  LPL_EVAL(LPL_WHILE_IMPL(x, predicat, macroToApply))\n"
2087                        "#define LPL_AND_IMPL_TREAT_PARENTHESIS_1(x, ...) 1\n"
2088                        "#define LPL_AND_IMPL_TREAT_PARENTHESIS_0(x, ...) \\\n"
2089                        "  LPL_IF(LPL_IS_0(x))(0, LPL_DEFER_TWICE(LPL_AND_IMPL_I)()(__VA_ARGS__))\n"
2090                        "#define LPL_AND_IMPL(x, ...) \\\n"
2091                        "  LPL_CAT(LPL_AND_IMPL_TREAT_PARENTHESIS_, LPL_IS_PARENTHESIS(x)) \\\n"
2092                        "  (x, __VA_ARGS__)\n"
2093                        "#define LPL_AND_IMPL_I() LPL_AND_IMPL\n"
2094                        "#define LPL_AND(...) LPL_EVAL(LPL_AND_IMPL(__VA_ARGS__, (), 0))\n"
2095                        "LPL_WHILE(2, LPL_IS_NOT_0, LPL_DEC);";
2096 
2097     QByteArray prep = preprocess.run(QLatin1String("<stdin>"), input);
2098     qDebug() << prep;
2099     const QByteArray output = "# 1 \"<stdin>\"\n";
2100     // Check that it does not crash.
2101     QVERIFY(prep.contains(output));
2102 }
2103 
preprocessorSymbolsAsMacroArguments()2104 void tst_Preprocessor::preprocessorSymbolsAsMacroArguments()
2105 {
2106     Environment env;
2107     Preprocessor preprocess(nullptr, &env);
2108     const QByteArray input =
2109             "#define IFGEN(if, endif) if (1 == 0) endif\n"
2110             "int main()\n"
2111             "{\n"
2112             "IFGEN(#if, #endif)\n"
2113             "return 0;\n"
2114             "}\n";
2115     QVERIFY(preprocess.run(QLatin1String("<stdin>"), input).startsWith("# 1 \"<stdin>\"\n"));
2116 }
2117 
excessive_nesting()2118 void tst_Preprocessor::excessive_nesting()
2119 {
2120     Environment env;
2121     Preprocessor preprocess(0, &env);
2122     QByteArray input;
2123     const QByteArray output =
2124         "# 1 \"<stdin>\"\n"
2125         "# 2001 \"<stdin>\"\n";
2126     for (int i = 0; i < 1000; ++i)
2127         input += "#if FOO\n";
2128     for (int i = 0; i < 1000; ++i)
2129         input += "#endif\n";
2130     QByteArray prep = preprocess.run(QLatin1String("<stdin>"), input);
2131     // Output cannot be precisely determined, but it shouldn't crash.
2132     QCOMPARE(prep, output);
2133 }
2134 
multi_byte_code_point_in_expansion()2135 void tst_Preprocessor::multi_byte_code_point_in_expansion()
2136 {
2137     Environment env;
2138     Preprocessor preprocess(0, &env);
2139     const QByteArray input =
2140         "#define FOO(x) x\n"
2141         "FOO(arg" UC_U00FC "\n)\n";
2142 
2143     const QByteArray actual = preprocess.run(QLatin1String("<stdin>"), input);
2144 
2145     const QByteArray expected =
2146         "# 1 \"<stdin>\"\n"
2147         "\n"
2148         "# expansion begin 17,3 2:4\n"
2149         "arg" UC_U00FC "\n"
2150         "# expansion end\n"
2151         "# 4 \"<stdin>\"\n";
2152     QCOMPARE(actual, expected);
2153 }
2154 
compare_input_output(bool keepComments)2155 void tst_Preprocessor::compare_input_output(bool keepComments)
2156 {
2157     QFETCH(QByteArray, input);
2158     QFETCH(QByteArray, output);
2159 
2160     Environment env;
2161     Preprocessor preprocess(0, &env);
2162     preprocess.setKeepComments(keepComments);
2163     QByteArray prep = preprocess.run(QLatin1String("<stdin>"), input);
2164     QVERIFY(compare(prep, output));
2165 }
2166 
trigraph()2167 void tst_Preprocessor::trigraph()
2168 {
2169     Environment env;
2170     Preprocessor preprocess(0, &env);
2171 
2172     // We cannot use actual trigraphs in strings, they would be replaced by the preprocessor when
2173     // compiling the test, so we use strings with 'j' character instead of '?', and perform a
2174     // replacement at runtime.
2175 
2176     // Trigraphs in source code are replaced
2177     QByteArray prep = preprocess.run(QLatin1String("<stdin>"),
2178                                      QByteArray("jj(  jj)  jj<  jj>  jj=  jj=jj=  jj'  jj'=  jj!  jj!=  jj-  jj-=").replace('j', '?'),
2179                                      true, false);
2180     QCOMPARE(prep.constData(), "[  ]  {  }  #  ##  ^  ^=  |  |=  ~  ~=");
2181 
2182     // Trigraphs that appear after macro expansion are not replaced
2183     prep = preprocess.run(QLatin1String("<stdin>"),
2184                           "#define TRIGRAPH(x...) ? ## x ## ? ## x ## =\n"
2185                           "TRIGRAPH()",
2186                           true, false);
2187     QCOMPARE(prep.constData(), QByteArray("\njj=").replace('j', '?').data());
2188 }
2189 
2190 QTEST_APPLESS_MAIN(tst_Preprocessor)
2191 
2192 #include "tst_preprocessor.moc"
2193