Fixed handling of macros with known value defined in header file in configuration splitting (#8404)

Added proper unit test for configuration validation to ensure that it actually works when cppcheck is executed
This commit is contained in:
PKEuS 2018-09-26 12:17:14 +02:00
parent c312bbad78
commit 86a1b84b0c
4 changed files with 35 additions and 9 deletions

View File

@ -1110,9 +1110,9 @@ unsigned int simplecpp::TokenList::fileIndex(const std::string &filename)
namespace simplecpp {
class Macro {
public:
explicit Macro(std::vector<std::string> &f) : nameTokDef(NULL), variadic(false), valueToken(NULL), endToken(NULL), files(f), tokenListDefine(f) {}
explicit Macro(std::vector<std::string> &f) : nameTokDef(NULL), variadic(false), valueToken(NULL), endToken(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) {}
Macro(const Token *tok, std::vector<std::string> &f) : nameTokDef(NULL), files(f), tokenListDefine(f) {
Macro(const Token *tok, std::vector<std::string> &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(true) {
if (sameline(tok->previous, tok))
throw std::runtime_error("bad macro syntax");
if (tok->op != '#')
@ -1128,7 +1128,7 @@ namespace simplecpp {
throw std::runtime_error("bad macro syntax");
}
Macro(const std::string &name, const std::string &value, std::vector<std::string> &f) : nameTokDef(NULL), files(f), tokenListDefine(f) {
Macro(const std::string &name, const std::string &value, std::vector<std::string> &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) {
const std::string def(name + ' ' + value);
std::istringstream istr(def);
tokenListDefine.readfile(istr);
@ -1136,12 +1136,13 @@ namespace simplecpp {
throw std::runtime_error("bad macro syntax");
}
Macro(const Macro &macro) : nameTokDef(NULL), files(macro.files), tokenListDefine(macro.files) {
Macro(const Macro &macro) : nameTokDef(NULL), files(macro.files), tokenListDefine(macro.files), valueDefinedInCode_(macro.valueDefinedInCode_) {
*this = macro;
}
void operator=(const Macro &macro) {
if (this != &macro) {
valueDefinedInCode_ = macro.valueDefinedInCode_;
if (macro.tokenListDefine.empty())
parseDefine(macro.nameTokDef);
else {
@ -1151,6 +1152,10 @@ namespace simplecpp {
}
}
bool valueDefinedInCode() const {
return valueDefinedInCode_;
}
/**
* Expand macro. This will recursively expand inner macros.
* @param output destination tokenlist
@ -1821,6 +1826,9 @@ namespace simplecpp {
/** usage of this macro */
mutable std::list<Location> usageList;
/** was the value of this macro actually defined in the code? */
bool valueDefinedInCode_;
};
}
@ -2612,7 +2620,7 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL
const Macro &macro = macroIt->second;
const std::list<Location> &usage = macro.usage();
for (std::list<Location>::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) {
MacroUsage mu(usageIt->files);
MacroUsage mu(usageIt->files, macro.valueDefinedInCode());
mu.macroName = macro.name();
mu.macroLocation = macro.defineLocation();
mu.useLocation = *usageIt;

View File

@ -269,10 +269,11 @@ namespace simplecpp {
/** Tracking how macros are used */
struct SIMPLECPP_LIB MacroUsage {
explicit MacroUsage(const std::vector<std::string> &f) : macroLocation(f), useLocation(f) {}
explicit MacroUsage(const std::vector<std::string> &f, bool macroValueKnown_) : macroLocation(f), useLocation(f), macroValueKnown(macroValueKnown_) {}
std::string macroName;
Location macroLocation;
Location useLocation;
bool macroValueKnown;
};
/**

View File

@ -786,6 +786,8 @@ bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp
continue;
const std::string macroName(define.substr(0, define.find('(')));
for (const simplecpp::MacroUsage &mu : macroUsageList) {
if (mu.macroValueKnown)
continue;
if (mu.macroName != macroName)
continue;
bool directiveLocation = false;

View File

@ -40,6 +40,7 @@ public:
TestPreprocessor()
: TestFixture("TestPreprocessor")
, preprocessor0(settings0, this) {
settings0.addEnabled("information");
}
class OurPreprocessor : public Preprocessor {
@ -231,7 +232,8 @@ private:
TEST_CASE(getConfigsU6);
TEST_CASE(getConfigsU7);
TEST_CASE(validateCfg);
TEST_CASE(validateCfg1);
TEST_CASE(validateCfg2);
TEST_CASE(if_sizeof);
@ -2209,11 +2211,11 @@ private:
}
void validateCfg() {
void validateCfg1() {
Preprocessor preprocessor(settings0, this);
std::vector<std::string> files(1, "test.c");
simplecpp::MacroUsage macroUsage(files);
simplecpp::MacroUsage macroUsage(files, false);
macroUsage.useLocation.fileIndex = 0;
macroUsage.useLocation.line = 1;
macroUsage.macroName = "X";
@ -2224,6 +2226,19 @@ private:
ASSERT_EQUALS(false, preprocessor.validateCfg("A=42;X", macroUsageList));
ASSERT_EQUALS(true, preprocessor.validateCfg("X=1", macroUsageList));
ASSERT_EQUALS(true, preprocessor.validateCfg("Y", macroUsageList));
macroUsageList.front().macroValueKnown = true; // #8404
ASSERT_EQUALS(true, preprocessor.validateCfg("X", macroUsageList));
}
void validateCfg2() {
const char filedata[] = "#ifdef ABC\n"
"#endif\n"
"int i = ABC;";
std::map<std::string, std::string> actual;
preprocess(filedata, actual, "file.cpp");
ASSERT_EQUALS("[file.cpp:3]: (information) Skipping configuration 'ABC' since the value of 'ABC' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str());
}
void if_sizeof() { // #4071