Fixed #5692 (Preprocessor: ifdef symbol used indirectly in code leads to 'analysis failed')
This commit is contained in:
parent
da34883770
commit
695b1f0ef3
|
@ -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> ¯oUsageList)
|
||||
{
|
||||
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 ¯o = *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 ¯o)
|
||||
void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o)
|
||||
{
|
||||
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 ..
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ¯o);
|
||||
bool validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> ¯oUsageList);
|
||||
void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue