diff options
Diffstat (limited to 'tests/auto/cplusplus')
32 files changed, 1505 insertions, 19 deletions
diff --git a/tests/auto/cplusplus/cxx11/data/staticAssert.1.cpp b/tests/auto/cplusplus/cxx11/data/staticAssert.1.cpp index bb4a1507f9..b91abac7bf 100644 --- a/tests/auto/cplusplus/cxx11/data/staticAssert.1.cpp +++ b/tests/auto/cplusplus/cxx11/data/staticAssert.1.cpp @@ -6,3 +6,4 @@ struct S { } }; +static_assert(sizeof(char) == 1, "One more"); diff --git a/tests/auto/cplusplus/preprocessor/data/empty-macro.2.cpp b/tests/auto/cplusplus/preprocessor/data/empty-macro.2.cpp new file mode 100644 index 0000000000..389eef523c --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/empty-macro.2.cpp @@ -0,0 +1,13 @@ +#define Q_DECL_EQ_DELETE +#define Q_DISABLE_COPY(Class) \ + Class(const Class &) Q_DECL_EQ_DELETE;\ + Class &operator=(const Class &) Q_DECL_EQ_DELETE; + +class Test { +private: + Q_DISABLE_COPY(Test) + +public: + Test(); +}; + diff --git a/tests/auto/cplusplus/preprocessor/data/empty-macro.2.out.cpp b/tests/auto/cplusplus/preprocessor/data/empty-macro.2.out.cpp new file mode 100644 index 0000000000..03703e9bbd --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/empty-macro.2.out.cpp @@ -0,0 +1,15 @@ +# 1 "data/empty-macro.2.cpp" + + + + + +class Test { +private: +# expansion begin 182,14 8:19 ~2 8:19 ~3 8:19 ~5 8:19 ~3 +Test(const Test &); Test &operator=(const Test &); +# expansion end +# 10 "data/empty-macro.2.cpp" +public: + Test(); +}; diff --git a/tests/auto/cplusplus/preprocessor/data/empty-macro.cpp b/tests/auto/cplusplus/preprocessor/data/empty-macro.cpp new file mode 100644 index 0000000000..b86fccb7b9 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/empty-macro.cpp @@ -0,0 +1,13 @@ +#define EMPTY_MACRO + +class EMPTY_MACRO Foo { +}; + +class EMPTY_MACRO Foo2 { +}; + +class EMPTY_MACRO Foo3 { +}; + +class EMPTY_MACRO Foo3 { +}; diff --git a/tests/auto/cplusplus/preprocessor/data/empty-macro.out.cpp b/tests/auto/cplusplus/preprocessor/data/empty-macro.out.cpp new file mode 100644 index 0000000000..405cbbacf9 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/empty-macro.out.cpp @@ -0,0 +1,14 @@ +# 1 "data/empty-macro.cpp" + + +class Foo { +}; + +class Foo2 { +}; + +class Foo3 { +}; + +class Foo3 { +}; diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.1.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.1.cpp new file mode 100644 index 0000000000..6091e56301 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.1.cpp @@ -0,0 +1,5 @@ +#define TEST test + +TEST TEST; + +void TEST(); diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.1.out.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.1.out.cpp new file mode 100644 index 0000000000..a401d55dbf --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.1.out.cpp @@ -0,0 +1,18 @@ +# 1 "data/identifier-expansion.1.cpp" + + +# expansion begin 19,4 ~1 +test +# expansion end +# expansion begin 24,4 ~1 +test +# expansion end +# 3 "data/identifier-expansion.1.cpp" + ; + +void +# expansion begin 36,4 ~1 +test +# expansion end +# 5 "data/identifier-expansion.1.cpp" + (); diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.2.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.2.cpp new file mode 100644 index 0000000000..5a4c14f2a8 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.2.cpp @@ -0,0 +1,6 @@ +#define TEST test +#define ANOTHER_TEST TEST + +ANOTHER_TEST TEST; + +void ANOTHER_TEST(); diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.2.out.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.2.out.cpp new file mode 100644 index 0000000000..8d4c833a0d --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.2.out.cpp @@ -0,0 +1,19 @@ +# 1 "data/identifier-expansion.2.cpp" + + + +# expansion begin 45,12 ~1 +test +# expansion end +# expansion begin 58,4 ~1 +test +# expansion end +# 4 "data/identifier-expansion.2.cpp" + ; + +void +# expansion begin 70,12 ~1 +test +# expansion end +# 6 "data/identifier-expansion.2.cpp" + (); diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.3.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.3.cpp new file mode 100644 index 0000000000..4f0e0bd81c --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.3.cpp @@ -0,0 +1,21 @@ +#define FOR_EACH_INSTR(V) \ + V(ADD) \ + V(SUB) + +#define OTHER_FOR_EACH(V) \ + V(DIV) \ + V(MUL) + +#define DECLARE_INSTR(op) #op, +#define DECLARE_OP_INSTR(op) op_##op, + +enum op_code { + FOR_EACH_INSTR(DECLARE_OP_INSTR) + OTHER_FOR_EACH(DECLARE_OP_INSTR) +}; + + +static const char *names[] = { +FOR_EACH_INSTR(DECLARE_INSTR) +OTHER_FOR_EACH(DECLARE_INSTR) +}; diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.3.out.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.3.out.cpp new file mode 100644 index 0000000000..bd8b2bdbf2 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.3.out.cpp @@ -0,0 +1,22 @@ +# 1 "data/identifier-expansion.3.cpp" +# 12 "data/identifier-expansion.3.cpp" +enum op_code { +# expansion begin 195,14 ~4 +op_ADD, op_SUB, +# expansion end +# expansion begin 232,14 ~4 +op_DIV, op_MUL, +# expansion end +# 15 "data/identifier-expansion.3.cpp" +}; + + +static const char *names[] = { +# expansion begin 301,14 ~4 +"ADD", "SUB", +# expansion end +# expansion begin 331,14 ~4 +"DIV", "MUL", +# expansion end +# 21 "data/identifier-expansion.3.cpp" +}; diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.4.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.4.cpp new file mode 100644 index 0000000000..9c9c356c3d --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.4.cpp @@ -0,0 +1,8 @@ +#define foobar(a) a +#define food foobar + +void baz() +{ + int aaa; + food(aaa); +} diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.4.out.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.4.out.cpp new file mode 100644 index 0000000000..759eae4418 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.4.out.cpp @@ -0,0 +1,13 @@ +# 1 "data/identifier-expansion.4.cpp" + + + +void baz() +{ + int aaa; +# expansion begin 88,4 7:9 +aaa +# expansion end +# 7 "data/identifier-expansion.4.cpp" + ; +} diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.5.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.5.cpp new file mode 100644 index 0000000000..eca3cb1dda --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.5.cpp @@ -0,0 +1,8 @@ +#define FOOBAR + +#ifdef FOO + +class FOOBAR Zoo { +}; + +#endif diff --git a/tests/auto/cplusplus/preprocessor/data/identifier-expansion.5.out.cpp b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.5.out.cpp new file mode 100644 index 0000000000..3d328fe251 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/identifier-expansion.5.out.cpp @@ -0,0 +1 @@ +# 1 "data/identifier-expansion.5.cpp" diff --git a/tests/auto/cplusplus/preprocessor/data/macro-test.cpp b/tests/auto/cplusplus/preprocessor/data/macro-test.cpp new file mode 100644 index 0000000000..7e3d52e9e8 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/macro-test.cpp @@ -0,0 +1,36 @@ +#define USE(MY_USE) (defined MY_USE_##MY_USE && MY_USE_##MY_USE) + +#define MY_USE_FEATURE1 1 +#define MY_USE_FEATURE2 0 + +#if USE(FEATURE1) +void thisFunctionIsEnabled(); +#endif + +#if USE(FEATURE2) +void thisFunctionIsDisabled(); +#endif + +#if USE(FEATURE3) +void thisFunctionIsAlsoDisabled(); +#endif + +#define USE2(MY_USE) (defined MY_USE_##MY_USE) + +#if USE2(FEATURE1) +void thisFunctionIsEnabled2(); +#endif + +#if USE2(FEATURE3) +void thisFunctionIsDisabled2(); +#endif + +#define USE3(MY_USE) (MY_USE_##MY_USE) + +#if USE3(FEATURE1) +void thisFunctionIsEnabled3(); +#endif + +#if USE3(FEATURE2) +void thisFunctionIsDisabled3(); +#endif diff --git a/tests/auto/cplusplus/preprocessor/data/macro-test.out.cpp b/tests/auto/cplusplus/preprocessor/data/macro-test.out.cpp new file mode 100644 index 0000000000..d13e2c5b0b --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/macro-test.out.cpp @@ -0,0 +1,12 @@ +# 1 "data/macro-test.cpp" + + + + + + +void thisFunctionIsEnabled(); +# 21 "data/macro-test.cpp" +void thisFunctionIsEnabled2(); +# 31 "data/macro-test.cpp" +void thisFunctionIsEnabled3(); diff --git a/tests/auto/cplusplus/preprocessor/data/macro_expand.c b/tests/auto/cplusplus/preprocessor/data/macro_expand.c new file mode 100644 index 0000000000..44bf3a8788 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/macro_expand.c @@ -0,0 +1,4 @@ +#define X() Y +#define Y() X + +A: X()()() diff --git a/tests/auto/cplusplus/preprocessor/data/macro_expand.out.c b/tests/auto/cplusplus/preprocessor/data/macro_expand.out.c new file mode 100644 index 0000000000..24442a7567 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/macro_expand.out.c @@ -0,0 +1,9 @@ +# 1 "data/macro_expand.c" + + + +A: +# expansion begin 32,1 ~1 +Y +# expansion end +# 5 "data/macro_expand.c" diff --git a/tests/auto/cplusplus/preprocessor/data/macro_expand_1.cpp b/tests/auto/cplusplus/preprocessor/data/macro_expand_1.cpp new file mode 100644 index 0000000000..8420d7ef7d --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/macro_expand_1.cpp @@ -0,0 +1,2 @@ +#define DECLARE_CLASS(s) class s +DECLARE_CLASS(QString); diff --git a/tests/auto/cplusplus/preprocessor/data/macro_expand_1.out.cpp b/tests/auto/cplusplus/preprocessor/data/macro_expand_1.out.cpp new file mode 100644 index 0000000000..e281d8d60f --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/macro_expand_1.out.cpp @@ -0,0 +1,7 @@ +# 1 "data/macro_expand_1.cpp" + +# expansion begin 33,13 ~1 2:14 +class QString +# expansion end +# 2 "data/macro_expand_1.cpp" + ; diff --git a/tests/auto/cplusplus/preprocessor/data/macro_pounder_fn.c b/tests/auto/cplusplus/preprocessor/data/macro_pounder_fn.c new file mode 100644 index 0000000000..7be2e0d14f --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/macro_pounder_fn.c @@ -0,0 +1,18 @@ +// This file is copied from Clang. Everything below this line is "theirs". + +// This pounds on macro expansion for performance reasons. This is currently +// heavily constrained by darwin's malloc. + +// Function-like macros. +#define A0(A, B) A B +#define A1(A, B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) +#define A2(A, B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) +#define A3(A, B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) +#define A4(A, B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) +#define A5(A, B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) +#define A6(A, B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) +#define A7(A, B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) +#define A8(A, B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) + +A8(a, b) + diff --git a/tests/auto/cplusplus/preprocessor/data/noPP.1.cpp b/tests/auto/cplusplus/preprocessor/data/noPP.1.cpp new file mode 100644 index 0000000000..32b2797cee --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/noPP.1.cpp @@ -0,0 +1,65 @@ +WRITE IN C (sung to The Beatles "Let it Be") + +When I find my code in tons of trouble, +Friends and colleagues come to me, +Speaking words of wisdom: +"Write in C." + +As the deadline fast approaches, +And bugs are all that I can see, +Somewhere, someone whispers" +"Write in C." + +Write in C, write in C, +Write in C, write in C. +LISP is dead and buried, +Write in C. + +I used to write a lot of FORTRAN, +for science it worked flawlessly. +Try using it for graphics! +Write in C. + +If you've just spent nearly 30 hours +Debugging some assembly, +Soon you will be glad to +Write in C. + +Write in C, write in C, +Write In C, yeah, write in C. +Only wimps use BASIC. +Write in C. + +Write in C, write in C, +Write in C, oh, write in C. +Pascal won't quite cut it. +Write in C. + +{ + Guitar Solo +} + +Write in C, write in C, +Write in C, yeah, write in C. +Don't even mention COBOL. +Write in C. + +And when the screen is fuzzy, +And the edior is bugging me. +I'm sick of ones and zeroes. +Write in C. + +A thousand people people swear that T.P. +Seven is the one for me. +I hate the word PROCEDURE, +Write in C. + +Write in C, write in C, +Write in C, yeah, write in C. +PL1 is 80's, +Write in C. + +Write in C, write in C, +Write in C, yeah, write in C. +The government loves ADA, +Write in C. diff --git a/tests/auto/cplusplus/preprocessor/data/noPP.2.cpp b/tests/auto/cplusplus/preprocessor/data/noPP.2.cpp new file mode 100644 index 0000000000..60f2cd9596 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/noPP.2.cpp @@ -0,0 +1,26 @@ + +void hey(int a) { + int b = a + 10; + b; +} + +class hello +{ +public: + bool doit() { return true; } + bool dothat(); + void run(); +}; + +bool hello::dothat() +{ + bool should = true; + if (should) { + int i = 10; + while (i > 0) { + run(); + --i; + } + } + return false; +} diff --git a/tests/auto/cplusplus/preprocessor/data/poundpound.1.cpp b/tests/auto/cplusplus/preprocessor/data/poundpound.1.cpp new file mode 100644 index 0000000000..ab02ae6a66 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/poundpound.1.cpp @@ -0,0 +1,7 @@ +struct QQ {}; + +#define NN(x) typedef QQ PP ## x; + +NN(CC) + +typedef PPCC RR; diff --git a/tests/auto/cplusplus/preprocessor/data/poundpound.1.out.cpp b/tests/auto/cplusplus/preprocessor/data/poundpound.1.out.cpp new file mode 100644 index 0000000000..96b0706210 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/poundpound.1.out.cpp @@ -0,0 +1,10 @@ +# 1 "data/poundpound.1.cpp" +struct QQ {}; + + + +# expansion begin 50,2 ~4 +typedef QQ PPCC; +# expansion end +# 7 "data/poundpound.1.cpp" +typedef PPCC RR; diff --git a/tests/auto/cplusplus/preprocessor/data/recursive.1.cpp b/tests/auto/cplusplus/preprocessor/data/recursive.1.cpp new file mode 100644 index 0000000000..3e59b29907 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/recursive.1.cpp @@ -0,0 +1,5 @@ +#define a b +#define b a + +b +a diff --git a/tests/auto/cplusplus/preprocessor/data/recursive.1.out.cpp b/tests/auto/cplusplus/preprocessor/data/recursive.1.out.cpp new file mode 100644 index 0000000000..3f702f011e --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/recursive.1.out.cpp @@ -0,0 +1,11 @@ +# 1 "data/recursive.1.cpp" + + + +# expansion begin 25,1 ~1 +b +# expansion end +# expansion begin 27,1 ~1 +a +# expansion end +# 6 "data/recursive.1.cpp" diff --git a/tests/auto/cplusplus/preprocessor/data/reserved.1.cpp b/tests/auto/cplusplus/preprocessor/data/reserved.1.cpp new file mode 100644 index 0000000000..9c6a2d6230 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/reserved.1.cpp @@ -0,0 +1,10 @@ +#define Q_FOREACH(variable, container) foobar(variable, container) +#define foreach Q_FOREACH + + +int f() { + foreach (QString &s, QStringList()) { + doSomething(); + } + return 1; +} diff --git a/tests/auto/cplusplus/preprocessor/data/reserved.1.out.cpp b/tests/auto/cplusplus/preprocessor/data/reserved.1.out.cpp new file mode 100644 index 0000000000..8eb99b63fb --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/reserved.1.out.cpp @@ -0,0 +1,11 @@ +# 1 "data/reserved.1.cpp" + + + + +int f() { + foreach (QString &s, QStringList()) { + doSomething(); + } + return 1; +} diff --git a/tests/auto/cplusplus/preprocessor/preprocessor.pro b/tests/auto/cplusplus/preprocessor/preprocessor.pro index 564c5b67ca..05c463833a 100644 --- a/tests/auto/cplusplus/preprocessor/preprocessor.pro +++ b/tests/auto/cplusplus/preprocessor/preprocessor.pro @@ -1,3 +1,21 @@ include(../../qttest.pri) include(../shared/shared.pri) SOURCES += tst_preprocessor.cpp + +OTHER_FILES = \ + data/noPP.1.cpp \ + data/noPP.2.cpp \ + data/identifier-expansion.1.cpp data/identifier-expansion.1.out.cpp \ + data/identifier-expansion.2.cpp data/identifier-expansion.2.out.cpp \ + data/identifier-expansion.3.cpp data/identifier-expansion.3.out.cpp \ + data/identifier-expansion.4.cpp data/identifier-expansion.4.out.cpp \ + data/identifier-expansion.5.cpp data/identifier-expansion.5.out.cpp \ + data/reserved.1.cpp data/reserved.1.out.cpp \ + data/recursive.1.cpp data/recursive.1.out.cpp \ + data/macro_expand.c data/macro_expand.out.c \ + data/macro_expand_1.cpp data/macro_expand_1.out.cpp \ + data/macro-test.cpp data/macro-test.out.cpp \ + data/poundpound.1.cpp data/poundpound.1.out.cpp \ + data/empty-macro.cpp data/empty-macro.out.cpp \ + data/empty-macro.2.cpp data/empty-macro.2.out.cpp \ + data/macro_pounder_fn.c diff --git a/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp b/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp index 1add272616..f528c52a81 100644 --- a/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp +++ b/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp @@ -32,31 +32,639 @@ #include <QtTest> #include <pp.h> +#include <QHash> //TESTED_COMPONENT=src/libs/cplusplus using namespace CPlusPlus; -class tst_Preprocessor: public QObject +#define DUMP_OUTPUT(x) {QByteArray b(x);qDebug("output: [[%s]]", b.replace("\n", "<<\n").constData());} + + +QByteArray loadSource(const QString &fileName) +{ + QFile inf(fileName); + if (!inf.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug("Cannot open \"%s\"", fileName.toUtf8().constData()); + return QByteArray(); + } + + QTextStream ins(&inf); + QString source = ins.readAll(); + inf.close(); + return source.toUtf8(); +} + +void saveData(const QByteArray &data, const QString &fileName) +{ + QFile inf(fileName); + if (!inf.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug("Cannot open \"%s\"", fileName.toUtf8().constData()); + return; + } + + inf.write(data); + inf.close(); +} + +struct Include +{ + Include(const QString &fileName, Client::IncludeType type, unsigned line) + : fileName(fileName), type(type), line(line) + {} + + QString fileName; + Client::IncludeType type; + unsigned line; +}; + +QDebug &operator<<(QDebug& d, const Include &i) +{ + d << '[' << i.fileName + << ',' << (i.type == Client::IncludeGlobal ? "Global" + : (i.type == Client::IncludeLocal ? "Local" : "Unknown")) + << ',' << i.line + << ']'; + return d; +} + +class MockClient : public Client +{ +public: + struct Block { + Block() : start(0), end(0) {} + Block(unsigned start) : start(start), end(0) {} + + unsigned start; + unsigned end; + }; + +public: + MockClient(Environment *env, QByteArray *output) + : m_env(env) + , m_output(output) + , m_pp(this, env) + , m_includeDepth(0) + {} + + virtual ~MockClient() {} + + virtual void macroAdded(const Macro & macro) + { + m_definedMacros.append(macro.name()); + m_definedMacrosLine.append(macro.line()); + } + + virtual void passedMacroDefinitionCheck(unsigned /*offset*/, + unsigned /*line*/, + const Macro &/*macro*/) {} + virtual void failedMacroDefinitionCheck(unsigned /*offset*/, const ByteArrayRef &/*name*/) {} + + virtual void notifyMacroReference(unsigned offset, unsigned line, const Macro ¯o) + { + m_macroUsesLine[macro.name()].append(line); + m_expandedMacrosOffset.append(offset); + } + + virtual void startExpandingMacro(unsigned offset, + unsigned line, + const Macro ¯o, + const QVector<MacroArgumentReference> &actuals + = QVector<MacroArgumentReference>()) + { + m_expandedMacros.append(macro.name()); + m_expandedMacrosOffset.append(offset); + m_macroUsesLine[macro.name()].append(line); + m_macroArgsCount.append(actuals.size()); + } + + virtual void stopExpandingMacro(unsigned /*offset*/, const Macro &/*macro*/) {} + + virtual void startSkippingBlocks(unsigned offset) + { m_skippedBlocks.append(Block(offset)); } + + virtual void stopSkippingBlocks(unsigned offset) + { m_skippedBlocks.last().end = offset; } + + virtual void sourceNeeded(unsigned line, QString &includedFileName, IncludeType mode) + { +#if 1 + m_recordedIncludes.append(Include(includedFileName, mode, line)); +#else + Q_UNUSED(line); + + QString resolvedFileName; + if (mode == IncludeLocal) + resolvedFileName = resolveLocally(m_env->currentFile, includedFileName); + else + resolvedFileName = resolveGlobally(includedFileName); + + // qDebug("resolved [[%s]] to [[%s]] from [[%s]] (%s)\n", + // includedFileName.toUtf8().constData(), + // resolvedFileName.toUtf8().constData(), + // currentFileName.toUtf8().constData(), + // (mode == IncludeLocal) ? "locally" : "globally"); + + if (resolvedFileName.isEmpty()) + return; + + ++m_includeDepth; + // qDebug("%5d %s %s", m_includeDepth, QByteArray(m_includeDepth, '+').constData(), resolvedFileName.toUtf8().constData()); + sourceNeeded(resolvedFileName); + --m_includeDepth; +#endif + } + + QString resolveLocally(const QString ¤tFileName, + const QString &includedFileName) const + { + QDir dir; + if (currentFileName.isEmpty()) + dir = QDir::current(); + else + dir = QFileInfo(currentFileName).dir(); + const QFileInfo inc(dir, includedFileName); + if (inc.exists()) { + const QString resolved = inc.filePath(); + return resolved.toUtf8().constData(); + } else { + // std::cerr<<"Cannot find " << inc.fileName().toUtf8().constData()<<std::endl; + return QString(); + } + } + + QString resolveGlobally(const QString ¤tFileName) const + { + foreach (const QDir &dir, m_includePaths) { + QFileInfo f(dir, currentFileName); + if (f.exists()) + return f.filePath(); + } + + return QString(); + } + + void setIncludePaths(const QStringList &includePaths) + { + foreach (const QString &path, includePaths) { + QDir dir(path); + if (dir.exists()) + m_includePaths.append(dir); + } + } + + void sourceNeeded(const QString &fileName, bool nolines) + { + QByteArray src = loadSource(fileName); + QVERIFY(!src.isEmpty()); + *m_output = m_pp.run(fileName, src, nolines, true); + } + + QList<Block> skippedBlocks() const + { return m_skippedBlocks; } + + QList<Include> recordedIncludes() const + { return m_recordedIncludes; } + + QList<QByteArray> expandedMacros() const + { return m_expandedMacros; } + + QList<unsigned> expandedMacrosOffset() const + { return m_expandedMacrosOffset; } + + QList<QByteArray> definedMacros() const + { return m_definedMacros; } + + QList<unsigned> definedMacrosLine() const + { return m_definedMacrosLine; } + + QHash<QByteArray, QList<unsigned> > macroUsesLine() const + { return m_macroUsesLine; } + + const QList<int> macroArgsCount() const + { return m_macroArgsCount; } + +private: + Environment *m_env; + QByteArray *m_output; + Preprocessor m_pp; + QList<QDir> m_includePaths; + unsigned m_includeDepth; + QList<Block> m_skippedBlocks; + QList<Include> m_recordedIncludes; + QList<QByteArray> m_expandedMacros; + QList<unsigned> m_expandedMacrosOffset; + QList<QByteArray> m_definedMacros; + QList<unsigned> m_definedMacrosLine; + QHash<QByteArray, QList<unsigned> > m_macroUsesLine; + QList<int> m_macroArgsCount; +}; + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> char *toString(const QList<unsigned> &list) + { + QByteArray ba = "QList<unsigned>("; + foreach (const unsigned& item, list) { + ba += QTest::toString(item); + ba += ','; + } + if (!list.isEmpty()) + ba[ba.size() - 1] = ')'; + return qstrdup(ba.data()); + } + template<> char *toString(const QList<QByteArray> &list) + { + QByteArray ba = "QList<QByteArray>("; + foreach (const QByteArray& item, list) { + ba += QTest::toString(item); + ba += ','; + } + if (!list.isEmpty()) + ba[ba.size() - 1] = ')'; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +QDebug &operator<<(QDebug& d, const MockClient::Block &b) +{ + d << '[' << b.start << ',' << b.end << ']'; + return d; +} + +class tst_Preprocessor : public QObject { -Q_OBJECT + Q_OBJECT -private Q_SLOTS: +protected: + QByteArray preprocess(const QString &fileName, QByteArray * /*errors*/, bool nolines) { + //### TODO: hook up errors + QByteArray output; + Environment env; + MockClient client(&env, &output); + client.sourceNeeded("data/" + fileName, nolines); + return output; + } + static QString simplified(QByteArray buf); + +private: + void compare_input_output(); + +private slots: + void va_args(); + void named_va_args(); + void defined(); + void defined_data(); + void empty_macro_args(); + void macro_args_count(); + void invalid_param_count(); + void objmacro_expanding_as_fnmacro_notification(); + void macro_uses(); + void macro_uses_lines(); + void macro_arguments_notificatin(); void unfinished_function_like_macro_call(); void nasty_macro_expansion(); - void tstst(); + void glib_attribute(); + void builtin__FILE__(); + void blockSkipping(); + void includes_1(); + void dont_eagerly_expand(); + void dont_eagerly_expand_data(); + void comparisons_data(); + void comparisons(); + void comments_within(); + void comments_within_data(); + void multitokens_argument(); + void multitokens_argument_data(); }; -void tst_Preprocessor::unfinished_function_like_macro_call() +// Remove all #... lines, and 'simplify' string, to allow easily comparing the result +// Also, remove all unneeded spaces: keep only to ensure identifiers are separated. +// NOTE: may not correctly handle underscore in identifiers +QString tst_Preprocessor::simplified(QByteArray buf) +{ + QString out; + QList<QByteArray> lines = buf.split('\n'); + foreach (const QByteArray &line, lines) { + if (!line.startsWith('#')) { + out.append(' '); + out.append(line); + } + } + + out = out.simplified(); + for (int i = 1; i < out.length() - 1; ) { + if (out.at(i).isSpace() + && !(out.at(i-1).isLetterOrNumber() + && out.at(i+1).isLetterOrNumber())) + out.remove(i,1); + else + i++; + } + + return out; +} + +void tst_Preprocessor::va_args() { Client *client = 0; // no client. Environment env; Preprocessor preprocess(client, &env); - QByteArray preprocessed = preprocess(QLatin1String("<stdin>"), + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), + "#define foo(...) int f(__VA_ARGS__);\n" + "\nfoo( )\n" + "\nfoo(int a)\n" + "\nfoo(int a,int b)\n", + true, false); + + preprocessed = preprocessed.simplified(); +// DUMP_OUTPUT(preprocessed); + QCOMPARE(simplified(preprocessed), QString("int f();int f(int a);int f(int a,int b);")); +} + +void tst_Preprocessor::named_va_args() +{ + Client *client = 0; // no client. + Environment env; + + Preprocessor preprocess(client, &env); + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), + "\n#define foo(ARGS...) int f(ARGS);" + "\nfoo( )\n" + "\nfoo(int a)\n" + "\nfoo(int a,int b)\n", + true, false); + + preprocessed = preprocessed.simplified(); + QCOMPARE(simplified(preprocessed), QString("int f();int f(int a);int f(int a,int b);")); +} + +void tst_Preprocessor::empty_macro_args() +{ + Client *client = 0; // no client. + Environment env; + + Preprocessor preprocess(client, &env); + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), + "\n#define foo(a,b) a int b;" + "\nfoo(const,cVal)\n" + "\nfoo(,Val)\n" + "\nfoo( ,Val2)\n" + "\nfoo(,)\n" + "\nfoo(, )\n", + true, false); + + preprocessed = preprocessed.simplified(); +// DUMP_OUTPUT(preprocessed); + QCOMPARE(simplified(preprocessed), + QString("const int cVal;int Val;int Val2;int;int;")); +} + +void tst_Preprocessor::macro_args_count() +{ + Environment env; + QByteArray output; + MockClient client(&env, &output); + Preprocessor preprocess(&client, &env); + preprocess.run(QLatin1String("<stdin>"), + "#define foo(a,b) a int b;\n" + "foo(const,cVal)\n" + "foo(, i)\n" + "foo(,Val)\n" + "foo( ,Val2)\n" + "foo(,)\n" + "foo(, )\n" + "#define bar(a)\n" + "bar()\n" + "bar(i)\n", + true, false); + + QCOMPARE(client.macroArgsCount(), + QList<int>() << 2 // foo(const,cVal) + << 2 // foo(, i) + << 2 // foo(,Val) + << 2 // foo( , Val2) + << 2 // foo(,) + << 2 // foo(, ) + << 1 // bar() + << 1 // bar(i) + ); + +} + +void tst_Preprocessor::invalid_param_count() +{ + Environment env; + QByteArray output; + MockClient client(&env, &output); + Preprocessor preprocess(&client, &env); + // The following are illegal, but shouldn't crash the preprocessor. + preprocess.run(QLatin1String("<stdin>"), + "\n#define foo(a,b) int f(a,b);" + "\n#define ARGS(t) t a,t b" + "\nfoo(ARGS(int))" + "\nfoo()" + "\nfoo(int a, int b, int c)", + true, false); + + // Output is not that relevant but check that nothing triggered expansion. + QCOMPARE(client.macroArgsCount(), QList<int>()); +} + +void tst_Preprocessor::macro_uses() +{ + QByteArray buffer = QByteArray("\n#define FOO 8" + "\n#define BAR 9" + "\nvoid test(){" + "\n\tint x=FOO;" + "\n\tint y=BAR;" + "\n}"); + + QByteArray output; + Environment env; + MockClient client(&env, &output); + + Preprocessor preprocess(&client, &env); + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), buffer); + QCOMPARE(simplified(preprocessed), QString("void test(){int x=8;int y=9;}")); + QCOMPARE(client.expandedMacros(), QList<QByteArray>() << QByteArray("FOO") << QByteArray("BAR")); + QCOMPARE(client.expandedMacrosOffset(), QList<unsigned>() << buffer.indexOf("FOO;") << buffer.indexOf("BAR;")); + QCOMPARE(client.definedMacros(), QList<QByteArray>() << QByteArray("FOO") << QByteArray("BAR")); + QCOMPARE(client.definedMacrosLine(), QList<unsigned>() << 2 << 3); +} + +void tst_Preprocessor::macro_uses_lines() +{ + QByteArray buffer("#define FOO\n" + "FOO\n" + "\n" + "#define HEADER <test>\n" + "#include HEADER\n" + "\n" + "#define DECLARE(C, V) struct C {}; C V;\n" + "#define ABC X\n" + "DECLARE(Test, test)\n" + "\n" + "int abc;\n" + "#define NOTHING(C)\n" + "NOTHING(abc)\n" + "\n" + "#define ENABLE(FEATURE) (defined ENABLE_##FEATURE && ENABLE_##FEATURE)\n" + "#define ENABLE_COOL 1\n" + "void fill();\n" + "#if ENABLE(COOL)\n" + "class Cool {};\n" + "#endif\n" + "int cool = ENABLE_COOL;\n" + "#define OTHER_ENABLE(FEATURE) ENABLE(FEATURE)\n" + "#define MORE(LESS) FOO ENABLE(LESS)\n"); + + QByteArray output; + Environment env; + MockClient client(&env, &output); + Preprocessor preprocess(&client, &env); + preprocess.run(QLatin1String("<stdin>"), buffer); + + QCOMPARE(client.macroUsesLine().value("FOO"), QList<unsigned>() << 2U << 23U); + QCOMPARE(client.macroUsesLine().value("HEADER"), QList<unsigned>() << 5U); + QCOMPARE(client.macroUsesLine().value("DECLARE"), QList<unsigned>() << 9U); + QCOMPARE(client.macroUsesLine().value("NOTHING"), QList<unsigned>() << 13U); + QCOMPARE(client.macroUsesLine().value("ENABLE"), QList<unsigned>() << 18U << 22U << 23U); + QCOMPARE(client.macroUsesLine().value("ENABLE_COOL"), QList<unsigned>() << 21U); + QCOMPARE(client.expandedMacrosOffset(), QList<unsigned>() + << buffer.lastIndexOf("FOO\n") + << buffer.lastIndexOf("HEADER") + << buffer.lastIndexOf("DECLARE") + << buffer.lastIndexOf("NOTHING") + << buffer.lastIndexOf("ENABLE(COOL)") + << buffer.lastIndexOf("ENABLE_COOL") + << buffer.lastIndexOf("ENABLE(FEATURE)") + << buffer.lastIndexOf("FOO ") + << buffer.lastIndexOf("ENABLE(LESS)")); +} + +void tst_Preprocessor::multitokens_argument_data() +{ + QTest::addColumn<QByteArray>("input"); + QTest::addColumn<QByteArray>("output"); + + QByteArray original; + QByteArray expected; + + original = + "#define foo(ARGS) int f(ARGS)\n" + "foo(int a);\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "# expansion begin 30,3 ~3 2:4 2:8 ~1\n" + "int f(int a)\n" + "# expansion end\n" + "# 2 \"<stdin>\"\n" + " ;\n"; + QTest::newRow("case 1") << original << expected; + + original = + "#define foo(ARGS) int f(ARGS)\n" + "foo(int \n" + " a);\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "# expansion begin 30,3 ~3 2:4 3:4 ~1\n" + "int f(int a)\n" + "# expansion end\n" + "# 3 \"<stdin>\"\n" + " ;\n"; + QTest::newRow("case 2") << original << expected; + + original = + "#define foo(ARGS) int f(ARGS)\n" + "foo(int a = 0);\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "# expansion begin 30,3 ~3 2:4 2:8 2:10 2:12 ~1\n" + "int f(int a = 0)\n" + "# expansion end\n" + "# 2 \"<stdin>\"\n" + " ;\n"; + QTest::newRow("case 3") << original << expected; + + original = + "#define foo(X) int f(X = 0)\n" + "foo(int \n" + " a);\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "# expansion begin 28,3 ~3 2:4 3:4 ~3\n" + "int f(int a = 0)\n" + "# expansion end\n" + "# 3 \"<stdin>\"\n" + " ;\n"; + QTest::newRow("case 4") << original << expected; +} + +void tst_Preprocessor::multitokens_argument() +{ + compare_input_output(); +} + +void tst_Preprocessor::objmacro_expanding_as_fnmacro_notification() +{ + QByteArray output; + Environment env; + MockClient client(&env, &output); + + Preprocessor preprocess(&client, &env); + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), + QByteArray("\n#define bar(a,b) a + b" + "\n#define foo bar" + "\nfoo(1, 2)\n")); + + QVERIFY(client.expandedMacros() == (QList<QByteArray>() << QByteArray("foo"))); +} + +void tst_Preprocessor::macro_arguments_notificatin() +{ + QByteArray output; + Environment env; + MockClient client(&env, &output); + + Preprocessor preprocess(&client, &env); + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), QByteArray("\n#define foo(a,b) a + b" - "\nfoo(1, 2\n")); + "\n#define arg(a) a" + "\n#define value 2" + "\nfoo(arg(1), value)\n")); - QCOMPARE(preprocessed.trimmed(), QByteArray("foo")); + QVERIFY(client.expandedMacros() == (QList<QByteArray>() << QByteArray("foo") + << QByteArray("arg") + << QByteArray("value"))); +} + +void tst_Preprocessor::unfinished_function_like_macro_call() +{ + Client *client = 0; // no client. + Environment env; + + Preprocessor preprocess(client, &env); + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), + QByteArray("\n" + "#define foo(a,b) a + b\n" + "foo(1, 2\n")); + QByteArray expected__("# 1 \"<stdin>\"\n" + "\n" + "\n" + "# expansion begin 24,3 3:4 ~1 3:7\n" + "1 + 2\n" + "# expansion end\n" + "# 4 \"<stdin>\"\n"); + +// DUMP_OUTPUT(preprocessed); + QCOMPARE(preprocessed, expected__); } void tst_Preprocessor::nasty_macro_expansion() @@ -107,33 +715,483 @@ void tst_Preprocessor::nasty_macro_expansion() Environment env; Preprocessor preprocess(client, &env); - QByteArray preprocessed = preprocess(QLatin1String("<stdin>"), input); + QByteArray preprocessed = preprocess.run(QLatin1String("<stdin>"), input); QVERIFY(!preprocessed.contains("FIELD32")); } -void tst_Preprocessor::tstst() +void tst_Preprocessor::glib_attribute() { - Client *client = 0; // no client. Environment env; - - Preprocessor preprocess(client, &env); - QByteArray preprocessed = preprocess( + Preprocessor preprocess(0, &env); + QByteArray preprocessed = preprocess.run( QLatin1String("<stdin>"), QByteArray("\n" "# define _GLIBCXX_VISIBILITY(V) __attribute__ ((__visibility__ (#V)))\n" "namespace std _GLIBCXX_VISIBILITY(default) {\n" "}\n" )); + const QByteArray result____ = + "# 1 \"<stdin>\"\n" + "\n" + "\n" + "namespace std\n" + "# expansion begin 85,19 ~9\n" + "__attribute__ ((__visibility__ (\"default\")))\n" + "# expansion end\n" + "# 3 \"<stdin>\"\n" + " {\n" + "}\n"; + +// DUMP_OUTPUT(preprocessed); + QCOMPARE(preprocessed, result____); +} + +void tst_Preprocessor::builtin__FILE__() +{ + Client *client = 0; // no client. + Environment env; + + Preprocessor preprocess(client, &env); + QByteArray preprocessed = preprocess.run( + QLatin1String("some-file.c"), + QByteArray("const char *f = __FILE__\n" + )); + const QByteArray result____ = + "# 1 \"some-file.c\"\n" + "const char *f = \"some-file.c\"\n"; + + QCOMPARE(preprocessed, result____); +} + +void tst_Preprocessor::comparisons_data() +{ + QTest::addColumn<QString>("infile"); + QTest::addColumn<QString>("outfile"); + QTest::addColumn<QString>("errorfile"); + + QTest::newRow("do nothing") << "noPP.1.cpp" << "noPP.1.cpp" << ""; + QTest::newRow("no PP 2") << "noPP.2.cpp" << "noPP.2.cpp" << ""; + QTest::newRow("identifier-expansion 1") + << "identifier-expansion.1.cpp" << "identifier-expansion.1.out.cpp" << ""; + QTest::newRow("identifier-expansion 2") + << "identifier-expansion.2.cpp" << "identifier-expansion.2.out.cpp" << ""; + QTest::newRow("identifier-expansion 3") + << "identifier-expansion.3.cpp" << "identifier-expansion.3.out.cpp" << ""; + QTest::newRow("identifier-expansion 4") + << "identifier-expansion.4.cpp" << "identifier-expansion.4.out.cpp" << ""; + QTest::newRow("identifier-expansion 5") + << "identifier-expansion.5.cpp" << "identifier-expansion.5.out.cpp" << ""; + QTest::newRow("reserved 1") + << "reserved.1.cpp" << "reserved.1.out.cpp" << ""; + QTest::newRow("recursive 1") + << "recursive.1.cpp" << "recursive.1.out.cpp" << ""; + QTest::newRow("macro_pounder_fn") + << "macro_pounder_fn.c" << "" << ""; + QTest::newRow("macro_expand") + << "macro_expand.c" << "macro_expand.out.c" << ""; + QTest::newRow("macro_expand_1") + << "macro_expand_1.cpp" << "macro_expand_1.out.cpp" << ""; + QTest::newRow("macro-test") + << "macro-test.cpp" << "macro-test.out.cpp" << ""; + QTest::newRow("empty-macro") + << "empty-macro.cpp" << "empty-macro.out.cpp" << ""; + QTest::newRow("empty-macro 2") + << "empty-macro.2.cpp" << "empty-macro.2.out.cpp" << ""; + QTest::newRow("poundpound 1") + << "poundpound.1.cpp" << "poundpound.1.out.cpp" << ""; +} + +void tst_Preprocessor::comparisons() +{ + QFETCH(QString, infile); + QFETCH(QString, outfile); + QFETCH(QString, errorfile); + + QByteArray errors; + QByteArray preprocessed = preprocess(infile, &errors, infile == outfile); + + + // DUMP_OUTPUT(preprocessed); + + if (!outfile.isEmpty()) { + // These weird underscores are here to make the name as long as + // "preprocessed", so the QCOMPARE error messages are nicely aligned. + QByteArray output____ = loadSource("data/" + outfile); + // QCOMPARE(preprocessed, output____); + QCOMPARE(QString::fromUtf8(preprocessed.constData()), + QString::fromUtf8(output____.constData())); + } + + if (!errorfile.isEmpty()) { + QByteArray errorFileContents = loadSource("data/" + errorfile); + QCOMPARE(QString::fromUtf8(errors.constData()), + QString::fromUtf8(errorFileContents.constData())); + } +} + +void tst_Preprocessor::blockSkipping() +{ + QByteArray output; + Environment env; + MockClient client(&env, &output); + Preprocessor pp(&client, &env); + /*QByteArray preprocessed =*/ pp.run( + QLatin1String("<stdin>"), + QByteArray("#if 0\n" + "\n" + "int yes;\n" + "\n" + "#elif 0\n" + "\n" + "int no;\n" + "\n" + "#else // foobar\n" + "\n" + "void also_not;\n" + "\n" + "#endif\n" + )); - qDebug() << preprocessed; + QList<MockClient::Block> blocks = client.skippedBlocks(); + QCOMPARE(blocks.size(), 1); + MockClient::Block b = blocks.at(0); + QCOMPARE(b.start, 6U); + QCOMPARE(b.end, 34U); +} + +void tst_Preprocessor::includes_1() +{ + QByteArray output; + Environment env; + MockClient client(&env, &output); + Preprocessor pp(&client, &env); + /*QByteArray preprocessed =*/ pp.run( + QLatin1String("<stdin>"), + QByteArray("#define FOO <foo.h>\n" + "#define BAR \"bar.h\"\n" + "\n" + "#include FOO\n" + "#include BAR\n" + "\n" + "#include <zoo.h>\n" + "#include \"mooze.h\"\n" + )); - /* -# define _GLIBCXX_VISIBILITY(V) __attribute__ ((__visibility__ (#V))) -namespace std _GLIBCXX_VISIBILITY(default) + QList<Include> incs = client.recordedIncludes(); +// qDebug()<<incs; + QCOMPARE(incs.size(), 4); + QCOMPARE(incs.at(0).fileName, QLatin1String("foo.h")); + QCOMPARE(incs.at(0).type, Client::IncludeGlobal); + QCOMPARE(incs.at(0).line, 4U); + QCOMPARE(incs.at(1).fileName, QLatin1String("bar.h")); + QCOMPARE(incs.at(1).type, Client::IncludeLocal); + QCOMPARE(incs.at(1).line, 5U); + QCOMPARE(incs.at(2).fileName, QLatin1String("zoo.h")); + QCOMPARE(incs.at(2).type, Client::IncludeGlobal); + QCOMPARE(incs.at(2).line, 7U); + QCOMPARE(incs.at(3).fileName, QLatin1String("mooze.h")); + QCOMPARE(incs.at(3).type, Client::IncludeLocal); + QCOMPARE(incs.at(3).line, 8U); +} - */ +void tst_Preprocessor::defined() +{ + QFETCH(bool, xdefined); + QFETCH(bool, ydefined); + QFETCH(QString, input); + QByteArray output; + Environment env; + MockClient client(&env, &output); + Preprocessor pp(&client, &env); + pp.run(QLatin1String("<stdin>"), input.toLatin1(), false, true); + QList<QByteArray> expected; + if (xdefined) + expected.append("X"); + if (ydefined) + expected.append("Y"); + if (client.definedMacros() != expected) + qWarning() << "\nSource: " << input.replace('\n', " "); + QCOMPARE(client.definedMacros(), expected); +} + +void tst_Preprocessor::defined_data() +{ + QTest::addColumn<bool>("xdefined"); + QTest::addColumn<bool>("ydefined"); + QTest::addColumn<QString>("input"); + + QTest::newRow("1a") << true << true << + "#define X\n#if defined(X)\n#define Y\n#endif"; + QTest::newRow("1b") << true << true << + "#define X\n#if defined X \n#define Y\n#endif"; + QTest::newRow("1c") << true << true << + "#define X\n#ifdef X \n#define Y\n#endif"; + + QTest::newRow("2a") << false << false << + "#if defined(X)\n#define Y\n#endif"; + QTest::newRow("2b") << false << false << + "#if defined X \n#define Y\n#endif"; + QTest::newRow("2c") << false << false << + "#ifdef X \n#define Y\n#endif"; + + QTest::newRow("3a") << true << false << + "#define X\n#if !defined(X)\n#define Y\n#endif"; + QTest::newRow("3b") << true << false << + "#define X\n#if !defined X \n#define Y\n#endif"; + QTest::newRow("3c") << true << false << + "#define X\n#ifndef X \n#define Y\n#endif"; + + QTest::newRow("4a") << false << true << + "#if !defined(X)\n#define Y\n#endif"; + QTest::newRow("4b") << false << true << + "#if !defined X \n#define Y\n#endif"; + QTest::newRow("4c") << false << true << + "#ifndef X \n#define Y\n#endif"; + + QTest::newRow("5a") << false << false << + "#if !defined(X) && (defined(Y))\n" + "#define X\n" + "#endif\n"; + QTest::newRow("5b") << false << false << + "#if !defined(X) && defined(Y)\n" + "#define X\n" + "#endif\n"; + QTest::newRow("5c") << false << false << + "#if !defined(X) && 0" + "#define X\n" + "#endif\n"; + QTest::newRow("5d") << false << false << + "#if (!defined(X)) && defined(Y)\n" + "#define X\n" + "#endif\n"; + QTest::newRow("5d") << false << false << + "#if (define(Y))\n" + "#define X\n" + "#endif\n"; + + QTest::newRow("6a") << true << true << + "#define X 0x040500\n" + "#if X > 0x040000\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("6b") << true << true << + "#define X 0x040500\n" + "#if X >= 0x040000\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("6c") << true << false << + "#define X 0x040500\n" + "#if X == 0x040000\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("6d") << true << true << + "#define X 0x040500\n" + "#if X == 0x040500\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("6e") << true << false << + "#define X 0x040500\n" + "#if X < 0x040000\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("6f") << true << false << + "#define X 0x040500\n" + "#if X <= 0x040000\n" + "#define Y 1\n" + "#endif\n"; + + QTest::newRow("incomplete defined 1") << true << true << + "#define X 0x040500\n" + "#if defined(X\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("incomplete defined 2") << false << false << + "#if defined(X\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("complete defined 1") << true << true << + "#define X 0x040500\n" + "#if defined(X )\n" + "#define Y 1\n" + "#endif\n"; + QTest::newRow("complete defined 2") << true << true << + "#define X 0x040500\n" + "#if defined(X/*xxx*/)\n" + "#define Y 1\n" + "#endif\n"; +} + +void tst_Preprocessor::dont_eagerly_expand_data() +{ + QTest::addColumn<QByteArray>("input"); + QTest::addColumn<QByteArray>("output"); + + QByteArray original; + QByteArray expected; + + // Expansion must be processed upon invocation of the macro. Therefore a particular + // identifier within a define must not be expanded (in the case it matches an + // already known macro) during the processor directive handling, but only when + // it's actually "used". Naturally, if it's still not replaced after an invocation + // it should then be expanded. This is consistent with clang and gcc for example. + + original = "#define T int\n" + "#define FOO(T) T\n" + "FOO(double)\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "\n" + "# expansion begin 31,3 3:4\n" + "double\n" + "# expansion end\n" + "# 4 \"<stdin>\"\n"; + QTest::newRow("case 1") << original << expected; + + original = "#define T int\n" + "#define FOO(X) T\n" + "FOO(double)\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "\n" + "# expansion begin 31,3 ~1\n" + "int\n" + "# expansion end\n" + "# 4 \"<stdin>\"\n"; + QTest::newRow("case 2") << original << expected; +} + +void tst_Preprocessor::dont_eagerly_expand() +{ + compare_input_output(); +} + +void tst_Preprocessor::comments_within() +{ + compare_input_output(); +} + +void tst_Preprocessor::comments_within_data() +{ + QTest::addColumn<QByteArray>("input"); + QTest::addColumn<QByteArray>("output"); + + QByteArray original; + QByteArray expected; + + original = "#define FOO int x;\n" + "\n" + " // comment\n" + " // comment\n" + " // comment\n" + " // comment\n" + "FOO\n" + "x = 10\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "# expansion begin 76,3 ~3\n" + "int x;\n" + "# expansion end\n" + "# 8 \"<stdin>\"\n" + "x = 10\n"; + QTest::newRow("case 1") << original << expected; + + + original = "#define FOO int x;\n" + "\n" + " /* comment\n" + " comment\n" + " comment\n" + " comment */\n" + "FOO\n" + "x = 10\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "# expansion begin 79,3 ~3\n" + "int x;\n" + "# expansion end\n" + "# 8 \"<stdin>\"\n" + "x = 10\n"; + QTest::newRow("case 2") << original << expected; + + + original = "#define FOO int x;\n" + "\n" + " // comment\n" + " // comment\n" + " // comment\n" + " // comment\n" + "FOO\n" + "// test\n" + "// test again\n" + "x = 10\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "# expansion begin 76,3 ~3\n" + "int x;\n" + "# expansion end\n" + "# 10 \"<stdin>\"\n" + "x = 10\n"; + QTest::newRow("case 3") << original << expected; + + + original = "#define FOO int x;\n" + "\n" + " /* comment\n" + " comment\n" + " comment\n" + " comment */\n" + "FOO\n" + "/* \n" + "*/\n" + "x = 10\n"; + expected = + "# 1 \"<stdin>\"\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "# expansion begin 79,3 ~3\n" + "int x;\n" + "# expansion end\n" + "# 10 \"<stdin>\"\n" + "x = 10\n"; + QTest::newRow("case 4") << original << expected; +} + +void tst_Preprocessor::compare_input_output() +{ + QFETCH(QByteArray, input); + QFETCH(QByteArray, output); + + Environment env; + Preprocessor preprocess(0, &env); + QByteArray prep = preprocess.run(QLatin1String("<stdin>"), input); + QCOMPARE(output, prep); } QTEST_APPLESS_MAIN(tst_Preprocessor) + #include "tst_preprocessor.moc" |