From 003e27c10ea77075f578cd9f8870366bec166041 Mon Sep 17 00:00:00 2001 From: Reijo Tomperi Date: Sun, 20 Sep 2009 00:09:05 +0300 Subject: [PATCH] Fix #667 (Preprocessor does not handle macro inside macro correctly) http://sourceforge.net/apps/trac/cppcheck/ticket/667 --- src/preprocessor.cpp | 67 ++++++++++++++++++++++++++++++++++++--- test/testpreprocessor.cpp | 16 +++++++++- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/preprocessor.cpp b/src/preprocessor.cpp index 16171a170..f63bff808 100644 --- a/src/preprocessor.cpp +++ b/src/preprocessor.cpp @@ -1221,13 +1221,18 @@ private: std::string _name; std::string _macro; bool _variadic; + const std::string _prefix; /** The macro has parantheses but no parameters.. "AAA()" */ bool _nopar; public: + /** + * @param macro The code after #define, until end of line, + * e.g. "A(x) foo(x);" + */ PreprocessorMacro(const std::string ¯o) - : _macro(macro) + : _macro(macro), _prefix("__cppcheck__") { // Tokenize the macro to make it easier to handle std::istringstream istr(macro.c_str()); @@ -1267,6 +1272,41 @@ public: } } + /** + * To avoid name collisions, we will rename macro variables by + * adding _prefix in front of the name of each variable. + * Returns the macro with converted names + * @return e.g. "A(__cppcheck__x) foo(__cppcheck__x);" + */ + std::string renameMacroVariables() + { + // No variables + if (_params.size() == 0) + return _macro; + + // Already renamed + if (_params[0].compare(0, _prefix.length(), _prefix) == 0) + return _macro; + + std::string result; + result.append(_name); + result.append("("); + std::vector values; + for (unsigned int i = 0; i < _params.size(); ++i) + { + if (i > 0) + result.append(","); + values.push_back(_prefix + _params[i]); + result.append(values.back()); + } + + result.append(") "); + std::string temp; + this->code(values, temp); + result.append(temp); + return result; + } + const Token *tokens() const { return tokenizer.tokens(); @@ -1424,12 +1464,12 @@ public: std::string Preprocessor::expandMacros(std::string code, const std::string &filename, ErrorLogger *errorLogger) { // Search for macros and expand them.. - std::string::size_type defpos = std::string::npos; - while ((defpos > 0) && ((defpos = code.rfind("#define ", defpos)) != std::string::npos)) + std::string::size_type defpos = 0; + while ((defpos = code.find("#define ", defpos)) != std::string::npos) { if (defpos > 0 && code[defpos-1] != '\n') { - defpos--; + defpos++; continue; } @@ -1541,6 +1581,25 @@ std::string Preprocessor::expandMacros(std::string code, const std::string &file if (macro.params().size() && pos2 >= code.length()) continue; + // Check are we in #define + std::string::size_type startOfLine = code.rfind("\n", pos1); + ++startOfLine; + + if (code.substr(startOfLine, 8) == "#define ") + { + // We are inside a define, make sure we don't have name collision + // by e.g. replacing the following code: + // #define B(a) A(a) + // With this: + // #define B(2a) A(2a) + std::string::size_type endOfLine = code.find("\n", pos1); + startOfLine += 8; + + PreprocessorMacro tempMacro(code.substr(startOfLine, endOfLine - startOfLine)); + code.erase(startOfLine, endOfLine - startOfLine); + code.insert(startOfLine, tempMacro.renameMacroVariables()); + } + unsigned int numberOfNewlines = 0; if (macro.variadic() || macro.nopar() || macro.params().size()) diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 03ed1d4c4..656d4c926 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -888,7 +888,21 @@ private: const char filedata[] = "#define A B\n" "#define B 3\n" "A"; - TODO_ASSERT_EQUALS("\n\n3", OurPreprocessor::expandMacros(filedata)); + ASSERT_EQUALS("\n\n3", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" + "#define D(fmt, args...) DBG(fmt, ## args)\n" + "DBG(\"hello\");"; + ASSERT_EQUALS("\n\nprintf(\"hello\");", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" + "#define D(fmt, args...) DBG(fmt, ## args)\n" + "DBG(\"hello: %d\",3);"; + ASSERT_EQUALS("\n\nprintf(\"hello: %d\",3);", OurPreprocessor::expandMacros(filedata)); } }