diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 601618dc2..5acf7a8bf 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -69,6 +69,41 @@ static unsigned char readChar(std::istream &istr) return ch; } +// Splits a string that contains the specified separator into substrings +static std::list split(const std::string &s, char separator) +{ + std::list parts; + + std::string::size_type prevPos = 0; + for (std::string::size_type pos = 0; pos < s.length(); ++pos) + { + if (s[pos] == separator) + { + if (pos > prevPos) + parts.push_back(s.substr(prevPos, pos - prevPos)); + prevPos = pos + 1; + } + } + if (prevPos < s.length()) + parts.push_back(s.substr(prevPos)); + + return parts; +} + +// Concatenates a list of strings, inserting a separator between parts +static std::string join(const std::list &list, char separator) +{ + std::string s; + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (!s.empty()) + s += separator; + + s += *it; + } + return s; +} + /** Just read the code into a string. Perform simple cleanup of the code */ std::string Preprocessor::read(std::istream &istr, const std::string &filename, Settings *settings) { @@ -743,6 +778,7 @@ std::list Preprocessor::getcfgs(const std::string &filedata, const deflist.pop_back(); deflist.push_back(def); def = ""; + for (std::list::const_iterator it = deflist.begin(); it != deflist.end(); ++it) { if (*it == "0") @@ -891,20 +927,24 @@ std::list Preprocessor::getcfgs(const std::string &filedata, const } varList.sort(); - s = ""; - for (std::list::iterator varIter = varList.begin(); varIter != varList.end(); ++varIter) - { - if (!s.empty()) - s += ";"; - - s += *varIter; - } + s = join(varList, ';'); if (!s.empty()) *it = s; } } + // Convert configurations into a canonical form: B;C;A or C;A;B => A;B;C + for (std::list::iterator it = ret.begin(); it != ret.end(); ++it) + { + // Split the configuration into a list of defines + std::list defs = split(*it, ';'); + + // Re-constitute the configuration after sorting the defines + defs.sort(); + *it = join(defs, ';'); + } + // Remove duplicates from the ret list.. ret.sort(); ret.unique(); diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index cb7904182..21013e1d1 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -178,6 +178,8 @@ private: // define and then ifdef TEST_CASE(define_ifdef); TEST_CASE(endfile); + + TEST_CASE(redundant_config); } @@ -2068,6 +2070,38 @@ private: } } + void redundant_config() + { + const char filedata[] = "int main() {\n" + "#ifdef FOO\n" + "#ifdef BAR\n" + " std::cout << 1;\n" + "#endif\n" + "#endif\n" + "\n" + "#ifdef BAR\n" + "#ifdef FOO\n" + " std::cout << 2;\n" + "#endif\n" + "#endif\n" + "}\n"; + + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess(istr, actual, "file.c"); + + // Compare results.. + ASSERT_EQUALS(4U, actual.size()); + ASSERT(actual.find("") != actual.end()); + ASSERT(actual.find("BAR") != actual.end()); + ASSERT(actual.find("FOO") != actual.end()); + ASSERT(actual.find("BAR;FOO") != actual.end()); + } + + void endfile() { const char filedata[] = "char a[] = \"#endfile\";\n" diff --git a/test/testsuite.cpp b/test/testsuite.cpp index a6f5c8779..484e7aa67 100644 --- a/test/testsuite.cpp +++ b/test/testsuite.cpp @@ -105,6 +105,15 @@ static std::string writestr(const std::string &str) return ostr.str(); } +void TestFixture::assert(const char *filename, int linenr, bool condition) +{ + if (!condition) + { + ++fails_counter; + errmsg << "Assertion failed in " << filename << " at line " << linenr << std::endl; + } +} + void TestFixture::assertEquals(const char *filename, int linenr, const std::string &expected, const std::string &actual) { if (expected != actual) diff --git a/test/testsuite.h b/test/testsuite.h index 5a2ab242c..785e9fa37 100644 --- a/test/testsuite.h +++ b/test/testsuite.h @@ -41,6 +41,8 @@ protected: bool runTest(const char testname[]); + void assert(const char *filename, int linenr, bool condition); + void assertEquals(const char *filename, int linenr, const std::string &expected, const std::string &actual); // the vars expected and actual need to be of type double, in order to avoid overflow of unsigned int @@ -66,6 +68,7 @@ public: #define TEST_CASE( NAME ) if ( runTest(#NAME) ) NAME (); +#define ASSERT( CONDITION ) assert(__FILE__, __LINE__, CONDITION) #define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) #define ASSERT_THROW( CMD, EXCEPTION ) try { CMD ; assertThrowFail(__FILE__, __LINE__); } catch (EXCEPTION &) { } catch (...) { assertThrowFail(__FILE__, __LINE__); } #define TODO_ASSERT_EQUALS( EXPECTED , ACTUAL ) todoAssertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL)