From c3517924d07bbf376e6e136d3081615086041ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 4 Oct 2020 11:27:31 +0200 Subject: [PATCH] Clang import testing: Compare AST --- lib/tokenlist.cpp | 16 ++++++++++++- test/cli/test-clang-import.py | 42 +++++++++++++++++++++++++++++++++++ test/testother.cpp | 2 +- test/testsimplifytypedef.cpp | 5 ++--- test/testsymboldatabase.cpp | 2 +- test/testtokenize.cpp | 32 +++++++++++++------------- 6 files changed, 77 insertions(+), 22 deletions(-) diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index a7239137c..3bd81893c 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -1545,11 +1545,25 @@ static Token * createAstAtToken(Token *tok, bool cpp) return tok->linkAt(1); if (Token::Match(tok, "%type% %name%|*|&|::") && tok->str() != "return") { + int typecount = 0; Token *typetok = tok; - while (Token::Match(typetok, "%type%|::|*|&")) + while (Token::Match(typetok, "%type%|::|*|&")) { + if (typetok->isName() && !Token::simpleMatch(typetok->previous(), "::")) + typecount++; typetok = typetok->next(); + } if (Token::Match(typetok, "%var% =") && typetok->varId()) tok = typetok; + + // Do not create AST for function declaration + if (typetok && + typecount >= 2 && + !Token::Match(tok, "return|throw") && + Token::Match(typetok->previous(), "%name% (") && + typetok->previous()->varId() == 0 && + !typetok->previous()->isKeyword() && + Token::Match(typetok->link(), ") const|;|{")) + return typetok; } if (Token::Match(tok, "return|case") || diff --git a/test/cli/test-clang-import.py b/test/cli/test-clang-import.py index f1bdbf3de..b3970f21f 100644 --- a/test/cli/test-clang-import.py +++ b/test/cli/test-clang-import.py @@ -42,6 +42,39 @@ def check_symbol_database(code): assert get_debug_section('### Symbol database', stdout1) == get_debug_section('### Symbol database', stdout2) +def check_ast(code): + # Only compare syntax trees if clang is found in PATH + try: + subprocess.call(['clang', '--version']) + except OSError: + return + + testfile = 'test.cpp' + with open(testfile, 'w+t') as f: + f.write(code) + ret1, stdout1, stderr1 = cppcheck(['--clang', '--debug', '-v', testfile]) + ret2, stdout2, stderr2 = cppcheck(['--debug', '-v', testfile]) + os.remove(testfile) + assert get_debug_section('##AST', stdout1) == get_debug_section('##AST', stdout2) + + +def todo_check_ast(code): + # Only compare syntax trees if clang is found in PATH + try: + subprocess.call(['clang', '--version']) + except OSError: + return + + testfile = 'test.cpp' + with open(testfile, 'w+t') as f: + f.write(code) + ret1, stdout1, stderr1 = cppcheck(['--clang', '--debug', '-v', testfile]) + ret2, stdout2, stderr2 = cppcheck(['--debug', '-v', testfile]) + os.remove(testfile) + assert get_debug_section('##AST', stdout1) != get_debug_section('##AST', stdout2) + + + def test1(): check_symbol_database('int main(){return 0;}') @@ -49,5 +82,14 @@ def test2(): code = 'struct Foo { void f(); }; void Foo::f() {}' check_symbol_database(code) +def test_ast_calculations(): + check_ast('int x = 5; int y = (x + 4) * 2;') + todo_check_ast('int dostuff(int x) { return x ? 3 : 5; }') + +def test_ast_control_flow(): + check_ast('void foo(int x) { if (x > 5){} }') + check_ast('int dostuff() { for (int x = 0; x < 10; x++); }') + todo_check_ast('void foo(int x) { switch (x) {case 1: break; } }') + diff --git a/test/testother.cpp b/test/testother.cpp index aa860b8aa..204e769da 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -5838,7 +5838,7 @@ private: check("void f() {\n" " int val = 0;\n" - " int *p = &val;n" + " int *p = &val;\n" " if (*p < 0) continue;\n" " if ((*p > 0)) {}\n" "}\n"); diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index c7e3a192e..e90f738ce 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -1153,8 +1153,7 @@ private: ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS_WITHOUT_LINENUMBERS( - "[test.cpp:31]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n" - "[test.cpp:28]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n", + "[test.cpp:31]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n", errout.str()); } @@ -2158,7 +2157,7 @@ private: " return fred;\n" "}"; tok(code); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("", errout.str()); } void simplifyTypedef101() { // ticket #3003 (segmentation fault) diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 4d378d501..803d9be97 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -6665,7 +6665,7 @@ private: void executableScopeWithUnknownFunction() { GET_SYMBOL_DB("class Fred {\n" - " void foo(const std::string & a = "");\n" + " void foo(const std::string & a = \"\");\n" "};\n" "Fred::foo(const std::string & b) { }\n"); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 74b2bb2e7..5e5cf7d39 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -7579,7 +7579,7 @@ private: ASSERT_EQUALS("a0>bc/d:?", testAst("(a>0) ? (b/(c)) : d;")); ASSERT_EQUALS("abc/+d+", testAst("a + (b/(c)) + d;")); - ASSERT_EQUALS("f( x1024x/0:?", testAst("void f() { x ? 1024 / x : 0; }")); + ASSERT_EQUALS("x1024x/0:?", testAst("void f() { x ? 1024 / x : 0; }")); ASSERT_EQUALS("absizeofd(ef.+(=", testAst("a = b(sizeof(c d) + e.f)")); @@ -7623,11 +7623,11 @@ private: ASSERT_EQUALS("catch...(", testAst("try {} catch (...) {}")); - ASSERT_EQUALS("FooBar(", testAst("void Foo(Bar&);")); - ASSERT_EQUALS("FooBar(", testAst("void Foo(Bar&&);")); + ASSERT_EQUALS("", testAst("void Foo(Bar&);")); + ASSERT_EQUALS("", testAst("void Foo(Bar&&);")); - ASSERT_EQUALS("FooBarb&(", testAst("void Foo(Bar& b);")); - ASSERT_EQUALS("FooBarb&&(", testAst("void Foo(Bar&& b);")); + ASSERT_EQUALS("Barb&", testAst("void Foo(Bar& b);")); + ASSERT_EQUALS("Barb&&", testAst("void Foo(Bar&& b);")); ASSERT_EQUALS("DerivedDerived::(", testAst("Derived::~Derived() {}")); @@ -7720,7 +7720,7 @@ private: ASSERT_EQUALS("a::new=", testAst("a = new (b) ::X;")); ASSERT_EQUALS("aA1(new(bB2(new(,", testAst("a(new A(1)), b(new B(2))")); ASSERT_EQUALS("Fred10[new", testAst(";new Fred[10];")); - ASSERT_EQUALS("f( adelete", testAst("void f() { delete a; }")); + ASSERT_EQUALS("adelete", testAst("void f() { delete a; }")); // invalid code (libreoffice), don't hang // #define SlideSorterViewShell @@ -7788,7 +7788,7 @@ private: // Type{data}() ASSERT_EQUALS("ab{(=", testAst("a=b{}();")); ASSERT_EQUALS("abc{((=", testAst("a=b(c{}());")); - ASSERT_EQUALS("f( xNULL!=0(x(:?", testAst("void f() { {} ((x != NULL) ? (void)0 : x()); }")); + ASSERT_EQUALS("xNULL!=0(x(:?", testAst("void f() { {} ((x != NULL) ? (void)0 : x()); }")); // ({..}) ASSERT_EQUALS("a{+d+ bc+", testAst("a+({b+c;})+d")); @@ -7824,7 +7824,7 @@ private: ASSERT_EQUALS("a0{,( \'\'abc12:?,", testAst("a(0, {{\'\', (abc) ? 1 : 2}});")); // struct initialization hang - ASSERT_EQUALS("sbar.1{,{(={= fcmd( forfieldfield++;;(", + ASSERT_EQUALS("sbar.1{,{(={= forfieldfield++;;(", testAst("struct S s = {.bar = (struct foo) { 1, { } } };\n" "void f(struct cmd *) { for (; field; field++) {} }")); @@ -7864,8 +7864,8 @@ private: // #9127 const char code1[] = "using uno::Ref;\n" "Ref r;\n" - "int x(0);"; - ASSERT_EQUALS("unoRef:: x0(", testAst(code1)); + "int var(0);"; + ASSERT_EQUALS("unoRef:: var0(", testAst(code1)); ASSERT_EQUALS("vary=", testAst("std::string var = y;")); } @@ -7897,10 +7897,10 @@ private: ASSERT_EQUALS("1f2(+3+", testAst("1+f(2)+3")); ASSERT_EQUALS("1f23,(+4+", testAst("1+f(2,3)+4")); ASSERT_EQUALS("1f2a&,(+", testAst("1+f(2,&a)")); - ASSERT_EQUALS("fargv[(", testAst("int f(char argv[]);")); - ASSERT_EQUALS("fchar(", testAst("extern unsigned f(const char *);")); - ASSERT_EQUALS("fcharformat*...,(", testAst("extern void f(const char *format, ...);")); - ASSERT_EQUALS("for_each_commit_graftint((void,(", testAst("extern int for_each_commit_graft(int (*)(int*), void *);")); + ASSERT_EQUALS("argv[", testAst("int f(char argv[]);")); + ASSERT_EQUALS("", testAst("extern unsigned f(const char *);")); + ASSERT_EQUALS("charformat*...,", testAst("extern void f(const char *format, ...);")); + ASSERT_EQUALS("int((void,", testAst("extern int for_each_commit_graft(int (*)(int*), void *);")); ASSERT_EQUALS("for;;(", testAst("for (;;) {}")); ASSERT_EQUALS("xsizeofvoid(=", testAst("x=sizeof(void*)")); ASSERT_EQUALS("abc{d{,{(=", testAst("a = b({ c{}, d{} });")); @@ -7918,7 +7918,7 @@ private: // This two unit tests were added to avoid a crash. The actual correct AST result for non-executable code has not been determined so far. ASSERT_EQUALS("Cpublica::b:::", testAst("class C : public ::a::b { };")); - ASSERT_EQUALS("AB: f( abc+=", testAst("struct A : public B { void f() { a=b+c; } };")); + ASSERT_EQUALS("AB: abc+=", testAst("struct A : public B { void f() { a=b+c; } };")); ASSERT_EQUALS("xfts(=", testAst("; auto x = f(ts...);")); } @@ -8011,7 +8011,7 @@ private: // #9662 ASSERT_EQUALS("b{[{ stdunique_ptr::0nullptrnullptr:?{", testAst("auto b{[] { std::unique_ptr{0 ? nullptr : nullptr}; }};")); - ASSERT_EQUALS("a( b{[=", testAst("void a() { [b = [] { ; }] {}; }")); + ASSERT_EQUALS("b{[=", testAst("void a() { [b = [] { ; }] {}; }")); }