Skip to content
Merged
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
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ifndef VERBOSE
endif
# To compile with rules, use 'make HAVE_RULES=yes'
ifndef HAVE_RULES
HAVE_RULES=no
HAVE_RULES=
endif

ifndef MATCHCOMPILER
Expand Down Expand Up @@ -160,6 +160,8 @@ ifeq ($(HAVE_RULES),yes)
else
LIBS=$(shell $(PCRE_CONFIG) --libs)
endif
else ifneq ($(HAVE_RULES),)
$(error invalid HAVE_RULES value '$(HAVE_RULES)')
endif

ifndef PREFIX
Expand Down
10 changes: 10 additions & 0 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,16 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
return Result::Fail;
}

if (rule.tokenlist.empty()) {
mLogger.printError("unable to load rule-file '" + ruleFile + "' - a rule is lacking a tokenlist.");
return Result::Fail;
}

if (rule.tokenlist != "normal" && rule.tokenlist != "define" && rule.tokenlist != "raw") {
mLogger.printError("unable to load rule-file '" + ruleFile + "' - a rule is using the unsupported tokenlist '" + rule.tokenlist + "'.");
return Result::Fail;
}

mSettings.rules.emplace_back(std::move(rule));
}
} else {
Expand Down
55 changes: 17 additions & 38 deletions lib/cppcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -819,20 +819,17 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string

#ifdef HAVE_RULES
// Run define rules on raw code
const auto rules_it = std::find_if(mSettings.rules.cbegin(), mSettings.rules.cend(), [](const Settings::Rule& rule) {
return rule.tokenlist == "define";
});
if (rules_it != mSettings.rules.cend()) {
if (hasRule("define")) {
std::string code;
const std::list<Directive> &directives = preprocessor.getDirectives();
for (const Directive &dir : directives) {
for (const Directive &dir : preprocessor.getDirectives()) {
if (startsWith(dir.str,"#define ") || startsWith(dir.str,"#include "))
code += "#line " + std::to_string(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n';
}
Tokenizer tokenizer2(mSettings, this);
TokenList tokenlist(&mSettings);
std::istringstream istr2(code);
tokenizer2.list.createTokens(istr2, Path::identify(*files.begin()));
executeRules("define", tokenizer2);
// TODO: asserts when file has unknown extension
tokenlist.createTokens(istr2, Path::identify(*files.begin())); // TODO: check result?
executeRules("define", tokenlist);
}
#endif

Expand Down Expand Up @@ -924,8 +921,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
if (mSettings.checkConfiguration)
continue;

// Check raw tokens
checkRawTokens(tokenizer);
#ifdef HAVE_RULES
// Execute rules for "raw" code
executeRules("raw", tokenizer.list);
#endif

// Simplify tokens into normal form, skip rest of iteration if failed
if (!tokenizer.simplifyTokens1(mCurrentConfig))
Expand Down Expand Up @@ -959,13 +958,6 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string

// Check normal tokens
checkNormalTokens(tokenizer);

#ifdef HAVE_RULES
// handling of "simple" rules has been removed.
if (hasRule("simple"))
throw InternalError(nullptr, "Handling of \"simple\" rules has been removed in Cppcheck. Use --addon instead.");
#endif

} catch (const simplecpp::Output &o) {
// #error etc during preprocessing
configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + std::to_string(o.location.line) + "] " + o.msg);
Expand Down Expand Up @@ -1072,19 +1064,6 @@ void CppCheck::internalError(const std::string &filename, const std::string &msg
mErrorLogger.reportErr(errmsg);
}

//---------------------------------------------------------------------------
// CppCheck - A function that checks a raw token list
//---------------------------------------------------------------------------
void CppCheck::checkRawTokens(const Tokenizer &tokenizer)
{
#ifdef HAVE_RULES
// Execute rules for "raw" code
executeRules("raw", tokenizer);
#else
(void)tokenizer;
#endif
}

//---------------------------------------------------------------------------
// CppCheck - A function that checks a normal token list
//---------------------------------------------------------------------------
Expand Down Expand Up @@ -1169,7 +1148,7 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
}

#ifdef HAVE_RULES
executeRules("normal", tokenizer);
executeRules("normal", tokenizer.list);
#endif
}

Expand Down Expand Up @@ -1312,15 +1291,15 @@ static const char * pcreErrorCodeToString(const int pcreExecRet)
return "";
}

void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer)
void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list)
{
// There is no rule to execute
if (!hasRule(tokenlist))
return;

// Write all tokens in a string that can be parsed by pcre
std::ostringstream ostr;
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next())
for (const Token *tok = list.front(); tok; tok = tok->next())
ostr << " " << tok->str();
const std::string str(ostr.str());

Expand Down Expand Up @@ -1400,14 +1379,14 @@ void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &token
pos = (int)pos2;

// determine location..
std::string file = tokenizer.list.getSourceFilePath();
std::string file = list.getSourceFilePath();
int line = 0;

