diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index f4a80138f..a333804a8 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -129,7 +129,31 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) } // Filter errors - else if (strcmp(argv[i], "--suppressions") == 0) + else if (strncmp(argv[i], "--suppressions-list=", 20) == 0) + { + std::string filename = argv[i]; + filename = filename.substr(20); + std::ifstream f(filename.c_str()); + if (!f.is_open()) + { + std::string message("cppcheck: Couldn't open the file \""); + message += std::string(filename); + message += "\""; + PrintMessage(message); + return false; + } + const std::string errmsg(_settings->nomsg.parseFile(f)); + if (!errmsg.empty()) + { + PrintMessage(errmsg); + return false; + } + } + + // Filter errors + // This is deprecated, see --supressions-list above + else if (strcmp(argv[i], "--suppressions") == 0 && + strlen(argv[i]) == 14) { ++i; @@ -516,7 +540,7 @@ void CmdLineParser::PrintHelp() " cppcheck [--append=file] [-D] [--enable=] [--error-exitcode=[n]]\n" " [--exitcode-suppressions file] [--file-list=file.txt] [--force]\n" " [--help] [-Idir] [--inline-suppr] [-j [jobs]] [--quiet]\n" - " [--report-progress] [--style] [--suppressions file.txt]\n" + " [--report-progress] [--style] [--suppressions-list=file.txt]\n" " [--verbose] [--version] [--xml] [file or path1] [file or path]\n" "\n" "If path is given instead of filename, *.cpp, *.cxx, *.cc, *.c++ and *.c files\n" @@ -540,6 +564,7 @@ void CmdLineParser::PrintHelp() " if arguments are not valid or if no input files are\n" " provided. Note that your operating system can\n" " modify this value, e.g. 256 can become 0.\n" + " --errorlist Print a list of all error messages in XML format.\n" " --exitcode-suppressions file\n" " Used when certain messages should be displayed but\n" " should not cause a non-zero exitcode.\n" @@ -557,8 +582,10 @@ void CmdLineParser::PrintHelp() " -q, --quiet Only print error messages\n" " --report-progress Report progress messages while checking a file.\n" " -s, --style deprecated, use --enable=style\n" - " --suppressions file Suppress warnings listed in the file. Filename and line\n" - " are optional. The format of the single line in file is:\n" + " --suppressions-list=file\n" + " Suppress warnings listed in the file. Filename and line\n" + " are optional in the suppression file. The format of the\n" + " single line in the suppression file is:\n" " [error id]:[filename]:[line]\n" " --template '[text]' Format the error messages. E.g.\n" " '{file}:{line},{severity},{id},{message}' or\n" diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 8b7947387..51032d44d 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1348,6 +1348,42 @@ const Scope *SymbolDatabase::findVariableType(const Scope *start, const Token *t //--------------------------------------------------------------------------- +const Scope *SymbolDatabase::findFunctionScopeByToken(const Token *tok) const +{ + std::list::const_iterator scope; + + for (scope = scopeList.begin(); scope != scopeList.end(); ++scope) + { + if ((*scope)->type == Scope::eFunction) + { + if ((*scope)->classDef == tok) + return (*scope); + } + } + return 0; +} + +//--------------------------------------------------------------------------- + +const Function *SymbolDatabase::findFunctionByToken(const Token *tok) const +{ + std::list::const_iterator scope; + + for (scope = scopeList.begin(); scope != scopeList.end(); ++scope) + { + std::list::const_iterator func; + + for (func = (*scope)->functionList.begin(); func != (*scope)->functionList.end(); ++func) + { + if (func->token == tok) + return &*func; + } + } + return 0; +} + +//--------------------------------------------------------------------------- + Scope * Scope::findInNestedList(const std::string & name) { std::list::iterator it; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index f1de6c0b0..e8c858c3d 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -362,6 +362,10 @@ public: */ const Scope *findVariableType(const Scope *start, const Token *type) const; + const Scope *findFunctionScopeByToken(const Token *tok) const; + + const Function *findFunctionByToken(const Token *tok) const; + bool argsMatch(const Scope *info, const Token *first, const Token *second, const std::string &path, unsigned int depth) const; bool isClassOrStruct(const std::string &type) const diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index d21484c52..9cbdcd6ea 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -2041,6 +2041,16 @@ bool Tokenizer::tokenize(std::istream &code, } } + // if MACRO + for (const Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::Match(tok, "if|for|while %var% (")) + { + syntaxError(tok); + return false; + } + } + // Simplify JAVA/C# code if (isJavaOrCSharp() && _files[0].find(".java") != std::string::npos) { @@ -6836,7 +6846,7 @@ bool Tokenizer::simplifyRedundantParanthesis() ret = true; } - while (Token::Match(tok->previous(), "[;{(] ( %var% (") && + while (Token::Match(tok->previous(), "[,;{}(] ( %var% (") && tok->link()->previous() == tok->tokAt(2)->link()) { // We have "( func ( *something* ))", remove the outer diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 78dc5be51..c9bac0429 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -72,7 +72,9 @@ private: TEST_CASE(jobsMissingCount); TEST_CASE(jobsInvalid); TEST_CASE(reportProgress); - TEST_CASE(suppressions); // TODO: Create and test real suppression file + TEST_CASE(suppressionsOld); // TODO: Create and test real suppression file + TEST_CASE(suppressions) + TEST_CASE(suppressionsNoFile) TEST_CASE(templates); TEST_CASE(templatesGcc); TEST_CASE(templatesVs); @@ -492,8 +494,9 @@ private: ASSERT(settings.reportProgress); } - void suppressions() + void suppressionsOld() { + // TODO: Fails because there is no suppr.txt file! REDIRECT; const char *argv[] = {"cppcheck", "--suppressions suppr.txt", "file.cpp"}; Settings settings; @@ -501,6 +504,25 @@ private: ASSERT(!parser.ParseFromArgs(3, argv)); } + void suppressions() + { + // TODO: Fails because there is no suppr.txt file! + REDIRECT; + const char *argv[] = {"cppcheck", "--suppressions-list=suppr.txt", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(!parser.ParseFromArgs(3, argv)); + } + + void suppressionsNoFile() + { + REDIRECT; + const char *argv[] = {"cppcheck", "--suppressions-list=", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(!parser.ParseFromArgs(3, argv)); + } + void templates() { REDIRECT; diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index a1154d325..bfcb71b60 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -20,6 +20,14 @@ #include "testutils.h" #include "symboldatabase.h" +#define GET_SYMBOL_DB(code) \ + errout.str(""); \ + Settings settings; \ + Tokenizer tokenizer(&settings, this); \ + std::istringstream istr(code); \ + tokenizer.tokenize(istr, "test.cpp"); \ + const SymbolDatabase *db = tokenizer.getSymbolDatabase(); + class TestSymbolDatabase: public TestFixture { public: @@ -75,6 +83,16 @@ private: TEST_CASE(canFindMatchingBracketsOuterPair); TEST_CASE(canFindMatchingBracketsWithTooManyClosing); TEST_CASE(canFindMatchingBracketsWithTooManyOpening); + + TEST_CASE(hasRegularFunction); + TEST_CASE(hasInlineClassFunction); + TEST_CASE(hasMissingInlineClassFunction); + TEST_CASE(hasClassFunction); + + TEST_CASE(hasRegularFunctionReturningFunctionPointer); + TEST_CASE(hasInlineClassFunctionReturningFunctionPointer); + TEST_CASE(hasMissingInlineClassFunctionReturningFunctionPointer); + TEST_CASE(hasClassFunctionReturningFunctionPointer); } void test_isVariableDeclarationCanHandleNull() @@ -336,6 +354,174 @@ private: found = si.findClosingBracket(var.tokens()->tokAt(1), t); ASSERT(!found); } + + void hasRegularFunction() + { + GET_SYMBOL_DB("void func() { }\n") + + // 2 scopes: Global and Function + ASSERT(db && db->scopeList.size() == 2 && tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(1)); + + ASSERT(scope && scope->className == "func"); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(1)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(1)); + ASSERT(function && function->hasBody); + } + } + + void hasInlineClassFunction() + { + GET_SYMBOL_DB("class Fred { void func() { } };\n") + + // 3 scopes: Global, Class, and Function + ASSERT(db && db->scopeList.size() == 3 && tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(4)); + + ASSERT(scope && scope->className == "func"); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(4)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(4)); + ASSERT(function && function->hasBody && function->isInline); + } + } + + void hasMissingInlineClassFunction() + { + GET_SYMBOL_DB("class Fred { void func(); };\n") + + // 2 scopes: Global and Class (no Function scope because there is no function implementation) + ASSERT(db && db->scopeList.size() == 2 && !tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(4)); + + ASSERT(scope == NULL); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(4)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(4)); + ASSERT(function && !function->hasBody); + } + } + + void hasClassFunction() + { + GET_SYMBOL_DB("class Fred { void func(); }; Fred::func() { }\n") + + // 3 scopes: Global, Class, and Function + ASSERT(db && db->scopeList.size() == 3 && tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(12)); + + ASSERT(scope && scope->className == "func"); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(12)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(12)); + ASSERT(function && function->hasBody && !function->isInline); + } + } + + void hasRegularFunctionReturningFunctionPointer() + { + GET_SYMBOL_DB("void (*func(int f))(char) { }\n") + + // 2 scopes: Global and Function + ASSERT(db && db->scopeList.size() == 2 && tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(3)); + + ASSERT(scope && scope->className == "func"); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(3)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(3)); + ASSERT(function && function->hasBody && function->retFuncPtr); + } + } + + void hasInlineClassFunctionReturningFunctionPointer() + { + GET_SYMBOL_DB("class Fred { void (*func(int f))(char) { } };\n") + + // 3 scopes: Global, Class, and Function + ASSERT(db && db->scopeList.size() == 3 && tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(6)); + + ASSERT(scope && scope->className == "func"); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(6)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(6)); + ASSERT(function && function->hasBody && function->isInline && function->retFuncPtr); + } + } + + void hasMissingInlineClassFunctionReturningFunctionPointer() + { + GET_SYMBOL_DB("class Fred { void (*func(int f))(char); };\n") + + // 2 scopes: Global and Class (no Function scope because there is no function implementation) + ASSERT(db && db->scopeList.size() == 2 && !tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(6)); + + ASSERT(scope == NULL); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(6)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(6)); + ASSERT(function && !function->hasBody && function->retFuncPtr); + } + } + + void hasClassFunctionReturningFunctionPointer() + { + GET_SYMBOL_DB("class Fred { void (*func(int f))(char); }; void (*Fred::func(int f))(char) { }\n") + + // 3 scopes: Global, Class, and Function + ASSERT(db && db->scopeList.size() == 3 && tokenizer.getFunctionTokenByName("func")); + + if (db) + { + const Scope *scope = db->findFunctionScopeByToken(tokenizer.tokens()->tokAt(23)); + + ASSERT(scope && scope->className == "func"); + + const Function *function = db->findFunctionByToken(tokenizer.tokens()->tokAt(23)); + + ASSERT(function && function->token->str() == "func"); + ASSERT(function && function->token == tokenizer.tokens()->tokAt(23)); + ASSERT(function && function->hasBody && !function->isInline && function->retFuncPtr); + } + } }; REGISTER_TEST(TestSymbolDatabase) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 561b91a95..f6d07042f 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -52,6 +52,7 @@ private: // don't freak out when the syntax is wrong TEST_CASE(wrong_syntax); + TEST_CASE(wrong_syntax_if_macro); // #2518 - if MACRO() TEST_CASE(minus); @@ -204,6 +205,7 @@ private: TEST_CASE(removeParantheses8); // Ticket #1865 TEST_CASE(removeParantheses9); // Ticket #1962 TEST_CASE(removeParantheses10); // Ticket #2320 + TEST_CASE(removeParantheses11); // Ticket #2505 TEST_CASE(tokenize_double); TEST_CASE(tokenize_strings); @@ -522,6 +524,15 @@ private: } } + void wrong_syntax_if_macro() + { + // #2518 + errout.str(""); + const std::string code("void f() { if MACRO(); }"); + tokenizeAndStringify(code.c_str(), false); + ASSERT_EQUALS("[test.cpp:1]: (error) syntax error\n", errout.str()); + } + void minus() { ASSERT_EQUALS("i = -12", tokenizeAndStringify("i = -12")); @@ -3678,6 +3689,12 @@ private: ASSERT_EQUALS("p = buf + 8 ;", tokenizeAndStringify("p = (buf + 8);", false)); } + void removeParantheses11() + { + // #2502 + ASSERT_EQUALS("{ } x ( ) ;", tokenizeAndStringify("{}(x());", false)); + } + void tokenize_double() { const char code[] = "void f()\n"