From a402b6bb98217e248ca400fb01ac56c4ce5ad5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 19 Jun 2010 12:03:39 +0200 Subject: [PATCH] Fixed #1802 (Preprocessor: macros are expanded wrong) --- lib/preprocessor.cpp | 113 +++++++++++++++++++++++++++++--------- test/testpreprocessor.cpp | 27 ++++++--- 2 files changed, 105 insertions(+), 35 deletions(-) diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index b9736dba0..3e266505a 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -1476,7 +1476,7 @@ static void skipstring(const std::string &line, std::string::size_type &pos) * @param endFound out: was the end paranthesis found? */ static void getparams(const std::string &line, - unsigned int &pos2, + unsigned int &pos, std::vector ¶ms, unsigned int &numberOfNewlines, bool &endFound) @@ -1485,24 +1485,23 @@ static void getparams(const std::string &line, numberOfNewlines = 0; endFound = false; - if (line[pos2] == ' ') - pos2++; + if (line[pos] == ' ') + pos++; - if (line[pos2] != '(') + if (line[pos] != '(') return; // parantheses level int parlevel = 0; - // current parameter + // current parameter data std::string par; - // scan for parameters.. - for (; pos2 < line.length(); ++pos2) + for (; pos < line.length(); ++pos) { // increase paranthesis level - if (line[pos2] == '(') + if (line[pos] == '(') { ++parlevel; if (parlevel == 1) @@ -1510,7 +1509,7 @@ static void getparams(const std::string &line, } // decrease paranthesis level - else if (line[pos2] == ')') + else if (line[pos] == ')') { --parlevel; if (parlevel <= 0) @@ -1522,32 +1521,32 @@ static void getparams(const std::string &line, } // string - else if (line[pos2] == '\"' || line[pos2] == '\'') + else if (line[pos] == '\"' || line[pos] == '\'') { - const std::string::size_type p = pos2; - skipstring(line, pos2); - if (pos2 == line.length()) + const std::string::size_type p = pos; + skipstring(line, pos); + if (pos == line.length()) break; - par += line.substr(p, pos2 + 1 - p); + par += line.substr(p, pos + 1 - p); continue; } // count newlines. the expanded macro must have the same number of newlines - else if (line[pos2] == '\n') + else if (line[pos] == '\n') { ++numberOfNewlines; continue; } // new parameter - if (parlevel == 1 && line[pos2] == ',') + if (parlevel == 1 && line[pos] == ',') { params.push_back(par); par = ""; } // spaces are only added if needed - else if (line[pos2] == ' ') + else if (line[pos] == ' ') { // Add space only if it is needed if (par.size() && std::isalnum(par[par.length()-1])) @@ -1559,7 +1558,7 @@ static void getparams(const std::string &line, // add character to current parameter else if (parlevel >= 1) { - par.append(1, line[pos2]); + par.append(1, line[pos]); } } } @@ -1591,6 +1590,65 @@ private: /** disabled assignment operator */ void operator=(const PreprocessorMacro &); + + /** @brief expand inner macro */ + std::vector expandInnerMacros(const std::vector ¶ms1, + const std::map ¯os) const + { + std::string innerMacroName; + + // Is there an inner macro.. + { + const Token *tok = Token::findmatch(tokens(), ")"); + if (!Token::Match(tok, ") %var% (")) + return params1; + innerMacroName = tok->strAt(1); + tok = tok->tokAt(3); + unsigned int par = 0; + while (Token::Match(tok, "%var% ,|)")) + { + tok = tok->tokAt(2); + par++; + } + if (tok || par != params1.size()) + return params1; + } + + std::vector params2(params1); + + for (unsigned int ipar = 0; ipar < params1.size(); ++ipar) + { + const std::string s(innerMacroName + "("); + std::string param(params1[ipar]); + if (param.compare(0,s.length(),s)==0 && param[param.length()-1]==')') + { + std::vector innerparams; + unsigned int pos = s.length() - 1; + unsigned int num = 0; + bool endFound = false; + getparams(param, pos, innerparams, num, endFound); + if (pos == param.length()-1 && num==0 && endFound && innerparams.size() == params1.size()) + { + // Is inner macro defined? + std::map::const_iterator it = macros.find(innerMacroName); + if (it != macros.end()) + { + // expand the inner macro + const PreprocessorMacro *innerMacro = it->second; + + std::string innercode; + std::map innermacros = macros; + innermacros.erase(innerMacroName); + innerMacro->code(innerparams, innermacros, innercode); + params2[ipar] = innercode; + } + } + } + } + + return params2; + } + public: /** * @brief Constructor for PreprocessorMacro. This is the "setter" @@ -1673,10 +1731,11 @@ public: /** * get expanded code for this macro * @param params2 macro parameters + * @param macros macro definitions (recursion) * @param macrocode output string * @return true if the expanding was successful */ - bool code(const std::vector ¶ms2, std::string ¯ocode) const + bool code(const std::vector ¶ms2, const std::map macros, std::string ¯ocode) const { if (_nopar) { @@ -1739,6 +1798,8 @@ public: else { + const std::vector givenparams = expandInnerMacros(params2, macros); + const Token *tok = tokens(); while (tok && tok->str() != ")") tok = tok->next(); @@ -1763,18 +1824,18 @@ public: { if (_variadic && (i == _params.size() - 1 || - (params2.size() + 2 == _params.size() && i + 1 == _params.size() - 1))) + (givenparams.size() + 2 == _params.size() && i + 1 == _params.size() - 1))) { str = ""; - for (unsigned int j = (unsigned int)_params.size() - 1; j < params2.size(); ++j) + for (unsigned int j = (unsigned int)_params.size() - 1; j < givenparams.size(); ++j) { if (optcomma || j > _params.size() - 1) str += ","; optcomma = false; - str += params2[j]; + str += givenparams[j]; } } - else if (i >= params2.size()) + else if (i >= givenparams.size()) { // Macro had more parameters than caller used. macrocode = ""; @@ -1782,7 +1843,7 @@ public: } else if (stringify) { - const std::string &s(params2[i]); + const std::string &s(givenparams[i]); std::ostringstream ostr; ostr << "\""; for (std::string::size_type j = 0; j < s.size(); ++j) @@ -1794,7 +1855,7 @@ public: str = ostr.str() + "\""; } else - str = params2[i]; + str = givenparams[i]; break; } @@ -2088,7 +2149,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file // Create macro code.. std::string tempMacro; - if (!macro->code(params, tempMacro)) + if (!macro->code(params, macros, tempMacro)) { // Syntax error in code writeError(filename, diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index f481aec80..b1ca419bf 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -1540,24 +1540,33 @@ private: void preprocessor_doublesharp() { - const char filedata[] = "#define TEST(var,val) var = val\n" - "TEST(foo,20);\n"; - - // Compare results.. - ASSERT_EQUALS("\nfoo=20;\n", OurPreprocessor::expandMacros(filedata)); + // simple testcase without ## + const char filedata1[] = "#define TEST(var,val) var = val\n" + "TEST(foo,20);\n"; + ASSERT_EQUALS("\nfoo=20;\n", OurPreprocessor::expandMacros(filedata1)); + // simple testcase with ## const char filedata2[] = "#define TEST(var,val) var##_##val = val\n" "TEST(foo,20);\n"; - - // Compare results.. ASSERT_EQUALS("\nfoo_20=20;\n", OurPreprocessor::expandMacros(filedata2)); + // concat macroname const char filedata3[] = "#define ABCD 123\n" "#define A(B) A##B\n" "A(BCD)\n"; - - // Compare results.. ASSERT_EQUALS("\n\n123\n", OurPreprocessor::expandMacros(filedata3)); + + // Ticket #1802 - inner ## must be expanded before outer macro + const char filedata4[] = "#define A(B) A##B\n" + "#define a(B) A(B)\n" + "a(A(B))\n"; + ASSERT_EQUALS("\n\nAAB\n", OurPreprocessor::expandMacros(filedata4)); + + // Ticket #1802 - inner ## must be expanded before outer macro + const char filedata5[] = "#define AB(A,B) A##B\n" + "#define ab(A,B) AB(A,B)\n" + "ab(a,AB(b,c))\n"; + ASSERT_EQUALS("\n\nabc\n", OurPreprocessor::expandMacros(filedata5)); }