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 ¯o)
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 ¯o)
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 ¯o,
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 ¤tFileName,
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 ¤tFileName) 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 ¯oName)
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, ®)) {}\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