From 1e8fc71f8e0de853c100ff3af0a45aed52afa814 Mon Sep 17 00:00:00 2001 From: makulik Date: Wed, 30 Nov 2011 20:24:01 +0100 Subject: [PATCH] Solution for ticket #3353 'Allow explicit undef's for configuration' Signed-off-by: makulik --- cli/cmdlineparser.cpp | 30 +++++++ lib/preprocessor.cpp | 24 +++++- lib/settings.h | 3 + man/cppcheck.1.xml | 12 ++- test/testcmdlineparser.cpp | 54 +++++++++++++ test/testpreprocessor.cpp | 155 +++++++++++++++++++++++++++++++++++++ 6 files changed, 274 insertions(+), 4 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 0e5a427ee..ec9beba5a 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -324,6 +324,32 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) _settings->userDefines += ";"; _settings->userDefines += define; } + // User undef + else if (strncmp(argv[i], "-U", 2) == 0) + { + std::string undef; + + // "-U undef" + if (strcmp(argv[i], "-U") == 0) + { + ++i; + if (i >= argc || strncmp(argv[i], "-", 1) == 0 || + strncmp(argv[i], "--", 2) == 0) + { + PrintMessage("cppcheck: argument to '-U' is missing."); + return false; + } + + undef = argv[i]; + } + // "-Uundef" + else + { + undef = 2 + argv[i]; + } + + _settings->userUndefs.insert(undef); + } // Include paths else if (strncmp(argv[i], "-I", 2) == 0) { @@ -676,6 +702,10 @@ void CmdLineParser::PrintHelp() " Use '-D' to limit the checking. When '-D' is used the\n" " checking is limited to the given configuration.\n" " Example: '-DDEBUG=1 -D__cplusplus'.\n" + " -U By default Cppcheck checks all configurations.\n" + " Use '-U' to explicitely hide certain #ifdef code\n" + " paths from checking.\n" + " Example: '-UDEBUG'\n" " --enable= Enable additional checks. The available ids are:\n" " * all\n" " Enable all checks\n" diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 2f1d27729..40bb1b753 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -739,8 +739,10 @@ void Preprocessor::preprocess(std::istream &istr, std::map configs; std::string data; preprocess(istr, data, configs, filename, includePaths); - for (std::list::const_iterator it = configs.begin(); it != configs.end(); ++it) - result[ *it ] = Preprocessor::getcode(data, *it, filename, _settings, _errorLogger); + for (std::list::const_iterator it = configs.begin(); it != configs.end(); ++it) { + if (_settings && (_settings->userUndefs.find(*it) == _settings->userUndefs.end())) + result[ *it ] = Preprocessor::getcode(data, *it, filename, _settings, _errorLogger); + } } std::string Preprocessor::removeSpaceNearNL(const std::string &str) @@ -1539,6 +1541,24 @@ std::string Preprocessor::getcode(const std::string &filedata, const std::string if (line.compare(0, 8, "#define ") == 0) { match = true; + + if (settings) { + typedef std::set::iterator It; + for (It it = settings->userUndefs.begin(); it != settings->userUndefs.end(); ++it) { + std::string::size_type pos = line.find_first_not_of(' ',8); + if (pos != std::string::npos) { + std::string::size_type pos2 = line.find(*it,pos); + if ((pos2 != std::string::npos) && + ((line.size() == pos2 + (*it).size()) || + (line[pos2 + (*it).size()] == ' ') || + (line[pos2 + (*it).size()] == '('))) { + match = false; + break; + } + } + } + } + for (std::list::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it) match &= bool(*it); diff --git a/lib/settings.h b/lib/settings.h index 60a02df3e..1ef7d9837 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -150,6 +150,9 @@ public: /** @brief defines given by the user */ std::string userDefines; + /** @brief undefines given by the user */ + std::set userUndefs; + /** @brief Experimental 2 pass checking of files */ bool test_2_pass; diff --git a/man/cppcheck.1.xml b/man/cppcheck.1.xml index 851e55fee..af40e1dcf 100644 --- a/man/cppcheck.1.xml +++ b/man/cppcheck.1.xml @@ -105,6 +105,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + @@ -170,8 +171,15 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ By default Cppcheck checks all configurations. Use -D to limit the checking. When -D is used the checking is limited to the given configuration. Example: -DDEBUG=1 -D__cplusplus - - + + + + + By default Cppcheck checks all configurations. Use '-U' to explicitely hide certain #ifdef <id> code paths from checking. +Example: '-UDEBUG' + + + Enable additional checks. The available ids are: diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 9f45c24cc..289101d48 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -120,6 +120,12 @@ private: TEST_CASE(checkconfig); TEST_CASE(unknownParam); + + TEST_CASE(undefs_noarg); + TEST_CASE(undefs_noarg2); + TEST_CASE(undefs_noarg3); + TEST_CASE(undefs); + TEST_CASE(undefs2); } @@ -923,6 +929,54 @@ private: CmdLineParser parser(&settings); ASSERT(!parser.ParseFromArgs(3, argv)); } + + void undefs() { + REDIRECT; + const char *argv[] = {"cppcheck", "-U_WIN32", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(parser.ParseFromArgs(3, argv)); + ASSERT_EQUALS(1, settings.userUndefs.size()); + ASSERT(settings.userUndefs.find("_WIN32") != settings.userUndefs.end()); + } + + void undefs2() { + REDIRECT; + const char *argv[] = {"cppcheck", "-U_WIN32", "-UNODEBUG", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(parser.ParseFromArgs(4, argv)); + ASSERT_EQUALS(2, settings.userUndefs.size()); + ASSERT(settings.userUndefs.find("_WIN32") != settings.userUndefs.end()); + ASSERT(settings.userUndefs.find("NODEBUG") != settings.userUndefs.end()); + } + + void undefs_noarg() { + REDIRECT; + const char *argv[] = {"cppcheck", "-U"}; + Settings settings; + CmdLineParser parser(&settings); + // Fails since -U has no param + ASSERT_EQUALS(false, parser.ParseFromArgs(2, argv)); + } + + void undefs_noarg2() { + REDIRECT; + const char *argv[] = {"cppcheck", "-U", "-v", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + // Fails since -U has no param + ASSERT_EQUALS(false, parser.ParseFromArgs(4, argv)); + } + + void undefs_noarg3() { + REDIRECT; + const char *argv[] = {"cppcheck", "-U", "--quiet", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + // Fails since -U has no param + ASSERT_EQUALS(false, parser.ParseFromArgs(4, argv)); + } }; REGISTER_TEST(TestCmdlineParser) diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index ed4611f91..7544cd209 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -235,6 +235,16 @@ private: // Defines are given: test Preprocessor::handleIncludes TEST_CASE(def_handleIncludes); TEST_CASE(def_missingInclude); + + // Using -U to undefine symbols + TEST_CASE(undef1); + TEST_CASE(undef2); + TEST_CASE(undef3); + TEST_CASE(undef4); + TEST_CASE(undef5); + TEST_CASE(undef6); + TEST_CASE(undef7); + } @@ -3031,6 +3041,151 @@ private: ASSERT_EQUALS("", errout.str()); } } + + void undef1() { + Settings settings; + + const char filedata[] = "#ifdef X\n" + "Fred & Wilma\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + settings.userUndefs.insert("X"); + + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n\n", actual[""]); + } + + void undef2() { + Settings settings; + + const char filedata[] = "#ifndef X\n" + "Fred & Wilma\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + settings.userUndefs.insert("X"); + + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\nFred & Wilma\n\n", actual[""]); + } + + void undef3() { + Settings settings; + + const char filedata[] = "#define X\n" + "#ifdef X\n" + "Fred & Wilma\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + settings.userUndefs.insert("X"); // User undefs should override internal defines + + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n\n\n", actual[""]); + } + + void undef4() { + Settings settings; + + const char filedata[] = "#define X Y\n" + "#ifdef X\n" + "Fred & Wilma\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + settings.userUndefs.insert("X"); // User undefs should override internal defines + + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n\n\n", actual[""]); + } + + void undef5() { + Settings settings; + + const char filedata[] = "#define X() Y\n" + "#ifdef X\n" + "Fred & Wilma\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + settings.userUndefs.insert("X"); // User undefs should override internal defines + + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n\n\n", actual[""]); + } + + void undef6() { + Settings settings; + + const char filedata[] = "#define X Y\n" + "#ifdef X\n" + "Fred & Wilma\n" + "#else" + "Barney & Betty\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + settings.userUndefs.insert("X"); // User undefs should override internal defines + + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + TODO_ASSERT_EQUALS("\n\n\nBarney & Betty\n\n","\n\n\n\n\n", actual[""]); + } + + void undef7() { + Settings settings; + + const char filedata[] = "#define X XDefined\n" + "X;\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + settings.userUndefs.insert("X"); // User undefs should override internal defines + + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + TODO_ASSERT_EQUALS("\n;\n","\nXDefined;\n", actual[""]); + } }; REGISTER_TEST(TestPreprocessor)