From 17cf24ed34484a973737f86c3473df61f572ae51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 10 Jul 2012 20:29:04 +0200 Subject: [PATCH] Fixed #3643 (Preprocessor: Invalid configuration (macro is used in code)) --- lib/cppcheck.cpp | 22 +++++++++++++--- lib/preprocessor.cpp | 54 ++++++++++++++++++++++++++++++++++++++- lib/preprocessor.h | 16 +++++++++++- test/testpreprocessor.cpp | 12 +++++++++ 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 25468fc11..344714032 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -214,9 +214,6 @@ unsigned int CppCheck::processFile(const std::string& filename) } cfg = *it; - Timer t("Preprocessor::getcode", _settings._showtime, &S_timerResults); - const std::string codeWithoutCfg = preprocessor.getcode(filedata, *it, filename); - t.Stop(); // If only errors are printed, print filename after the check if (_settings._errorsOnly == false && it != configurations.begin()) { @@ -225,6 +222,10 @@ unsigned int CppCheck::processFile(const std::string& filename) _errorLogger.reportOut(std::string("Checking ") + fixedpath + ": " + cfg + std::string("...")); } + Timer t("Preprocessor::getcode", _settings._showtime, &S_timerResults); + const std::string codeWithoutCfg = preprocessor.getcode(filedata, *it, filename, _settings.userDefines.empty()); + t.Stop(); + const std::string &appendCode = _settings.append(); if (_settings.debugFalsePositive) { @@ -513,6 +514,21 @@ void CppCheck::reportProgress(const std::string &filename, const char stage[], c void CppCheck::reportInfo(const ErrorLogger::ErrorMessage &msg) { + // Suppressing info message? + std::string file; + unsigned int line(0); + if (!msg._callStack.empty()) { + file = msg._callStack.back().getfile(false); + line = msg._callStack.back().line; + } + if (_useGlobalSuppressions) { + if (_settings.nomsg.isSuppressed(msg._id, file, line)) + return; + } else { + if (_settings.nomsg.isSuppressedLocal(msg._id, file, line)) + return; + } + _errorLogger.reportInfo(msg); } diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 1b7a5db3b..082f0c02b 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -1510,7 +1510,7 @@ static std::map getcfgmap(const std::string &cfg) } -std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename) +std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename, const bool validate) { // For the error report unsigned int lineno = 0; @@ -1705,6 +1705,10 @@ std::string Preprocessor::getcode(const std::string &filedata, const std::string ret << line << "\n"; } + if (validate && !validateCfg(ret.str(), cfg)) { + return ""; + } + return expandMacros(ret.str(), filename, cfg, _errorLogger); } @@ -2552,6 +2556,53 @@ static bool getlines(std::istream &istr, std::string &line) return true; } +bool Preprocessor::validateCfg(const std::string &code, const std::string &cfg) +{ + // fill up "macros" with empty configuration macros + std::set macros; + for (std::string::size_type pos = 0; pos < cfg.size(); ++pos) { + const std::string::size_type pos2 = cfg.find_first_of(";=", pos); + if (pos2 == std::string::npos) { + macros.insert(cfg.substr(pos)); + break; + } + if (cfg[pos2] == ';') + macros.insert(cfg.substr(pos, pos2-pos)); + pos = cfg.find(";", pos2); + if (pos != std::string::npos) + ++pos; + } + + // check if any empty macros are used in code + for (std::set::const_iterator it = macros.begin(); it != macros.end(); ++it) { + const std::string ¯o = *it; + std::string::size_type pos; + for (pos = code.find(macro); pos != std::string::npos; pos = code.find(macro,pos)) { + if (pos > 0 && (std::isalnum(code[pos-1U]) || code[pos-1U] == '_')) + continue; + pos += macro.size(); + if (pos < code.size() && (std::isalnum(code[pos]) || code[pos] == '_')) + continue; + // macro is used in code, return false + if (_settings->isEnabled("information")) + validateCfgError(cfg); + return false; + } + } + + return true; +} + +void Preprocessor::validateCfgError(const std::string &cfg) +{ + const std::string id = "ConfigurationNotChecked"; + const std::list locationList; + const Severity::SeverityType severity = Severity::information; + ErrorLogger::ErrorMessage errmsg(locationList, severity, "Skipping configuration '" + cfg + "' because it seems to be invalid. Use -D if you want to check it.", id, false); + errmsg.file0 = file0; + _errorLogger->reportInfo(errmsg); +} + std::string Preprocessor::expandMacros(const std::string &code, std::string filename, const std::string &cfg, ErrorLogger *errorLogger) { // Search for macros and expand them.. @@ -2829,5 +2880,6 @@ void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *se Settings settings2(*settings); Preprocessor preprocessor(&settings2, errorLogger); preprocessor.missingInclude("", 1, "", true); + preprocessor.validateCfgError("X"); preprocessor.error("", 1, "#error message"); // #error .. } diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 445952e18..cd164a1ab 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -94,8 +94,12 @@ public: /** * Get preprocessed code for a given configuration + * @param filedata file data including #if, #define, etc + * @param cfg configuration to read out + * @param filename name of source file + * @param validate true => perform validation that empty configuration macros are not used in the code */ - std::string getcode(const std::string &filedata, const std::string &cfg, const std::string &filename); + std::string getcode(const std::string &filedata, const std::string &cfg, const std::string &filename, const bool validate = false); /** * simplify condition @@ -110,6 +114,16 @@ public: * @param processedFile The data to be processed */ static void preprocessWhitespaces(std::string &processedFile); + + /** + * make sure empty configuration macros are not used in code. the given code must be a single configuration + * @param code The input code + * @param cfg configuration + * @return true => configuration is valid + */ + bool validateCfg(const std::string &code, const std::string &cfg); + void validateCfgError(const std::string &cfg); + protected: /** diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 7f047c242..916eb8bec 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -268,6 +268,8 @@ private: TEST_CASE(undef9); TEST_CASE(macroChar); + + TEST_CASE(validateCfg); } @@ -3532,6 +3534,16 @@ private: ASSERT_EQUALS("\n" + std::string(char(1),1U) + "1\n", OurPreprocessor::expandMacros(filedata,NULL)); OurPreprocessor::macroChar = '$'; } + + void validateCfg() { + Settings settings; + Preprocessor preprocessor(&settings, this); + + ASSERT_EQUALS(false, preprocessor.validateCfg("int x=X;", "X")); + ASSERT_EQUALS(false, preprocessor.validateCfg("X=1;", "X")); + ASSERT_EQUALS(true, preprocessor.validateCfg("int x=X;", "Y")); + } + }; REGISTER_TEST(TestPreprocessor)