diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 0b6fc5409..6b0ff26cc 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -832,6 +832,22 @@ std::list Preprocessor::getcfgs(const std::string &filedata, const return ret; } + // Replace defined constants + { + std::map varmap; + for (std::set::const_iterator it = defines.begin(); it != defines.end(); ++it) + { + std::string::size_type pos = (*it).find("="); + if (pos == std::string::npos) + continue; + const std::string varname((*it).substr(0, pos)); + const std::string value((*it).substr(pos + 1)); + varmap[varname] = value; + } + + simplifyCondition(varmap, def); + } + if (! deflist.empty() && line.find("#elif ") == 0) deflist.pop_back(); deflist.push_back(def); @@ -999,12 +1015,53 @@ std::list Preprocessor::getcfgs(const std::string &filedata, const } +void Preprocessor::simplifyCondition(const std::map &variables, std::string &condition) +{ + Tokenizer tokenizer; + std::istringstream istr(condition.c_str()); + tokenizer.tokenize(istr, ""); + + // replace variable names with values.. + for (Token *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) + { + const std::map::const_iterator it = variables.find(tok->str()); + if (it != variables.end()) + tok->str(it->second); + } + + // simplify calculations.. + tokenizer.simplifyCalculations(); + + if (!tokenizer.tokens()->tokAt(3) && Token::Match(tokenizer.tokens(), "%num% ==|!=|<=|>=|<|> %num%")) + { + const std::string op1(tokenizer.tokens()->str()); + const std::string cmp(tokenizer.tokens()->strAt(1)); + const std::string op2(tokenizer.tokens()->strAt(2)); + if (cmp == "==") + condition = (op1 == op2) ? "1" : "0"; + else if (cmp == "!=") + condition = (op1 != op2) ? "1" : "0"; + else if (cmp == "<=") + condition = (op1 <= op2) ? "1" : "0"; + else if (cmp == ">=") + condition = (op1 >= op2) ? "1" : "0"; + else if (cmp == "<") + condition = (op1 < op2) ? "1" : "0"; + else if (cmp == ">") + condition = (op1 > op2) ? "1" : "0"; + return; + } +} + + bool Preprocessor::match_cfg_def(const std::map &cfg, std::string def) { //std::cout << "cfg: \"" << cfg << "\" "; //std::cout << "def: \"" << def << "\""; + simplifyCondition(cfg, def); + if (cfg.find(def) != cfg.end()) return true; diff --git a/lib/preprocessor.h b/lib/preprocessor.h index b929e88f8..de9792253 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -77,6 +77,13 @@ public: */ static std::string getcode(const std::string &filedata, std::string cfg, const std::string &filename, ErrorLogger *errorLogger); + /** + * simplify condition + * @param variables Variable values + * @param condition The condition to simplify + */ + static void simplifyCondition(const std::map &variables, std::string &condition); + protected: static void writeError(const std::string &fileName, const std::string &code, size_t pos, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText); diff --git a/lib/tokenize.h b/lib/tokenize.h index 65c7bc72f..a3ef6bdb4 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -126,6 +126,13 @@ public: */ std::map _classInfoList; + /** + * Simplify constant calculations such as "1+2" => "3" + * @return true if modifications to token-list are done. + * false if no modifications are done. + */ + bool simplifyCalculations(); + #ifndef _MSC_VER private: #endif @@ -272,13 +279,6 @@ private: */ bool simplifyRedundantParanthesis(); - /** - * Simplify constant calculations such as "1+2" => "3" - * @return true if modifications to token-list are done. - * false if no modifications are done. - */ - bool simplifyCalculations(); - /** * Simplify functions like "void f(x) int x; {" * into "void f(int x) {" diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 77dc67508..11ed8bff0 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -99,6 +99,7 @@ private: TEST_CASE(if_cond4); TEST_CASE(if_cond5); TEST_CASE(if_cond6); + TEST_CASE(if_cond7); TEST_CASE(multiline1); TEST_CASE(multiline2); @@ -606,8 +607,8 @@ private: cfg["A"] = "1"; cfg["B"] = "2"; - TODO_ASSERT_EQUALS(true, Preprocessor::match_cfg_def(cfg, "A==1")); - TODO_ASSERT_EQUALS(true, Preprocessor::match_cfg_def(cfg, "A<2")); + ASSERT_EQUALS(true, Preprocessor::match_cfg_def(cfg, "A==1")); + ASSERT_EQUALS(true, Preprocessor::match_cfg_def(cfg, "A<2")); ASSERT_EQUALS(false, Preprocessor::match_cfg_def(cfg, "A==2")); ASSERT_EQUALS(false, Preprocessor::match_cfg_def(cfg, "A<1")); TODO_ASSERT_EQUALS(true, Preprocessor::match_cfg_def(cfg, "A>=1&&B<=A")); @@ -798,6 +799,25 @@ private: ASSERT_EQUALS("[file.c:2]: (error) mismatching number of '(' and ')' in this line: defined(A)&&defined(B))\n", errout.str()); } + void if_cond7() + { + const char filedata[] = "#define A 1\n" + "#if A==1\n" + "a1;\n" + "#endif\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + const Settings settings; + Preprocessor preprocessor(&settings, this); + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("\n\na1;\n\n", actual[""]); + } + void multiline1() { @@ -1657,8 +1677,8 @@ private: preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. - TODO_ASSERT_EQUALS("\n\n1\n\n", actual[""]); - TODO_ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("\n\n1\n\n", actual[""]); + ASSERT_EQUALS(1, actual.size()); } { @@ -1674,8 +1694,8 @@ private: preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. - TODO_ASSERT_EQUALS("\n\n1\n\n", actual[""]); - TODO_ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("\n\n1\n\n", actual[""]); + ASSERT_EQUALS(1, actual.size()); } }