1
#include <QtTest/QtTest>
8
#include "rpp/preprocessor.h"
12
#include "parsesession.h"
13
#include "commentformatter.h"
15
#include "testconfig.h"
18
#include <QDataStream>
22
#include <rpp/chartools.h>
23
#include <rpp/pp-engine.h>
26
bool hasKind(AST*, AST::NODE_KIND);
27
AST* getAST(AST*, AST::NODE_KIND, int num = 0);
29
class TestParser : public QObject
47
void testSymbolTable()
50
table.findOrInsert("Ideal::MainWindow", sizeof("Ideal::MainWindow"));
51
table.findOrInsert("QMainWindow", sizeof("QMainWindow"));
52
table.findOrInsert("KXmlGuiWindow", sizeof("KXmlGuiWindow"));
53
QCOMPARE(table.count(), size_t(3));
54
const NameSymbol *s = table.findOrInsert("QMainWindow", sizeof("QMainWindow"));
55
QCOMPARE(QString(s->data), QString("QMainWindow"));
56
QCOMPARE(table.count(), size_t(3));
60
// void testControlContexts()
63
// const NameSymbol *n1 = control.findOrInsertName("a", 1);
64
// int *type1 = new int(1); // don't care much about types
65
// control.declare(n1, (Type*)type1);
67
// control.pushContext();
68
// int *type2 = new int(2);
69
// const NameSymbol *n2 = control.findOrInsertName("b", 1);
70
// control.declare(n2, (Type*)type2);
72
// QCOMPARE(control.lookupType(n1), (Type*)type1);
73
// QCOMPARE(control.lookupType(n2), (Type*)type2);
75
// control.popContext();
76
// QCOMPARE(control.lookupType(n1), (Type*)type1);
77
// QCOMPARE(control.lookupType(n2), (Type*)0);
82
QCOMPARE(token_name(Token_EOF), "eof");
83
QCOMPARE(token_name('a'), "a");
84
QCOMPARE(token_name(Token_delete), "delete");
90
// QByteArray code("#include <foo.h>");
91
// TokenStream token_stream;
92
// LocationTable location_table;
93
// LocationTable line_table;
96
// Lexer lexer(token_stream, location_table, line_table, &control);
97
// lexer.tokenize(code, code.size()+1);
98
// QCOMPARE(control.problem(0).message(), QString("expected end of line"));
100
// QByteArray code2("class Foo { int foo() {} }; ");
101
// lexer.tokenize(code2, code2.size()+1);
102
// QCOMPARE(control.problemCount(), 1); //we still have the old problem in the list
107
QByteArray clazz("struct A { int i; A() : i(5) { } virtual void test() = 0; };");
109
TranslationUnitAST* ast = parse(clazz, &mem_pool);
111
QVERIFY(ast->declarations != 0);
114
void testTemplateArguments()
116
QByteArray templatetest("template <int N, int M> struct SeriesAdder{ enum { value = N + SeriesAdder< 0 >::value }; };");
118
TranslationUnitAST* ast = parse(templatetest, &mem_pool);
120
QVERIFY(ast->declarations != 0);
121
for (int i = 0; i < control.problems().count(); i++)
123
QVERIFY(control.problems().at(i)->description() == "Unexpected end of file");
127
void testManyComparisons()
131
QByteArray clazz("void test() { if(val < f && val < val1 && val < val2 && val < val3 ){ } }");
133
TranslationUnitAST* ast = parse(clazz, &mem_pool);
135
QVERIFY(ast->declarations != 0);
136
dumper.dump(ast, lastSession->token_stream);
139
QByteArray clazz("void test() { if(val < f && val < val1 && val < val2 && val < val3 && val < val4 && val < val5 && val < val6 && val < val7 && val < val8 && val < val9 && val < val10 && val < val11 && val < val12 && val < val13 && val < val14 && val < val15 && val < val16 && val < val17 && val < val18 && val < val19 && val < val20 && val < val21 && val < val22 && val < val23 && val < val24 && val < val25 && val < val26){ } }");
141
TranslationUnitAST* ast = parse(clazz, &mem_pool);
143
QVERIFY(ast->declarations != 0);
147
void testParserFail()
149
QByteArray stuff("foo bar !!! nothing that really looks like valid c++ code");
151
TranslationUnitAST *ast = parse(stuff, &mem_pool);
152
QVERIFY(ast->declarations == 0);
153
QVERIFY(control.problems().count() > 3);
156
void testPartialParseFail() {
158
QByteArray method("struct C { Something invalid is here };");
160
TranslationUnitAST* ast = parse(method, &mem_pool);
162
QVERIFY(hasKind(ast, AST::Kind_ClassSpecifier));
165
QByteArray method("void test() { Something invalid is here };");
167
TranslationUnitAST* ast = parse(method, &mem_pool);
169
QVERIFY(hasKind(ast, AST::Kind_FunctionDefinition));
172
QByteArray method("void test() { {Something invalid is here };");
174
TranslationUnitAST* ast = parse(method, &mem_pool);
176
QVERIFY(hasKind(ast, AST::Kind_FunctionDefinition));
177
QVERIFY(ast->hadMissingCompoundTokens);
180
QByteArray method("void test() { case:{};");
182
TranslationUnitAST* ast = parse(method, &mem_pool);
184
QVERIFY(hasKind(ast, AST::Kind_FunctionDefinition));
185
QVERIFY(ast->hadMissingCompoundTokens);
189
void testParseMethod()
191
QByteArray method("void A::test() { }");
193
TranslationUnitAST* ast = parse(method, &mem_pool);
195
QVERIFY(hasKind(ast, AST::Kind_FunctionDefinition));
199
// void testMethodArgs()
201
// QByteArray method("int A::test(int primitive, B* pointer) { return primitive; }");
203
// Parser parser(&control);
204
// TranslationUnitAST* ast = parser.parse(method.constData(),
205
// method.size() + 1, &mem_pool);
207
// SimpleTypeSpecifierAST* retType = static_cast<SimpleTypeSpecifierAST*>
208
// (getAST(ast, AST::Kind_SimpleTypeSpecifier));
209
// QCOMPARE((TOKEN_KIND)parser.token_stream.kind(retType->start_token),
213
// ParameterDeclarationAST* param = static_cast<ParameterDeclarationAST*>
214
// (getAST(ast, AST::Kind_ParameterDeclaration));
215
// SimpleTypeSpecifierAST* paramType = static_cast<SimpleTypeSpecifierAST*>
216
// (getAST(param, AST::Kind_SimpleTypeSpecifier));
217
// QCOMPARE((TOKEN_KIND)parser.token_stream.kind(paramType->start_token),
219
// UnqualifiedNameAST* argName = static_cast<UnqualifiedNameAST*>
220
// (getAST(param, AST::Kind_UnqualifiedName));
221
// QCOMPARE(parser.token_stream.symbol(argName->id)->as_string(),
222
// QString("primitive"));
225
// param = static_cast<ParameterDeclarationAST*>
226
// (getAST(ast, AST::Kind_ParameterDeclaration, 1));
227
// UnqualifiedNameAST* argType = static_cast<UnqualifiedNameAST*>
228
// (getAST(param, AST::Kind_UnqualifiedName));
229
// QCOMPARE(parser.token_stream.symbol(argType->id)->as_string(),
232
// // pointer operator
233
// QVERIFY(hasKind(param, AST::Kind_PtrOperator));
235
// argName = static_cast<UnqualifiedNameAST*>
236
// (getAST(param, AST::Kind_UnqualifiedName, 1));
237
// QCOMPARE(parser.token_stream.symbol(argName->id)->as_string(),
238
// QString("pointer"));
242
void testForStatements()
244
QByteArray method("void A::t() { for (int i = 0; i < 10; i++) { ; }}");
246
TranslationUnitAST* ast = parse(method, &mem_pool);
249
QVERIFY(hasKind(ast, AST::Kind_ForStatement));
250
QVERIFY(hasKind(ast, AST::Kind_Condition));
251
QVERIFY(hasKind(ast, AST::Kind_IncrDecrExpression));
252
QVERIFY(hasKind(ast, AST::Kind_SimpleDeclaration));
254
QByteArray emptyFor("void A::t() { for (;;) { } }");
255
ast = parse(emptyFor, &mem_pool);
257
QVERIFY(hasKind(ast, AST::Kind_ForStatement));
258
QVERIFY(!hasKind(ast, AST::Kind_Condition));
259
QVERIFY(!hasKind(ast, AST::Kind_SimpleDeclaration));
262
void testIfStatements()
264
QByteArray method("void A::t() { if (1 < 2) { } }");
266
TranslationUnitAST* ast = parse(method, &mem_pool);
267
QVERIFY(hasKind(ast, AST::Kind_Condition));
268
QVERIFY(hasKind(ast, AST::Kind_BinaryExpression));
273
QByteArray method("//TranslationUnitComment\n//Hello\nint A; //behind\n /*between*/\n /*Hello2*/\n class B{}; //behind\n//Hello3\n //beforeTest\nvoid test(); //testBehind");
275
TranslationUnitAST* ast = parse(method, &mem_pool);
277
QCOMPARE(CommentFormatter::formatComment(ast->comments, lastSession), QByteArray("TranslationUnitComment")); //The comments were merged
279
const ListNode<DeclarationAST*>* it = ast->declarations;
283
QCOMPARE(CommentFormatter::formatComment(it->element->comments, lastSession), QByteArray("Hello\n(behind)"));
287
QCOMPARE(CommentFormatter::formatComment(it->element->comments, lastSession), QByteArray("between\nHello2\n(behind)"));
291
QCOMPARE(CommentFormatter::formatComment(it->element->comments, lastSession), QByteArray("Hello3\nbeforeTest\n(testBehind)"));
296
QByteArray method("enum Enum\n {//enumerator1Comment\nenumerator1, //enumerator1BehindComment\n /*enumerator2Comment*/ enumerator2 /*enumerator2BehindComment*/};");
298
TranslationUnitAST* ast = parse(method, &mem_pool);
300
const ListNode<DeclarationAST*>* it = ast->declarations;
304
const SimpleDeclarationAST* simpleDecl = static_cast<const SimpleDeclarationAST*>(it->element);
307
const EnumSpecifierAST* enumSpec = (const EnumSpecifierAST*)simpleDecl->type_specifier;
310
const ListNode<EnumeratorAST*> *enumerator = enumSpec->enumerators;
312
enumerator = enumerator->next;
315
QCOMPARE(CommentFormatter::formatComment(enumerator->element->comments, lastSession), QByteArray("enumerator1Comment\n(enumerator1BehindComment)"));
317
enumerator = enumerator->next;
320
QCOMPARE(CommentFormatter::formatComment(enumerator->element->comments, lastSession), QByteArray("enumerator2Comment\n(enumerator2BehindComment)"));
325
QByteArray method("class Class{\n//Comment\n int val;};");
327
TranslationUnitAST* ast = parse(method, &mem_pool);
329
const ListNode<DeclarationAST*>* it = ast->declarations;
333
const SimpleDeclarationAST* simpleDecl = static_cast<const SimpleDeclarationAST*>(it->element);
336
const ClassSpecifierAST* classSpec = (const ClassSpecifierAST*)simpleDecl->type_specifier;
339
const ListNode<DeclarationAST*> *members = classSpec->member_specs;
341
members = members->next;
344
QCOMPARE(CommentFormatter::formatComment(members->element->comments, lastSession), QByteArray("Comment"));
349
QByteArray method("//TranslationUnitComment\n//Comment\ntemplate<class C> class Class{};");
351
TranslationUnitAST* ast = parse(method, &mem_pool);
353
const ListNode<DeclarationAST*>* it = ast->declarations;
357
const TemplateDeclarationAST* templDecl = static_cast<const TemplateDeclarationAST*>(it->element);
359
QVERIFY(templDecl->declaration);
361
//QCOMPARE(CommentFormatter::formatComment(templDecl->declaration->comments, lastSession), QString("Comment"));
364
QString preprocess(const QString& contents) {
365
rpp::Preprocessor preprocessor;
366
rpp::pp pp(&preprocessor);
367
return QString::fromUtf8(stringFromContents(pp.processFile("anonymous", contents.toUtf8())));
370
void testPreprocessor() {
371
rpp::Preprocessor preprocessor;
372
//QCOMPARE(preprocess("#define TEST (1L<<10)\nTEST").trimmed(), QString("(1L<<10)"));
373
QCOMPARE(preprocess("#define TEST //Comment\nTEST 1").trimmed(), QString("1")); //Comments are not included in macros
374
QCOMPARE(preprocess("#define TEST /*Comment\n*/\nTEST 1").trimmed(), QString("1")); //Comments are not included in macros
378
void testStringConcatenation()
380
rpp::Preprocessor preprocessor;
381
QCOMPARE(preprocess("Hello##You"), QString("HelloYou"));
382
QCOMPARE(preprocess("#define CONCAT(Var1, Var2) Var1##Var2 Var2##Var1\nCONCAT( Hello , You )").simplified(), QString("\nHelloYou YouHello").simplified());
387
QByteArray method("bool i = (small < big || big > small);");
389
TranslationUnitAST* ast = parse(method, &mem_pool);
390
dumper.dump(ast, lastSession->token_stream);
391
///@todo make this work, it should yield something like TranslationUnit -> SimpleDeclaration -> InitDeclarator -> BinaryExpression
394
void testNonTemplateDeclaration()
397
QByteArray templateMethod("template <int> class a {}; int main() { const int b = 1; const int c = 2; a<b|c> d; }");
399
TranslationUnitAST* ast = parse(templateMethod, &mem_pool);
400
dumper.dump(ast, lastSession->token_stream);
403
//int a, b, c, d; bool e;
404
QByteArray declaration("void expression() { if (a < b || c > d) {} }");
406
TranslationUnitAST* ast = parse(declaration, &mem_pool);
407
dumper.dump(ast, lastSession->token_stream);
410
void testInitListTrailingComma()
412
//see bug https://bugs.kde.org/show_bug.cgi?id=233328
414
QByteArray code("const int foo [] = {1,};");
416
TranslationUnitAST* ast = parse(code, &memPool);
417
dumper.dump(ast, lastSession->token_stream);
419
QCOMPARE(ast->declarations->count(), 1);
420
SimpleDeclarationAST* simpleDecl = reinterpret_cast<SimpleDeclarationAST*>(ast->declarations->at(0)->element);
423
QCOMPARE(simpleDecl->init_declarators->count(), 1);
426
/*void testParseFile()
428
QFile file(TEST_FILE);
429
QVERIFY(file.open(QFile::ReadOnly));
430
QByteArray contents = file.readAll();
433
Parser parser(&control);
434
ParseSession session;
435
session.setContents(contents);
436
TranslationUnitAST* ast = parser.parse(&session);
438
QVERIFY(ast->declarations != 0);
442
ParseSession* lastSession;
444
TranslationUnitAST* parse(const QByteArray& unit, pool* mem_pool)
446
Parser parser(&control);
447
lastSession = new ParseSession();
448
lastSession->setContentsAndGenerateLocationTable(tokenizeFromByteArray(unit));
449
return parser.parse(lastSession);
456
struct HasKindVisitor : protected DefaultVisitor
463
HasKindVisitor(AST::NODE_KIND kind, int num = 0)
464
: kind(kind), ast(0), num(num)
473
void visit(AST* node)
476
if (node->kind == kind && --num < 0) {
480
DefaultVisitor::visit(node);
486
bool hasKind(AST* ast, AST::NODE_KIND kind)
488
HasKindVisitor visitor(kind);
490
return visitor.hasKind();
493
AST* getAST(AST* ast, AST::NODE_KIND kind, int num)
495
HasKindVisitor visitor(kind, num);
501
#include "test_parser.moc"
503
QTEST_MAIN(TestParser)