Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions lib/preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,10 @@ simplecpp::TokenList Preprocessor::preprocess(const std::string &cfg, std::vecto

tokens2.removeComments();

// ensure that guessed define macros without value are not used in the code
if (!validateCfg(cfg, macroUsage))
return simplecpp::TokenList(files);

return tokens2;
}

Expand Down Expand Up @@ -1059,6 +1063,44 @@ void Preprocessor::invalidSuppression(const simplecpp::Location& loc, const std:
error(loc, msg, "invalidSuppression");
}

bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList)
{
bool ret = true;
std::list<std::string> defines;
splitcfg(cfg, defines, emptyString);
for (const std::string &define : defines) {
if (define.find('=') != std::string::npos)
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;
const bool directiveLocation = std::any_of(mDirectives.cbegin(), mDirectives.cend(),
[=](const Directive &dir) {
return mu.useLocation.file() == dir.file && mu.useLocation.line == dir.linenr;
});

if (!directiveLocation) {
if (mSettings.severity.isEnabled(Severity::information))
validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName);
ret = false;
}
}
}

return ret;
}

void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
{
const std::string id = "ConfigurationNotChecked";
ErrorMessage::FileLocation loc(file, line, 0);
const ErrorMessage errmsg({std::move(loc)}, mFile0, 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, Certainty::normal);
mErrorLogger->reportInfo(errmsg);
}

void Preprocessor::getErrorMessages(ErrorLogger &errorLogger, const Settings &settings)
{
std::vector<std::string> files;
Expand All @@ -1069,6 +1111,7 @@ void Preprocessor::getErrorMessages(ErrorLogger &errorLogger, const Settings &se
loc.col = 2;
preprocessor.missingInclude(loc, "", UserHeader);
preprocessor.missingInclude(loc, "", SystemHeader);
preprocessor.validateCfgError("", 1, "X", "X");
preprocessor.error(loc, "message", simplecpp::Output::ERROR);
preprocessor.error(loc, "message", simplecpp::Output::SYNTAX_ERROR);
preprocessor.error(loc, "message", simplecpp::Output::UNHANDLED_CHAR_ERROR);
Expand Down
9 changes: 9 additions & 0 deletions lib/preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor {

std::string getcode(const std::string &cfg, std::vector<std::string> &files, bool writeLocations);

/**
* make sure empty configuration macros are not used in code. the given code must be a single configuration
* @param cfg configuration
* @param macroUsageList macro usage list
* @return true => configuration is valid
*/
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);

/**
* Calculate HASH. Using toolinfo, tokens1, filedata.
*
Expand Down
33 changes: 33 additions & 0 deletions test/testpreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@ class TestPreprocessor : public TestFixture {
TEST_CASE(getConfigsU6);
TEST_CASE(getConfigsU7);

TEST_CASE(validateCfg1);
TEST_CASE(validateCfg2);

TEST_CASE(getConfigsAndCodeIssue14317);
TEST_CASE(getConfigsMostGeneralConfigIssue14317);

Expand Down Expand Up @@ -2668,6 +2671,36 @@ class TestPreprocessor : public TestFixture {
ASSERT_EQUALS("\nY\n", getConfigsStr(code, "-DX"));
}

void validateCfg1() {
Preprocessor preprocessor(settings0, this);

std::vector<std::string> files(1, "test.c");
simplecpp::MacroUsage macroUsage(files, false);
macroUsage.useLocation.fileIndex = 0;
macroUsage.useLocation.line = 1;
macroUsage.macroName = "X";
std::list<simplecpp::MacroUsage> macroUsageList(1, macroUsage);

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));

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 getConfigsAndCodeIssue14317() {
const char filedata[] = "bool test() {\n"
"return\n"
Expand Down