diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp new file mode 100644 index 000000000..095ce045e --- /dev/null +++ b/test/testgarbage.cpp @@ -0,0 +1,247 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testsuite.h" +#include "tokenize.h" +#include "token.h" +#include "settings.h" + +extern std::ostringstream errout; + + +class TestGarbage : public TestFixture { +public: + TestGarbage() : TestFixture("TestGarbage") { + } + +private: + + void run() { + // don't freak out when the syntax is wrong + TEST_CASE(wrong_syntax1); + TEST_CASE(wrong_syntax2); + TEST_CASE(wrong_syntax3); // #3544 + TEST_CASE(wrong_syntax4); // #3618 + TEST_CASE(wrong_syntax_if_macro); // #2518 - if MACRO() + TEST_CASE(wrong_syntax_class_x_y); // #3585 - class x y { }; + TEST_CASE(syntax_case_default); + TEST_CASE(garbageCode1); + TEST_CASE(garbageCode2); // #4300 + TEST_CASE(garbageCode3); // #4869 + TEST_CASE(garbageCode4); // #4887 + TEST_CASE(garbageCode5); // #5168 + TEST_CASE(garbageCode6); // #5214 + TEST_CASE(garbageCode7); + TEST_CASE(garbageCode8); // #5511 + TEST_CASE(garbageCode9); // #5604 + TEST_CASE(garbageCode10); // #6127 + + TEST_CASE(astGarbage); + } + + std::string checkCode(const char code[], const char filename[] = "test.cpp") { + errout.str(""); + + Settings settings; + settings.debugwarnings = true; + + // tokenize.. + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, filename); + tokenizer.simplifyTokenList2(); + + // TODO: Run checks + + return tokenizer.tokens()->stringifyList(false, false, false, true, false, 0, 0); + } + + void wrong_syntax1() { + { + const char code[] ="TR(kvmpio, PROTO(int rw), ARGS(rw), TP_(aa->rw;))"; + ASSERT_EQUALS("TR ( kvmpio , PROTO ( int rw ) , ARGS ( rw ) , TP_ ( aa . rw ; ) )", checkCode(code)); + ASSERT_EQUALS("", errout.str()); + } + + { + const char code[] ="struct A { template struct { }; };"; + ASSERT_THROW(checkCode(code), InternalError); + } + + { + const char code[] ="enum ABC { A,B, typedef enum { C } };"; + ASSERT_THROW(checkCode(code), InternalError); + } + + { + // #3314 - don't report syntax error. + const char code[] ="struct A { typedef B::C (A::*f)(); };"; + checkCode(code); + ASSERT_EQUALS("[test.cpp:1]: (debug) Failed to parse 'typedef B :: C ( A :: * f ) ( ) ;'. The checking continues anyway.\n", errout.str()); + } + } + + void wrong_syntax2() { // #3504 + const char code[] = "void f() {\n" + " X x;\n" + " Y y;\n" + "}\n" + "\n" + "void G( template class (j) ) {}"; + + // don't segfault.. + checkCode(code); + } + + void wrong_syntax3() { // #3544 + const char code[] = "X #define\n" + "{\n" + " (\n" + " for( #endif typedef typedef cb[N] )\n" + " ca[N]; = cb[i]\n" + " )\n" + "}"; + + Settings settings; + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + try { + tokenizer.tokenize(istr, "test.cpp"); + assertThrowFail(__FILE__, __LINE__); + } catch (InternalError& e) { + ASSERT_EQUALS("Analysis failed. If the code is valid then please report this failure.", e.errorMessage); + ASSERT_EQUALS("cppcheckError", e.id); + ASSERT_EQUALS(5, e.token->linenr()); + } + } + + void wrong_syntax4() { // #3618 + const char code[] = "typedef void (x) (int); return x&"; + + ASSERT_THROW(checkCode(code), InternalError); + } + + void wrong_syntax_if_macro() { + // #2518 #4171 + ASSERT_THROW(checkCode("void f() { if MACRO(); }"), InternalError); + + // #4668 - note there is no semicolon after MACRO() + ASSERT_THROW(checkCode("void f() { if (x) MACRO() {} }"), InternalError); + + // #4810 - note there is no semicolon after MACRO() + ASSERT_THROW(checkCode("void f() { if (x) MACRO() else ; }"), InternalError); + } + + void wrong_syntax_class_x_y() { + // #3585 + const char code[] = "class x y { };"; + + errout.str(""); + + Settings settings; + settings.addEnabled("information"); + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.c"); + tokenizer.simplifyTokenList2(); + + ASSERT_EQUALS("[test.c:1]: (information) The code 'class x y {' is not handled. You can use -I or --include to add handling of this code.\n", errout.str()); + } + + void syntax_case_default() { + ASSERT_THROW(checkCode("void f() {switch (n) { case: z(); break;}}"), InternalError); + + ASSERT_THROW(checkCode("void f() {switch (n) { case;: z(); break;}}"), InternalError); + + ASSERT_THROW(checkCode("void f() {switch (n) { case {}: z(); break;}}"), InternalError); + + ASSERT_THROW(checkCode("void f() {switch (n) { case 0?{1}:{2} : z(); break;}}"), InternalError); + + ASSERT_THROW(checkCode("void f() {switch (n) { case 0?1;:{2} : z(); break;}}"), InternalError); + + ASSERT_THROW(checkCode("void f() {switch (n) { case 0?(1?{3:4}):2 : z(); break;}}"), InternalError); + + //ticket #4234 + ASSERT_THROW(checkCode("( ) { switch break ; { switch ( x ) { case } y break ; : } }"), InternalError); + + //ticket #4267 + ASSERT_THROW(checkCode("f ( ) { switch break; { switch ( x ) { case } case break; -6: ( ) ; } }"), InternalError); + } + + void garbageCode1() { + checkCode("struct x foo_t; foo_t typedef y;"); + } + + void garbageCode2() { //#4300 (segmentation fault) + ASSERT_THROW(checkCode("enum { D = 1 struct { } ; } s.b = D;"), InternalError); + } + + void garbageCode3() { //#4849 (segmentation fault in Tokenizer::simplifyStructDecl (invalid code)) + ASSERT_THROW(checkCode("enum { D = 2 s ; struct y { x } ; } { s.a = C ; s.b = D ; }"), InternalError); + } + + void garbageCode4() { // #4887 + ASSERT_THROW(checkCode("void f ( ) { = a ; if ( 1 ) if = ( 0 ) ; }"), InternalError); + } + + void garbageCode5() { // #5168 + checkCode("( asm : ; void : );"); + } + + void garbageCode6() { // #5214 + checkCode("int b = ( 0 ? ? ) 1 : 0 ;"); + checkCode("int a = int b = ( 0 ? ? ) 1 : 0 ;"); + } + + void garbageCode7() { + ASSERT_THROW(checkCode("1 (int j) { return return (c) * sizeof } y[1];"), InternalError); + checkCode("foo(Args&&...) fn void = { } auto template { { e = T::error }; };\n" + "ScopedEnum1 se1; { enum class E : T { e = 0 = e ScopedEnum2 struct UnscopedEnum3 { T{ e = 4 }; };\n" + "arr[(int) E::e]; }; UnscopedEnum3 e2 = f()\n" + "{ { e = e1; T::error } int test1 ue2; g() { enum class E { e = T::error }; return E::e; } int test2 = } \n" + "namespace UnscopedEnum { template struct UnscopedEnum1 { E{ e = T::error }; }; UnscopedEnum1 { enum E : { e = 0 }; };\n" + "UnscopedEnum2 ue3; template struct UnscopedEnum3 { enum { }; }; int arr[E::e]; };\n" + "UnscopedEnum3 namespace template int f() { enum E { e }; T::error }; return (int) E(); } int test1 int g() { enum E { e = E };\n" + "E::e; } int test2 = g(); }"), InternalError); + } + + void garbageCode9() { + ASSERT_THROW(checkCode("enum { e = { } } ( ) { { enum { } } } { e } "), InternalError); + } + + void garbageCode10() { // #6127 + checkCode("for( rl=reslist; rl!=NULL; rl=rl->next )"); + } + + void astGarbage() { + checkCode("--"); // don't crash + + checkCode("N 1024 float a[N], b[N + 3], c[N]; void N; (void) i;\n" + "int #define for (i = avx_test i < c[i]; i++)\n" + "b[i + 3] = a[i] * {}"); // Don't hang (#5787) + + checkCode("START_SECTION([EXTRA](bool isValid(const String &filename)))"); // Don't crash (#5991) + } +}; + +REGISTER_TEST(TestGarbage) diff --git a/test/testrunner.vcxproj b/test/testrunner.vcxproj index 672017f98..17ebba124 100644 --- a/test/testrunner.vcxproj +++ b/test/testrunner.vcxproj @@ -45,6 +45,7 @@ + diff --git a/test/testrunner.vcxproj.filters b/test/testrunner.vcxproj.filters index e75af2a1d..4c0160783 100644 --- a/test/testrunner.vcxproj.filters +++ b/test/testrunner.vcxproj.filters @@ -199,6 +199,9 @@ Source Files + + Source Files + diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index a62d8dbc8..58da939a2 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -69,24 +69,7 @@ private: TEST_CASE(tokenize33); // #5780 Various crashes on valid template code TEST_CASE(tokenize34); // #6121 (crash upon invalid enum) - // don't freak out when the syntax is wrong - TEST_CASE(wrong_syntax1); - TEST_CASE(wrong_syntax2); - TEST_CASE(wrong_syntax3); // #3544 - TEST_CASE(wrong_syntax4); // #3618 - TEST_CASE(wrong_syntax_if_macro); // #2518 - if MACRO() - TEST_CASE(wrong_syntax_class_x_y); // #3585 - class x y { }; TEST_CASE(syntax_case_default); - TEST_CASE(garbageCode1); - TEST_CASE(garbageCode2); // #4300 - TEST_CASE(garbageCode3); // #4869 - TEST_CASE(garbageCode4); // #4887 - TEST_CASE(garbageCode5); // #5168 - TEST_CASE(garbageCode6); // #5214 - TEST_CASE(garbageCode7); - TEST_CASE(garbageCode8); // #5511 - TEST_CASE(garbageCode9); // #5604 - TEST_CASE(garbageCode10); // #6127 TEST_CASE(simplifyFileAndLineMacro); // tokenize "return - __LINE__;" TEST_CASE(foreach); // #3690 @@ -480,7 +463,6 @@ private: TEST_CASE(asttemplate); TEST_CASE(astcast); TEST_CASE(astlambda); - TEST_CASE(astGarbage); TEST_CASE(startOfExecutableScope); } @@ -801,208 +783,42 @@ private: ASSERT_THROW(tokenizeAndStringify(code, true), InternalError); } - void wrong_syntax1() { - { - const char code[] ="TR(kvmpio, PROTO(int rw), ARGS(rw), TP_(aa->rw;))"; - ASSERT_EQUALS("TR ( kvmpio , PROTO ( int rw ) , ARGS ( rw ) , TP_ ( aa . rw ; ) )", tokenizeAndStringify(code, true)); - ASSERT_EQUALS("", errout.str()); - } + void syntax_case_default() { // correct syntax + tokenizeAndStringify("void f() {switch (n) { case 0: z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - { - const char code[] ="struct A { template struct { }; };"; - ASSERT_THROW(tokenizeAndStringify(code, true), InternalError); - } + tokenizeAndStringify("void f() {switch (n) { case 0:; break;}}"); + ASSERT_EQUALS("", errout.str()); - { - const char code[] ="enum ABC { A,B, typedef enum { C } };"; - ASSERT_THROW(tokenizeAndStringify(code, true), InternalError); - } + tokenizeAndStringify("void f() {switch (n) { case 0?1:2 : z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - { - // #3314 - don't report syntax error. - const char code[] ="struct A { typedef B::C (A::*f)(); };"; - tokenizeAndStringify(code, true); - ASSERT_EQUALS("[test.cpp:1]: (debug) Failed to parse 'typedef B :: C ( A :: * f ) ( ) ;'. The checking continues anyway.\n", errout.str()); - } - } + tokenizeAndStringify("void f() {switch (n) { case 0?(1?3:4):2 : z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - void wrong_syntax2() { // #3504 - const char code[] = "void f() {\n" - " X x;\n" - " Y y;\n" - "}\n" - "\n" - "void G( template class (j) ) {}"; + //allow GCC '({ %var%|%num%|%bool% ; })' statement expression extension + tokenizeAndStringify("void f() {switch (n) { case 0?({0;}):1: z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - // don't segfault.. - tokenizeAndStringify(code); - } + //'b' can be or a macro or an undefined enum + tokenizeAndStringify("void f() {switch (n) { case b: z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - void wrong_syntax3() { // #3544 - const char code[] = "X #define\n" - "{\n" - " (\n" - " for( #endif typedef typedef cb[N] )\n" - " ca[N]; = cb[i]\n" - " )\n" - "}"; + //valid, when there's this declaration: 'constexpr int g() { return 2; }' + tokenizeAndStringify("void f() {switch (n) { case g(): z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - Settings settings; - Tokenizer tokenizer(&settings, this); - std::istringstream istr(code); - try { - tokenizer.tokenize(istr, "test.cpp"); - assertThrowFail(__FILE__, __LINE__); - } catch (InternalError& e) { - ASSERT_EQUALS("Analysis failed. If the code is valid then please report this failure.", e.errorMessage); - ASSERT_EQUALS("cppcheckError", e.id); - ASSERT_EQUALS(5, e.token->linenr()); - } - } + //valid, when there's also this declaration: 'constexpr int g[1] = {0};' + tokenizeAndStringify("void f() {switch (n) { case g[0]: z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - void wrong_syntax4() { // #3618 - const char code[] = "typedef void (x) (int); return x&"; + //valid, similar to above case + tokenizeAndStringify("void f() {switch (n) { case *g: z(); break;}}"); + ASSERT_EQUALS("", errout.str()); - ASSERT_THROW(tokenizeAndStringify(code), InternalError); - } - - void wrong_syntax_if_macro() { - // #2518 #4171 - ASSERT_THROW(tokenizeAndStringify("void f() { if MACRO(); }", false), InternalError); - - // #4668 - note there is no semicolon after MACRO() - ASSERT_THROW(tokenizeAndStringify("void f() { if (x) MACRO() {} }", false), InternalError); - - // #4810 - note there is no semicolon after MACRO() - ASSERT_THROW(tokenizeAndStringify("void f() { if (x) MACRO() else ; }", false), InternalError); - } - - void wrong_syntax_class_x_y() { - // #3585 - const char code[] = "class x y { };"; - - errout.str(""); - - Settings settings; - settings.addEnabled("information"); - Tokenizer tokenizer(&settings, this); - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.c"); - tokenizer.simplifyTokenList2(); - - ASSERT_EQUALS("[test.c:1]: (information) The code 'class x y {' is not handled. You can use -I or --include to add handling of this code.\n", errout.str()); - } - - void syntax_case_default() { - //correct syntax - { - tokenizeAndStringify("void f() {switch (n) { case 0: z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - tokenizeAndStringify("void f() {switch (n) { case 0:; break;}}"); - ASSERT_EQUALS("", errout.str()); - - tokenizeAndStringify("void f() {switch (n) { case 0?1:2 : z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - tokenizeAndStringify("void f() {switch (n) { case 0?(1?3:4):2 : z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - //allow GCC '({ %var%|%num%|%bool% ; })' statement expression extension - tokenizeAndStringify("void f() {switch (n) { case 0?({0;}):1: z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - //'b' can be or a macro or an undefined enum - tokenizeAndStringify("void f() {switch (n) { case b: z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - //valid, when there's this declaration: 'constexpr int g() { return 2; }' - tokenizeAndStringify("void f() {switch (n) { case g(): z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - //valid, when there's also this declaration: 'constexpr int g[1] = {0};' - tokenizeAndStringify("void f() {switch (n) { case g[0]: z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - //valid, similar to above case - tokenizeAndStringify("void f() {switch (n) { case *g: z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - - //valid, when 'x' and 'y' are constexpr. - tokenizeAndStringify("void f() {switch (n) { case sqrt(x+y): z(); break;}}"); - ASSERT_EQUALS("", errout.str()); - } - - //wrong syntax - { - ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case: z(); break;}}"), InternalError); - - ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case;: z(); break;}}"), InternalError); - - ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case {}: z(); break;}}"), InternalError); - - ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case 0?{1}:{2} : z(); break;}}"), InternalError); - - ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case 0?1;:{2} : z(); break;}}"), InternalError); - - ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case 0?(1?{3:4}):2 : z(); break;}}"), InternalError); - - //ticket #4234 - ASSERT_THROW(tokenizeAndStringify("( ) { switch break ; { switch ( x ) { case } y break ; : } }"), InternalError); - - //ticket #4267 - ASSERT_THROW(tokenizeAndStringify("f ( ) { switch break; { switch ( x ) { case } case break; -6: ( ) ; } }"), InternalError); - } - } - - void garbageCode1() { - tokenizeAndStringify("struct x foo_t; foo_t typedef y;"); - } - - void garbageCode2() { //#4300 (segmentation fault) - ASSERT_THROW(tokenizeAndStringify("enum { D = 1 struct { } ; } s.b = D;"), InternalError); - } - - void garbageCode3() { //#4849 (segmentation fault in Tokenizer::simplifyStructDecl (invalid code)) - ASSERT_THROW(tokenizeAndStringify("enum { D = 2 s ; struct y { x } ; } { s.a = C ; s.b = D ; }"), InternalError); - } - - void garbageCode4() { // #4887 - ASSERT_THROW(tokenizeAndStringify("void f ( ) { = a ; if ( 1 ) if = ( 0 ) ; }"), InternalError); - } - - void garbageCode5() { // #5168 - tokenizeAndStringify("( asm : ; void : );"); - } - - void garbageCode6() { // #5214 - tokenizeAndStringify("int b = ( 0 ? ? ) 1 : 0 ;", /*simplify=*/true); - tokenizeAndStringify("int a = int b = ( 0 ? ? ) 1 : 0 ;", /*simplify=*/true); - } - - void garbageCode7() { - ASSERT_THROW(tokenizeAndStringify("1 (int j) { return return (c) * sizeof } y[1];", /*simplify=*/true), InternalError); - tokenizeAndStringify("foo(Args&&...) fn void = { } auto template { { e = T::error }; };\n" - "ScopedEnum1 se1; { enum class E : T { e = 0 = e ScopedEnum2 struct UnscopedEnum3 { T{ e = 4 }; };\n" - "arr[(int) E::e]; }; UnscopedEnum3 e2 = f()\n" - "{ { e = e1; T::error } int test1 ue2; g() { enum class E { e = T::error }; return E::e; } int test2 = } \n" - "namespace UnscopedEnum { template struct UnscopedEnum1 { E{ e = T::error }; }; UnscopedEnum1 { enum E : { e = 0 }; };\n" - "UnscopedEnum2 ue3; template struct UnscopedEnum3 { enum { }; }; int arr[E::e]; };\n" - "UnscopedEnum3 namespace template int f() { enum E { e }; T::error }; return (int) E(); } int test1 int g() { enum E { e = E };\n" - "E::e; } int test2 = g(); }", true), InternalError); - } - - void garbageCode9() { - ASSERT_THROW(tokenizeAndStringify("enum { e = { } } ( ) { { enum { } } } { e } ", true), InternalError); - } - - void garbageCode10() { // #6127 - tokenizeAndStringify("for( rl=reslist; rl!=NULL; rl=rl->next )", /*simplify=*/true); + //valid, when 'x' and 'y' are constexpr. + tokenizeAndStringify("void f() {switch (n) { case sqrt(x+y): z(); break;}}"); + ASSERT_EQUALS("", errout.str()); } void simplifyFileAndLineMacro() { // tokenize 'return - __LINE__' correctly @@ -8704,16 +8520,6 @@ private: tokenizeAndStringify(code.c_str()); // just survive... } - void astGarbage() { - testAst("--"); // don't crash - - testAst("N 1024 float a[N], b[N + 3], c[N]; void N; (void) i;\n" - "int #define for (i = avx_test i < c[i]; i++)\n" - "b[i + 3] = a[i] * {}"); // Don't hang (#5787) - - testAst("START_SECTION([EXTRA](bool isValid(const String &filename)))"); // Don't crash (#5991) - } - bool isStartOfExecutableScope(int offset, const char code[]) { const Settings settings; Tokenizer tokenizer(&settings, this);