diff --git a/cppcheck.geany b/cppcheck.geany index 32bc84f6e..b151c26dc 100644 --- a/cppcheck.geany +++ b/cppcheck.geany @@ -8,3 +8,4 @@ run_cmd= [files] current_page=0 +FILE_NAME_0=18014;1;0;16;1;1;0;/home/danmar/cppcheck/test/testpreprocessor.cpp; diff --git a/src/cppcheck.cpp b/src/cppcheck.cpp index 50cd91f5c..22d6d5c6b 100644 --- a/src/cppcheck.cpp +++ b/src/cppcheck.cpp @@ -331,7 +331,7 @@ unsigned int CppCheck::check() try { - Preprocessor preprocessor; + Preprocessor preprocessor(_settings._debug); std::list configurations; std::string filedata = ""; diff --git a/src/preprocessor.cpp b/src/preprocessor.cpp index 7a01126eb..c611eea2f 100644 --- a/src/preprocessor.cpp +++ b/src/preprocessor.cpp @@ -32,6 +32,11 @@ #include #include +Preprocessor::Preprocessor(bool debug) : _debug(debug) +{ + +} + void Preprocessor::writeError(const std::string &fileName, const std::string &code, size_t endPos, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText) { if (!errorLogger) @@ -218,11 +223,6 @@ void Preprocessor::writeError(const std::string &fileName, const std::string &co errorType)); } -Preprocessor::Preprocessor() -{ - -} - static char readChar(std::istream &istr) { char ch = (char)istr.get(); @@ -621,13 +621,102 @@ std::list Preprocessor::getcfgs(const std::string &filedata) deflist.pop_back(); } + // convert configurations: "defined(A) && defined(B)" => "A;B" + for (std::list::iterator it = ret.begin(); it != ret.end(); ++it) + { + std::string s(*it); + + if (s.find("&&") != std::string::npos) + { + Tokenizer tokenizer; + std::istringstream istr(s.c_str()); + tokenizer.tokenize(istr, ""); + + s = ""; + const Token *tok = tokenizer.tokens(); + while (tok) + { + if (Token::Match(tok, "defined ( %var% )")) + { + s = s + tok->strAt(2); + tok = tok->tokAt(4); + if (tok && tok->str() == "&&") + { + s += ";"; + tok = tok->next(); + } + } + else if (Token::Match(tok, "%var% ;")) + { + s += tok->str() + ";"; + tok = tok->tokAt(2); + } + else + { + s = ""; + break; + } + } + + if (!s.empty()) + *it = s; + } + } + + // cleanup unhandled configurations.. + for (std::list::iterator it = ret.begin(); it != ret.end();) + { + const std::string &s(*it); + if (s.find("&&") != std::string::npos || s.find("||") != std::string::npos) + { + // unhandled ifdef configuration.. + if (_debug) + std::cout << "unhandled configuration: " << s << std::endl; + + ret.erase(it++); + } + else + ++it; + } + return ret; } -bool Preprocessor::match_cfg_def(std::string cfg, const std::string &def) +bool Preprocessor::match_cfg_def(std::string cfg, std::string def) { + //std::cout << "cfg: \"" << cfg << "\" "; + //std::cout << "def: \"" << def << "\""; + + for (std::string::size_type pos = def.find("defined("); pos != std::string::npos; pos = def.find("defined(", pos + 1)) + { + // The character before "defined" must not be '_' or alphanumeric + unsigned char chPrev = (pos > 0) ? def[pos-1] : ' '; + if (chPrev == '_' || std::isalnum(chPrev)) + continue; + + // Extract the parameter.. + std::string::size_type pos2 = def.find(")", pos); + if (pos2 == std::string::npos) + continue; + + std::string::size_type pos1 = pos + 8; + const std::string par(def.substr(pos1, pos2 - pos1)); + // TODO: better checking if parameter is defined + const bool isdefined(cfg.find(par) != std::string::npos); + + def.erase(pos, pos2 + 1 - pos); + def.insert(pos, isdefined ? "1" : "0"); + } + + while (def.find("1&&") != std::string::npos) + { + def.erase(def.find("1&&"), 3); + } + + //std::cout << " => \"" << def << "\"" << std::endl; + if (def == "0") return false; diff --git a/src/preprocessor.h b/src/preprocessor.h index 4f1234717..c6c4dc750 100644 --- a/src/preprocessor.h +++ b/src/preprocessor.h @@ -31,7 +31,7 @@ class Preprocessor { public: - Preprocessor(); + Preprocessor(bool debug = false); /** * Extract the code for each configuration @@ -105,6 +105,9 @@ protected: static int getHeaderFileName(std::string &str); private: + /** Show debug information when bailing out */ + const bool _debug; + /** * Remove space that has new line character on left or right side of it. * @@ -120,7 +123,7 @@ private: static std::string getdef(std::string line, bool def); - static bool match_cfg_def(std::string cfg, const std::string &def); + static bool match_cfg_def(std::string cfg, std::string def); /** * Search includes from code and append code from the included diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index e9fd1ce2c..b16f1c046 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -86,6 +86,8 @@ private: TEST_CASE(elif); TEST_CASE(if_cond1); + TEST_CASE(if_cond2); + TEST_CASE(if_cond3); TEST_CASE(multiline1); TEST_CASE(multiline2); @@ -532,6 +534,53 @@ private: ASSERT_EQUALS(2, static_cast(actual.size())); } + void if_cond2() + { + const char filedata[] = "#ifdef A\n" + "a\n" + "#endif\n" + "#if defined(A) && defined(B)\n" + "ab\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(3, static_cast(actual.size())); + ASSERT_EQUALS("\n\n\n\n\n\n", actual[""]); + ASSERT_EQUALS("\na\n\n\n\n\n", actual["A"]); + ASSERT_EQUALS("\na\n\n\nab\n\n", actual["A;B"]); + } + + void if_cond3() + { + const char filedata[] = "#ifdef A\n" + "a\n" + "#if defined(B) && defined(C)\n" + "abc\n" + "#endif\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(3, static_cast(actual.size())); + ASSERT_EQUALS("\n\n\n\n\n\n", actual[""]); + ASSERT_EQUALS("\na\n\n\n\n\n", actual["A"]); + ASSERT_EQUALS("\na\n\nabc\n\n\n", actual["A;B;C"]); + } + + + + void multiline1() {