forked from qt-creator/qt-creator
[C++] Rewrite of the preprocessor.
This rewrite fixes a couple of issues with the pre-processor. It now supports: - macros in macro bodies - stringification of parameters [cpp.stringize] - the concatenation operator [cpp.concat] - #include MACRO_HERE - defined() inside macro bodies used in pp-conditions. Change-Id: Ifdb78041fb6afadf44f939a4bd66ce2832b8601f Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
This commit is contained in:
13
tests/auto/cplusplus/preprocessor/data/empty-macro.2.cpp
Normal file
13
tests/auto/cplusplus/preprocessor/data/empty-macro.2.cpp
Normal file
@@ -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();
|
||||
};
|
||||
|
31
tests/auto/cplusplus/preprocessor/data/empty-macro.2.out.cpp
Normal file
31
tests/auto/cplusplus/preprocessor/data/empty-macro.2.out.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
# 6 "data/empty-macro.2.cpp"
|
||||
class Test {
|
||||
private:
|
||||
Test
|
||||
#gen true
|
||||
# 3 "data/empty-macro.2.cpp"
|
||||
(const
|
||||
#gen false
|
||||
# 8 "data/empty-macro.2.cpp"
|
||||
Test
|
||||
#gen true
|
||||
# 3 "data/empty-macro.2.cpp"
|
||||
&);
|
||||
#gen false
|
||||
# 8 "data/empty-macro.2.cpp"
|
||||
Test
|
||||
#gen true
|
||||
# 4 "data/empty-macro.2.cpp"
|
||||
&operator=(const
|
||||
#gen false
|
||||
# 8 "data/empty-macro.2.cpp"
|
||||
Test
|
||||
#gen true
|
||||
# 4 "data/empty-macro.2.cpp"
|
||||
&);
|
||||
#gen false
|
||||
# 10 "data/empty-macro.2.cpp"
|
||||
public:
|
||||
Test();
|
||||
};
|
||||
|
5
tests/auto/cplusplus/preprocessor/data/empty-macro.cpp
Normal file
5
tests/auto/cplusplus/preprocessor/data/empty-macro.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#define EMPTY_MACRO
|
||||
|
||||
class EMPTY_MACRO Foo {
|
||||
};
|
||||
|
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
class Foo {
|
||||
};
|
||||
|
@@ -0,0 +1,5 @@
|
||||
#define TEST test
|
||||
|
||||
TEST TEST;
|
||||
|
||||
void TEST();
|
@@ -0,0 +1,14 @@
|
||||
#gen true
|
||||
# 1 "data/identifier-expansion.1.cpp"
|
||||
test test
|
||||
#gen false
|
||||
# 3 "data/identifier-expansion.1.cpp"
|
||||
;
|
||||
|
||||
void
|
||||
#gen true
|
||||
# 1 "data/identifier-expansion.1.cpp"
|
||||
test
|
||||
#gen false
|
||||
# 5 "data/identifier-expansion.1.cpp"
|
||||
();
|
@@ -0,0 +1,6 @@
|
||||
#define TEST test
|
||||
#define ANOTHER_TEST TEST
|
||||
|
||||
ANOTHER_TEST TEST;
|
||||
|
||||
void ANOTHER_TEST();
|
@@ -0,0 +1,14 @@
|
||||
#gen true
|
||||
# 1 "data/identifier-expansion.2.cpp"
|
||||
test test
|
||||
#gen false
|
||||
# 4 "data/identifier-expansion.2.cpp"
|
||||
;
|
||||
|
||||
void
|
||||
#gen true
|
||||
# 1 "data/identifier-expansion.2.cpp"
|
||||
test
|
||||
#gen false
|
||||
# 6 "data/identifier-expansion.2.cpp"
|
||||
();
|
@@ -0,0 +1,14 @@
|
||||
#define FOR_EACH_INSTR(V) \
|
||||
V(ADD) \
|
||||
V(SUB)
|
||||
|
||||
#define DECLARE_INSTR(op) #op,
|
||||
#define DECLARE_OP_INSTR(op) op_##op,
|
||||
|
||||
enum op_code {
|
||||
FOR_EACH_INSTR(DECLARE_OP_INSTR)
|
||||
};
|
||||
|
||||
static const char *names[] = {
|
||||
FOR_EACH_INSTR(DECLARE_INSTR)
|
||||
};
|
@@ -0,0 +1,23 @@
|
||||
# 8 "data/identifier-expansion.3.cpp"
|
||||
enum op_code {
|
||||
#gen true
|
||||
# 6 "data/identifier-expansion.3.cpp"
|
||||
op_ADD, op_SUB,
|
||||
#gen false
|
||||
# 10 "data/identifier-expansion.3.cpp"
|
||||
};
|
||||
|
||||
static const char *names[] = {
|
||||
#gen true
|
||||
# 2 "data/identifier-expansion.3.cpp"
|
||||
"ADD"
|
||||
|
||||
|
||||
,
|
||||
# 3 "data/identifier-expansion.3.cpp"
|
||||
"SUB"
|
||||
|
||||
,
|
||||
#gen false
|
||||
# 14 "data/identifier-expansion.3.cpp"
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
#define foobar(a) a
|
||||
#define food foobar
|
||||
|
||||
void baz()
|
||||
{
|
||||
int aaa;
|
||||
food(aaa);
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
|
||||
void baz()
|
||||
{
|
||||
int aaa;
|
||||
aaa;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
#define FOOBAR
|
||||
|
||||
#ifdef FOO
|
||||
|
||||
class FOOBAR Zoo {
|
||||
};
|
||||
|
||||
#endif
|
@@ -0,0 +1 @@
|
||||
# 9 "data/identifier-expansion.5.cpp"
|
36
tests/auto/cplusplus/preprocessor/data/macro-test.cpp
Normal file
36
tests/auto/cplusplus/preprocessor/data/macro-test.cpp
Normal file
@@ -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
|
@@ -0,0 +1,7 @@
|
||||
# 7 "data/macro-test.cpp"
|
||||
void thisFunctionIsEnabled();
|
||||
# 21 "data/macro-test.cpp"
|
||||
void thisFunctionIsEnabled2();
|
||||
# 31 "data/macro-test.cpp"
|
||||
void thisFunctionIsEnabled3();
|
||||
# 37 "data/macro-test.cpp"
|
4
tests/auto/cplusplus/preprocessor/data/macro_expand.c
Normal file
4
tests/auto/cplusplus/preprocessor/data/macro_expand.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#define X() Y
|
||||
#define Y() X
|
||||
|
||||
A: X()()()
|
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
|
||||
A:
|
||||
#gen true
|
||||
# 1 "data/macro_expand.c"
|
||||
Y
|
||||
#gen false
|
||||
# 5 "data/macro_expand.c"
|
18
tests/auto/cplusplus/preprocessor/data/macro_pounder_fn.c
Normal file
18
tests/auto/cplusplus/preprocessor/data/macro_pounder_fn.c
Normal file
@@ -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)
|
||||
|
66
tests/auto/cplusplus/preprocessor/data/noPP.1.cpp
Normal file
66
tests/auto/cplusplus/preprocessor/data/noPP.1.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
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.
|
||||
|
5
tests/auto/cplusplus/preprocessor/data/recursive.1.cpp
Normal file
5
tests/auto/cplusplus/preprocessor/data/recursive.1.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#define a b
|
||||
#define b a
|
||||
|
||||
b
|
||||
a
|
@@ -0,0 +1,6 @@
|
||||
#gen true
|
||||
# 1 "data/recursive.1.cpp"
|
||||
b
|
||||
a
|
||||
#gen false
|
||||
# 6 "data/recursive.1.cpp"
|
10
tests/auto/cplusplus/preprocessor/data/reserved.1.cpp
Normal file
10
tests/auto/cplusplus/preprocessor/data/reserved.1.cpp
Normal file
@@ -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;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
# 5 "data/reserved.1.cpp"
|
||||
int f() {
|
||||
foreach (QString &s, QStringList()) {
|
||||
doSomething();
|
||||
}
|
||||
return 1;
|
||||
}
|
@@ -1,3 +1,13 @@
|
||||
include(../../qttest.pri)
|
||||
include(../shared/shared.pri)
|
||||
SOURCES += tst_preprocessor.cpp
|
||||
|
||||
OTHER_FILES = \
|
||||
data/noPP.1.cpp data/noPP.1.errors.txt \
|
||||
data/identifier-expansion.1.cpp data/identifier-expansion.1.out.cpp data/identifier-expansion.1.errors.txt \
|
||||
data/identifier-expansion.2.cpp data/identifier-expansion.2.out.cpp data/identifier-expansion.2.errors.txt \
|
||||
data/identifier-expansion.3.cpp data/identifier-expansion.3.out.cpp data/identifier-expansion.3.errors.txt \
|
||||
data/identifier-expansion.4.cpp data/identifier-expansion.4.out.cpp data/identifier-expansion.4.errors.txt \
|
||||
data/reserved.1.cpp data/reserved.1.out.cpp data/reserved.1.errors.txt \
|
||||
data/macro_expand.c data/macro_expand.out.c data/macro_expand.errors.txt \
|
||||
data/empty-macro.cpp data/empty-macro.out.cpp
|
||||
|
@@ -36,19 +36,191 @@
|
||||
//TESTED_COMPONENT=src/libs/cplusplus
|
||||
using namespace CPlusPlus;
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
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*/) {}
|
||||
|
||||
virtual void passedMacroDefinitionCheck(unsigned /*offset*/, const Macro &/*macro*/) {}
|
||||
virtual void failedMacroDefinitionCheck(unsigned /*offset*/, const QByteArray &/*name*/) {}
|
||||
|
||||
virtual void startExpandingMacro(unsigned /*offset*/,
|
||||
const Macro &/*macro*/,
|
||||
const QByteArray &/*originalText*/,
|
||||
const QVector<MacroArgumentReference> &/*actuals*/
|
||||
= QVector<MacroArgumentReference>()) {}
|
||||
|
||||
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(QString &includedFileName, IncludeType mode,
|
||||
unsigned /*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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
QByteArray src = loadSource(fileName);
|
||||
QVERIFY(!src.isEmpty());
|
||||
|
||||
m_pp.preprocess(fileName, src, m_output, false, true, false);
|
||||
}
|
||||
|
||||
QList<Block> skippedBlocks() const
|
||||
{ return m_skippedBlocks; }
|
||||
|
||||
private:
|
||||
Environment *m_env;
|
||||
QByteArray *m_output;
|
||||
Preprocessor m_pp;
|
||||
QList<QDir> m_includePaths;
|
||||
unsigned m_includeDepth;
|
||||
QList<Block> m_skippedBlocks;
|
||||
};
|
||||
|
||||
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*/) {
|
||||
//### TODO: hook up errors
|
||||
QByteArray output;
|
||||
Environment env;
|
||||
MockClient client(&env, &output);
|
||||
client.sourceNeeded("data/" + fileName);
|
||||
return output;
|
||||
}
|
||||
|
||||
private /* not corrected yet */:
|
||||
void macro_definition_lineno();
|
||||
|
||||
private slots:
|
||||
void va_args();
|
||||
void named_va_args();
|
||||
void first_empty_macro_arg();
|
||||
void param_expanding_as_multiple_params();
|
||||
void macro_definition_lineno();
|
||||
void invalid_param_count();
|
||||
void unfinished_function_like_macro_call();
|
||||
void nasty_macro_expansion();
|
||||
void tstst();
|
||||
void test_file_builtin();
|
||||
|
||||
void blockSkipping();
|
||||
|
||||
void comparisons_data();
|
||||
void comparisons();
|
||||
};
|
||||
|
||||
void tst_Preprocessor::va_args()
|
||||
@@ -58,14 +230,18 @@ void tst_Preprocessor::va_args()
|
||||
|
||||
Preprocessor preprocess(client, &env);
|
||||
QByteArray preprocessed = preprocess(QLatin1String("<stdin>"),
|
||||
QByteArray("\n#define foo(...) int f(__VA_ARGS__);"
|
||||
QByteArray("#define foo(...) int f(__VA_ARGS__);\n"
|
||||
"\nfoo( )\n"
|
||||
"\nfoo(int a)\n"
|
||||
"\nfoo(int a,int b)\n"));
|
||||
"\nfoo(int a,int b)\n"),
|
||||
true,
|
||||
false);
|
||||
|
||||
preprocessed = preprocessed.simplified();
|
||||
// DUMP_OUTPUT(preprocessed);
|
||||
QVERIFY(preprocessed.contains("int f();"));
|
||||
QVERIFY(preprocessed.contains("int f(int a);"));
|
||||
QVERIFY(preprocessed.contains("int f(int a,int b);"));
|
||||
QVERIFY(preprocessed.contains("int f( int a );"));
|
||||
QVERIFY(preprocessed.contains("int f( int a, int b );"));
|
||||
}
|
||||
|
||||
void tst_Preprocessor::named_va_args()
|
||||
@@ -78,11 +254,13 @@ void tst_Preprocessor::named_va_args()
|
||||
QByteArray("\n#define foo(ARGS...) int f(ARGS);"
|
||||
"\nfoo( )\n"
|
||||
"\nfoo(int a)\n"
|
||||
"\nfoo(int a,int b)\n"));
|
||||
"\nfoo(int a,int b)\n"),
|
||||
true, false);
|
||||
|
||||
preprocessed = preprocessed.simplified();
|
||||
QVERIFY(preprocessed.contains("int f();"));
|
||||
QVERIFY(preprocessed.contains("int f(int a);"));
|
||||
QVERIFY(preprocessed.contains("int f(int a,int b);"));
|
||||
QVERIFY(preprocessed.contains("int f( int a );"));
|
||||
QVERIFY(preprocessed.contains("int f( int a, int b );"));
|
||||
}
|
||||
|
||||
void tst_Preprocessor::first_empty_macro_arg()
|
||||
@@ -95,24 +273,30 @@ void tst_Preprocessor::first_empty_macro_arg()
|
||||
QByteArray("\n#define foo(a,b) a int b;"
|
||||
"\nfoo(const,cVal)\n"
|
||||
"\nfoo(,Val)\n"
|
||||
"\nfoo( ,Val2)\n"));
|
||||
"\nfoo( ,Val2)\n"),
|
||||
true, false);
|
||||
|
||||
QVERIFY(preprocessed.contains("const int cVal;"));
|
||||
QVERIFY(preprocessed.contains("int Val;"));
|
||||
QVERIFY(preprocessed.contains("int Val2;"));
|
||||
preprocessed = preprocessed.simplified();
|
||||
// DUMP_OUTPUT(preprocessed);
|
||||
QVERIFY(preprocessed.contains("const int cVal ;"));
|
||||
QVERIFY(preprocessed.contains("int Val ;"));
|
||||
QVERIFY(preprocessed.contains("int Val2 ;"));
|
||||
}
|
||||
|
||||
void tst_Preprocessor::param_expanding_as_multiple_params()
|
||||
void tst_Preprocessor::invalid_param_count()
|
||||
{
|
||||
Client *client = 0; // no client.
|
||||
Environment env;
|
||||
|
||||
Preprocessor preprocess(client, &env);
|
||||
// The following is illegal, but shouldn't crash the preprocessor.
|
||||
// GCC says: 3:14: error: macro "foo" requires 2 arguments, but only 1 given
|
||||
QByteArray preprocessed = preprocess(QLatin1String("<stdin>"),
|
||||
QByteArray("\n#define foo(a,b) int f(a,b);"
|
||||
"\n#define ARGS(t) t a,t b"
|
||||
"\nfoo(ARGS(int))"));
|
||||
QVERIFY(preprocessed.contains("int f(int a,int b);"));
|
||||
"\nfoo(ARGS(int))"),
|
||||
true, false);
|
||||
// do not verify the output: it's illegal, so anything might be outputted.
|
||||
}
|
||||
|
||||
void tst_Preprocessor::macro_definition_lineno()
|
||||
@@ -158,8 +342,8 @@ void tst_Preprocessor::unfinished_function_like_macro_call()
|
||||
QByteArray preprocessed = preprocess(QLatin1String("<stdin>"),
|
||||
QByteArray("\n#define foo(a,b) a + b"
|
||||
"\nfoo(1, 2\n"));
|
||||
|
||||
QCOMPARE(preprocessed.trimmed(), QByteArray("foo"));
|
||||
QByteArray expected__("\n\n 1\n#gen true\n# 2 \"<stdin>\"\n+\n#gen false\n# 3 \"<stdin>\"\n 2\n");
|
||||
QCOMPARE(preprocessed, expected__);
|
||||
}
|
||||
|
||||
void tst_Preprocessor::nasty_macro_expansion()
|
||||
@@ -228,17 +412,115 @@ void tst_Preprocessor::tstst()
|
||||
"namespace std _GLIBCXX_VISIBILITY(default) {\n"
|
||||
"}\n"
|
||||
));
|
||||
const QByteArray result =
|
||||
"namespace std \n"
|
||||
const QByteArray result____ ="\n\n"
|
||||
"namespace std\n"
|
||||
"#gen true\n"
|
||||
"# 3 \"<stdin>\"\n"
|
||||
" __attribute__ ((__visibility__ (\"default\")))\n"
|
||||
"# 2 \"<stdin>\"\n"
|
||||
"__attribute__ ((__visibility__ (\n"
|
||||
"\"default\"\n"
|
||||
"# 2 \"<stdin>\"\n"
|
||||
")))\n"
|
||||
"#gen false\n"
|
||||
"# 3 \"<stdin>\"\n"
|
||||
" {\n"
|
||||
"}";
|
||||
"}\n";
|
||||
|
||||
QVERIFY(preprocessed.contains(result));
|
||||
QCOMPARE(preprocessed, result____);
|
||||
}
|
||||
|
||||
void tst_Preprocessor::test_file_builtin()
|
||||
{
|
||||
Client *client = 0; // no client.
|
||||
Environment env;
|
||||
|
||||
Preprocessor preprocess(client, &env);
|
||||
QByteArray preprocessed = preprocess(
|
||||
QLatin1String("some-file.c"),
|
||||
QByteArray("const char *f = __FILE__\n"
|
||||
));
|
||||
const QByteArray result____ =
|
||||
"const char *f =\n"
|
||||
"#gen true\n"
|
||||
"# 1 \"some-file.c\"\n"
|
||||
"\"some-file.c\"\n"
|
||||
"#gen false\n"
|
||||
"# 2 \"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("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-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" << "";
|
||||
}
|
||||
|
||||
void tst_Preprocessor::comparisons()
|
||||
{
|
||||
QFETCH(QString, infile);
|
||||
QFETCH(QString, outfile);
|
||||
QFETCH(QString, errorfile);
|
||||
|
||||
QByteArray errors;
|
||||
QByteArray preprocessed = preprocess(infile, &errors);
|
||||
|
||||
// DUMP_OUTPUT(preprocessed);
|
||||
|
||||
if (!outfile.isEmpty()) {
|
||||
QByteArray output____ = loadSource("data/"+outfile); // these weird underscores are here to make the name as long as "preprocessed", so the QCOMPARE error messages are nicely aligned.
|
||||
// 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(
|
||||
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"
|
||||
));
|
||||
|
||||
QList<MockClient::Block> blocks = client.skippedBlocks();
|
||||
QCOMPARE(blocks.size(), 1);
|
||||
MockClient::Block b = blocks.at(0);
|
||||
QCOMPARE(b.start, 6U);
|
||||
QCOMPARE(b.end, 34U);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_Preprocessor)
|
||||
|
Reference in New Issue
Block a user