Fixed #5692 (Preprocessor: ifdef symbol used indirectly in code leads to 'analysis failed')

This commit is contained in:
Daniel Marjamäki 2016-08-01 20:09:41 +02:00
parent da34883770
commit 695b1f0ef3
3 changed files with 43 additions and 110 deletions

View File

@ -511,28 +511,8 @@ std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std
}
// ensure that guessed define macros without value are not used in the code
for (std::list<std::string>::const_iterator defineIt = dui.defines.begin(); defineIt != dui.defines.end(); ++defineIt) {
if (defineIt->find("=") != std::string::npos)
continue;
const std::string macroName = defineIt->substr(0, std::min(defineIt->find("="), defineIt->find("(")));
for (std::list<simplecpp::MacroUsage>::const_iterator usageIt = macroUsage.begin(); usageIt != macroUsage.end(); ++usageIt) {
const simplecpp::MacroUsage &mu = *usageIt;
if (mu.macroName != macroName)
continue;
bool directiveLocation = false;
for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) {
directiveLocation = true;
break;
}
}
if (!directiveLocation) {
if (_settings.isEnabled("information"))
validateCfgError(cfg, macroName);
return "";
}
}
}
if (!validateCfg(cfg, macroUsage))
return "";
// assembler code locations..
std::set<simplecpp::Location> assemblerLocations;
@ -690,72 +670,42 @@ void Preprocessor::missingInclude(const std::string &filename, unsigned int line
}
}
bool Preprocessor::validateCfg(const std::string &code, const std::string &cfg)
bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList)
{
const bool printInformation = _settings.isEnabled("information");
// fill up "macros" with empty configuration macros
std::set<std::string> macros;
for (std::string::size_type pos = 0; pos < cfg.size();) {
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<std::string>::const_iterator it = macros.begin(); it != macros.end(); ++it) {
const std::string &macro = *it;
std::string::size_type pos = 0;
while ((pos = code.find_first_of(std::string("#\"'")+macro[0], pos)) != std::string::npos) {
const std::string::size_type pos1 = pos;
const std::string::size_type pos2 = pos + macro.size();
pos++;
// skip string..
if (code[pos1] == '\"' || code[pos1] == '\'') {
while (pos < code.size() && code[pos] != code[pos1]) {
if (code[pos] == '\\')
++pos;
++pos;
bool ret = true;
std::list<std::string> defines;
splitcfg(cfg, defines, std::string());
for (std::list<std::string>::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) {
if (defineIt->find("=") != std::string::npos)
continue;
const std::string macroName(defineIt->substr(0, defineIt->find("(")));
for (std::list<simplecpp::MacroUsage>::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) {
const simplecpp::MacroUsage &mu = *usageIt;
if (mu.macroName != macroName)
continue;
bool directiveLocation = false;
for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) {
directiveLocation = true;
break;
}
++pos;
}
// skip preprocessor statement..
else if (code[pos1] == '#') {
if (pos1 == 0 || code[pos1-1] == '\n')
pos = code.find('\n', pos);
}
// is macro used in code?
else if (code.compare(pos1,macro.size(),macro) == 0) {
if (pos1 > 0 && (std::isalnum((unsigned char)code[pos1-1U]) || code[pos1-1U] == '_'))
continue;
if (pos2 < code.size() && (std::isalnum((unsigned char)code[pos2]) || code[pos2] == '_'))
continue;
// macro is used in code, return false
if (printInformation)
validateCfgError(cfg, macro);
return false;
if (!directiveLocation) {
if (_settings.isEnabled("information"))
validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName);
ret = false;
}
}
}
return true;
return ret;
}
void Preprocessor::validateCfgError(const std::string &cfg, const std::string &macro)
void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
{
const std::string id = "ConfigurationNotChecked";
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc(file0, 1);
ErrorLogger::ErrorMessage::FileLocation loc(file, line);
locationList.push_back(loc);
ErrorLogger::ErrorMessage errmsg(locationList, file0, Severity::information, "Skipping configuration '" + cfg + "' since the value of '" + macro + "' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.", id, false);
_errorLogger->reportInfo(errmsg);
@ -768,7 +718,7 @@ void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *se
settings2.checkConfiguration=true;
preprocessor.missingInclude("", 1, "", UserHeader);
preprocessor.missingInclude("", 1, "", SystemHeader);
preprocessor.validateCfgError("X", "X");
preprocessor.validateCfgError("", 1, "X", "X");
preprocessor.error("", 1, "#error message"); // #error ..
}

View File

@ -153,12 +153,12 @@ public:
/**
* 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
* @param macroUsageList macro usage list
* @return true => configuration is valid
*/
bool validateCfg(const std::string &code, const std::string &cfg);
void validateCfgError(const std::string &cfg, const std::string &macro);
bool validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList);
void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro);
private:

View File

@ -2114,37 +2114,20 @@ private:
Settings settings;
Preprocessor preprocessor(settings, this);
ASSERT_EQUALS(true, preprocessor.validateCfg("", "X=42")); // don't hang when parsing cfg
ASSERT_EQUALS(false, preprocessor.validateCfg("int y=Y;", "X=42;Y"));
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"));
ASSERT_EQUALS(true, preprocessor.validateCfg("FOO_DEBUG()", "DEBUG"));
ASSERT_EQUALS(true, preprocessor.validateCfg("\"DEBUG()\"", "DEBUG"));
ASSERT_EQUALS(true, preprocessor.validateCfg("\"\\\"DEBUG()\"", "DEBUG"));
ASSERT_EQUALS(false, preprocessor.validateCfg("\"DEBUG()\" DEBUG", "DEBUG"));
ASSERT_EQUALS(true, preprocessor.validateCfg("#undef DEBUG", "DEBUG"));
std::list<simplecpp::MacroUsage> macroUsageList;
std::vector<std::string> files;
files.push_back("test.c");
simplecpp::MacroUsage macroUsage(files);
macroUsage.useLocation.fileIndex = 0;
macroUsage.useLocation.line = 1;
macroUsage.macroName = "X";
macroUsageList.push_back(macroUsage);
// #4301:
// #ifdef A
// int a = A; // <- using macro. must use -D so "A" will get a proper value
errout.str("");
settings.addEnabled("all");
preprocessor.setFile0("test.c");
ASSERT_EQUALS(false, preprocessor.validateCfg("int a=A;", "A"));
ASSERT_EQUALS("[test.c:1]: (information) Skipping configuration 'A' since the value of 'A' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str());
// #4949:
// #ifdef A
// a |= A; // <- using macro. must use -D so "A" will get a proper value
errout.str("");
Settings settings1;
settings = settings1;
ASSERT_EQUALS("", preprocessor.getcode("if (x) a|=A;", "A", "test.c"));
ASSERT_EQUALS("", errout.str());
settings.addEnabled("information");
ASSERT_EQUALS("", preprocessor.getcode("if (x) a|=A;", "A", "test.c"));
ASSERT_EQUALS("[test.c:1]: (information) Skipping configuration 'A' since the value of 'A' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str());
ASSERT_EQUALS(true, preprocessor.validateCfg("", macroUsageList));
ASSERT_EQUALS(false, preprocessor.validateCfg("X",macroUsageList));
ASSERT_EQUALS(false, preprocessor.validateCfg("A=42;X", macroUsageList));
ASSERT_EQUALS(true, preprocessor.validateCfg("X=1", macroUsageList));
ASSERT_EQUALS(true, preprocessor.validateCfg("Y", macroUsageList));
}
void if_sizeof() { // #4071