std::size_t len = 0;
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
for (const Token *tok = list.front(); tok; tok = tok->next()) {
len = len + 1U + tok->str().size();
if (len > pos1) {
file = tokenizer.list.getFiles().at(tok->fileIndex());
file = list.getFiles().at(tok->fileIndex());
line = tok->linenr();
break;
}
Expand All @@ -1421,7 +1400,7 @@ void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &token
summary = "found '" + str.substr(pos1, pos2 - pos1) + "'";
else
summary = rule.summary;
const ErrorMessage errmsg({std::move(loc)}, tokenizer.list.getSourceFilePath(), rule.severity, summary, rule.id, Certainty::normal);
const ErrorMessage errmsg({std::move(loc)}, list.getSourceFilePath(), rule.severity, summary, rule.id, Certainty::normal);

// Report error
reportErr(errmsg);
Expand Down
14 changes: 4 additions & 10 deletions lib/cppcheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include <unordered_set>
#include <vector>

class Tokenizer;
class TokenList;
enum class SHOWTIME_MODES;
struct FileSettings;
class CheckUnusedFunctions;
Expand Down Expand Up @@ -169,12 +169,6 @@ class CPPCHECKLIB CppCheck : ErrorLogger {
*/
unsigned int checkFile(const std::string& filename, const std::string &cfgname, std::istream* fileStream = nullptr);

/**
* @brief Check raw tokens
* @param tokenizer tokenizer instance
*/
void checkRawTokens(const Tokenizer &tokenizer);

/**
* @brief Check normal tokens
* @param tokenizer tokenizer instance
Expand All @@ -195,10 +189,10 @@ class CPPCHECKLIB CppCheck : ErrorLogger {
#ifdef HAVE_RULES
/**
* @brief Execute rules, if any
* @param tokenlist token list to use (normal / simple)
* @param tokenizer tokenizer
* @param tokenlist token list to use (define / normal / raw)
* @param list token list
*/
void executeRules(const std::string &tokenlist, const Tokenizer &tokenizer);
void executeRules(const std::string &tokenlist, const TokenList &list);
#endif

unsigned int checkClang(const std::string &path);
Expand Down
2 changes: 1 addition & 1 deletion lib/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static std::vector<std::string> getnames(const char *names)
static void gettokenlistfromvalid(const std::string& valid, bool cpp, TokenList& tokenList)
{
std::istringstream istr(valid + ',');
tokenList.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C);
tokenList.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C); // TODO: check result?
for (Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok,"- %num%")) {
tok->str("-" + tok->strAt(1));
Expand Down
2 changes: 1 addition & 1 deletion lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7583,7 +7583,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
ValueType valuetype;
TokenList tokenList(&mSettings);
std::istringstream istr(typestr+";");
tokenList.createTokens(istr, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C);
tokenList.createTokens(istr, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C); // TODO: check result?
tokenList.simplifyStdType();
if (parsedecl(tokenList.front(), &valuetype, mDefaultSignedness, mSettings)) {
valuetype.originalTypeName = typestr;
Expand Down
2 changes: 1 addition & 1 deletion lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3815,7 +3815,7 @@ static bool isNotEqual(std::pair<const Token*, const Token*> x, const std::strin
{
TokenList tokenList(nullptr);
std::istringstream istr(y);
tokenList.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C);
tokenList.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C); // TODO: check result?
return isNotEqual(x, std::make_pair(tokenList.front(), tokenList.back()));
}
static bool isNotEqual(std::pair<const Token*, const Token*> x, const ValueType* y, bool cpp)
Expand Down
48 changes: 48 additions & 0 deletions test/cli/other_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1180,3 +1180,51 @@ def test_unknown_extension(tmpdir):
assert exitcode == 0, stderr
assert stdout == ''
assert stderr == ''


def test_multiple_define_rules(tmpdir):
rule_file = os.path.join(tmpdir, 'rule_file.xml')
with open(rule_file, 'wt') as f:
f.write("""
<rules>
<rule>
<tokenlist>define</tokenlist>
<pattern>DEF_1</pattern>
<message>
<severity>error</severity>
<id>ruleId1</id>
</message>
</rule>
<rule>
<tokenlist>define</tokenlist>
<pattern>DEF_2</pattern>
<message>
<severity>error</severity>
<id>ruleId2</id>
</message>
</rule>
</rules>""")

test_file = os.path.join(tmpdir, 'test.c')
with open(test_file, 'wt') as f:
f.write('''
#define DEF_1
#define DEF_2
void f() { }
''')

exitcode, stdout, stderr = cppcheck(['--template=simple', '--rule-file={}'.format(rule_file), test_file])
assert exitcode == 0, stderr
lines = stdout.splitlines()
assert lines == [
'Checking {} ...'.format(test_file),
'Processing rule: DEF_1',
'Processing rule: DEF_2'
]
lines = stderr.splitlines()
assert lines == [
"{}:2:0: error: found 'DEF_1' [ruleId1]".format(test_file),
"{}:3:0: error: found 'DEF_2' [ruleId2]".format(test_file)
]

# TODO: test "raw" and "normal" rules
38 changes: 32 additions & 6 deletions test/testcmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ class TestCmdlineParser : public TestFixture {
TEST_CASE(ruleFileEmptyElements2);
TEST_CASE(ruleFileUnknownElement1);
TEST_CASE(ruleFileUnknownElement2);
TEST_CASE(ruleFileMissingTokenList);
TEST_CASE(ruleFileUnknownTokenList);
#else
TEST_CASE(ruleFileNotSupported);
#endif
Expand Down Expand Up @@ -2157,7 +2159,7 @@ class TestCmdlineParser : public TestFixture {
ScopedFile file("rule.xml",
"<rules>\n"
"<rule>\n"
"<tokenlist>toklist1</tokenlist>\n"
"<tokenlist>raw</tokenlist>\n"
"<pattern>.+</pattern>\n"
"<message>\n"
"<severity>error</severity>\n"
Expand All @@ -2166,7 +2168,7 @@ class TestCmdlineParser : public TestFixture {
"</message>\n"
"</rule>\n"
"<rule>\n"
"<tokenlist>toklist2</tokenlist>\n"
"<tokenlist>define</tokenlist>\n"
"<pattern>.*</pattern>\n"
"<message>\n"
"<severity>warning</severity>\n"
Expand All @@ -2179,13 +2181,13 @@ class TestCmdlineParser : public TestFixture {
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv));
ASSERT_EQUALS(2, settings->rules.size());
auto it = settings->rules.cbegin();
ASSERT_EQUALS("toklist1", it->tokenlist);
ASSERT_EQUALS("raw", it->tokenlist);
ASSERT_EQUALS(".+", it->pattern);
ASSERT_EQUALS_ENUM(Severity::error, it->severity);
ASSERT_EQUALS("ruleId1", it->id);
ASSERT_EQUALS("ruleSummary1", it->summary);
++it;
ASSERT_EQUALS("toklist2", it->tokenlist);
ASSERT_EQUALS("define", it->tokenlist);
ASSERT_EQUALS(".*", it->pattern);
ASSERT_EQUALS_ENUM(Severity::warning, it->severity);
ASSERT_EQUALS("ruleId2", it->id);
Expand All @@ -2196,7 +2198,7 @@ class TestCmdlineParser : public TestFixture {
REDIRECT;
ScopedFile file("rule.xml",
"<rule>\n"
"<tokenlist>toklist</tokenlist>\n"
"<tokenlist>define</tokenlist>\n"
"<pattern>.+</pattern>\n"
"<message>\n"
"<severity>error</severity>\n"
Expand All @@ -2208,7 +2210,7 @@ class TestCmdlineParser : public TestFixture {
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv));
ASSERT_EQUALS(1, settings->rules.size());
auto it = settings->rules.cbegin();
ASSERT_EQUALS("toklist", it->tokenlist);
ASSERT_EQUALS("define", it->tokenlist);
ASSERT_EQUALS(".+", it->pattern);
ASSERT_EQUALS_ENUM(Severity::error, it->severity);
ASSERT_EQUALS("ruleId", it->id);
Expand Down Expand Up @@ -2300,6 +2302,30 @@ class TestCmdlineParser : public TestFixture {
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - unknown element 'pattern' encountered in 'message'.\n", logger->str());
}

void ruleFileMissingTokenList() {
REDIRECT;
ScopedFile file("rule.xml",
"<rule>\n"
"<tokenlist/>\n"
"<pattern>.+</pattern>\n"
"</rule>\n");
const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"};
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule is lacking a tokenlist.\n", logger->str());
}

void ruleFileUnknownTokenList() {
REDIRECT;
ScopedFile file("rule.xml",
"<rule>\n"
"<tokenlist>simple</tokenlist>\n"
"<pattern>.+</pattern>\n"
"</rule>\n");
const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"};
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule is using the unsupported tokenlist 'simple'.\n", logger->str());
}
#else
void ruleFileNotSupported() {
REDIRECT;
Expand Down
4 changes: 3 additions & 1 deletion tools/dmake/dmake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ int main(int argc, char **argv)
<< "endif\n";

fout << "# To compile with rules, use 'make HAVE_RULES=yes'\n";
makeConditionalVariable(fout, "HAVE_RULES", "no");
makeConditionalVariable(fout, "HAVE_RULES", "");

makeMatchcompiler(fout, emptyString, emptyString);

Expand Down Expand Up @@ -743,6 +743,8 @@ int main(int argc, char **argv)
<< " else\n"
<< " LIBS=$(shell $(PCRE_CONFIG) --libs)\n"
<< " endif\n"
<< "else ifneq ($(HAVE_RULES),)\n"
<< " $(error invalid HAVE_RULES value '$(HAVE_RULES)')\n"
<< "endif\n\n";

makeConditionalVariable(fout, "PREFIX", "/usr");
Expand Down