Inline suppression for block of code or whole file (#5402)

Added new suppress comments:
- `cppcheck-suppress-begin` and `cppcheck-suppress-end` to remove blocks
of suppression
- `cppcheck-suppress-file` to remove suppression at file level

The suppressions do not interfere with each others. For example, all the
suppressions are matched in the following code:

```c
// cppcheck-suppress-file uninitvar
void f() {
    int a;
    // cppcheck-suppress-begin uninitvar
    // cppcheck-suppress uninitvar
    a++;
    // cppcheck-suppress-end uninitvar
}
```

Tickets:
https://trac.cppcheck.net/ticket/11902
https://trac.cppcheck.net/ticket/8528
This commit is contained in:
Johan Bertrand 2023-10-13 12:45:13 +02:00 committed by GitHub
parent 8ef4da475a
commit 44ab976451
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 560 additions and 39 deletions

View File

@ -76,8 +76,9 @@ Preprocessor::~Preprocessor()
namespace { namespace {
struct BadInlineSuppression { struct BadInlineSuppression {
BadInlineSuppression(const simplecpp::Location &l, std::string msg) : location(l), errmsg(std::move(msg)) {} BadInlineSuppression(std::string file, const int line, std::string msg) : file(std::move(file)), line(line), errmsg(std::move(msg)) {}
simplecpp::Location location; std::string file;
int line;
std::string errmsg; std::string errmsg;
}; };
} }
@ -97,18 +98,50 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std:
if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress) if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress)
return false; return false;
// skip spaces after "cppcheck-suppress" // check if it has a prefix
const std::string::size_type pos2 = comment.find_first_not_of(' ', pos1+cppchecksuppress.size()); const std::string::size_type posEndComment = comment.find_first_of(" [", pos1+cppchecksuppress.size());
// skip spaces after "cppcheck-suppress" and its possible prefix
const std::string::size_type pos2 = comment.find_first_not_of(' ', posEndComment);
if (pos2 == std::string::npos) if (pos2 == std::string::npos)
return false; return false;
Suppressions::Type errorType = Suppressions::Type::unique;
// determine prefix if specified
if (posEndComment >= (pos1 + cppchecksuppress.size() + 1)) {
if (comment.at(pos1 + cppchecksuppress.size()) != '-')
return false;
const unsigned int argumentLength =
posEndComment - (pos1 + cppchecksuppress.size() + 1);
const std::string suppressTypeString =
comment.substr(pos1 + cppchecksuppress.size() + 1, argumentLength);
if ("file" == suppressTypeString) {
errorType = Suppressions::Type::file;
} else if ("begin" == suppressTypeString) {
errorType = Suppressions::Type::blockBegin;
} else if ("end" == suppressTypeString) {
errorType = Suppressions::Type::blockEnd;
} else {
return false;
}
}
if (comment[pos2] == '[') { if (comment[pos2] == '[') {
// multi suppress format // multi suppress format
std::string errmsg; std::string errmsg;
std::vector<Suppressions::Suppression> suppressions = Suppressions::parseMultiSuppressComment(comment, &errmsg); std::vector<Suppressions::Suppression> suppressions = Suppressions::parseMultiSuppressComment(comment, &errmsg);
for (Suppressions::Suppression &s : suppressions) {
s.type = errorType;
s.lineNumber = tok->location.line;
}
if (!errmsg.empty()) if (!errmsg.empty())
bad.emplace_back(tok->location, std::move(errmsg)); bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg));
std::copy_if(suppressions.cbegin(), suppressions.cend(), std::back_inserter(inlineSuppressions), [](const Suppressions::Suppression& s) { std::copy_if(suppressions.cbegin(), suppressions.cend(), std::back_inserter(inlineSuppressions), [](const Suppressions::Suppression& s) {
return !s.errorId.empty(); return !s.errorId.empty();
@ -120,21 +153,30 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std:
if (!s.parseComment(comment, &errmsg)) if (!s.parseComment(comment, &errmsg))
return false; return false;
s.type = errorType;
s.lineNumber = tok->location.line;
if (!s.errorId.empty()) if (!s.errorId.empty())
inlineSuppressions.push_back(std::move(s)); inlineSuppressions.push_back(std::move(s));
if (!errmsg.empty()) if (!errmsg.empty())
bad.emplace_back(tok->location, std::move(errmsg)); bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg));
} }
return true; return true;
} }
static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, Suppressions &suppressions, std::list<BadInlineSuppression> &bad) static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, Suppressions &suppressions, std::list<BadInlineSuppression> &bad)
{ {
std::list<Suppressions::Suppression> inlineSuppressionsBlockBegin;
bool onlyComments = true;
for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
if (!tok->comment) if (!tok->comment) {
onlyComments = false;
continue; continue;
}
std::list<Suppressions::Suppression> inlineSuppressions; std::list<Suppressions::Suppression> inlineSuppressions;
if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad)) if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad))
@ -142,18 +184,27 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett
if (!sameline(tok->previous, tok)) { if (!sameline(tok->previous, tok)) {
// find code after comment.. // find code after comment..
tok = tok->next; if (tok->next) {
while (tok && tok->comment) {
parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad);
tok = tok->next; tok = tok->next;
while (tok->comment) {
parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad);
if (tok->next) {
tok = tok->next;
} else {
break;
}
}
} }
if (!tok)
break;
} }
if (inlineSuppressions.empty()) if (inlineSuppressions.empty())
continue; continue;
// It should never happen
if (!tok)
continue;
// Relative filename // Relative filename
std::string relativeFilename(tok->location.file()); std::string relativeFilename(tok->location.file());
if (settings.relativePaths) { if (settings.relativePaths) {
@ -166,23 +217,66 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett
} }
relativeFilename = Path::simplifyPath(relativeFilename); relativeFilename = Path::simplifyPath(relativeFilename);
// special handling when suppressing { warnings for backwards compatibility
const bool thisAndNextLine = tok->previous &&
tok->previous->previous &&
tok->next &&
!sameline(tok->previous->previous, tok->previous) &&
tok->location.line + 1 == tok->next->location.line &&
tok->location.fileIndex == tok->next->location.fileIndex &&
tok->previous->str() == "{";
// Add the suppressions. // Add the suppressions.
for (Suppressions::Suppression &suppr : inlineSuppressions) { for (Suppressions::Suppression &suppr : inlineSuppressions) {
suppr.fileName = relativeFilename; suppr.fileName = relativeFilename;
suppr.lineNumber = tok->location.line;
suppr.thisAndNextLine = thisAndNextLine; if (Suppressions::Type::blockBegin == suppr.type)
suppressions.addSuppression(std::move(suppr)); {
inlineSuppressionsBlockBegin.push_back(std::move(suppr));
} else if (Suppressions::Type::blockEnd == suppr.type) {
bool throwError = true;
if (!inlineSuppressionsBlockBegin.empty()) {
const Suppressions::Suppression lastBeginSuppression = inlineSuppressionsBlockBegin.back();
for (const Suppressions::Suppression &supprBegin : inlineSuppressionsBlockBegin)
{
if (lastBeginSuppression.lineNumber != supprBegin.lineNumber)
continue;
if (suppr.symbolName == supprBegin.symbolName && suppr.lineNumber > supprBegin.lineNumber) {
suppr.lineBegin = supprBegin.lineNumber;
suppr.lineEnd = suppr.lineNumber;
suppr.lineNumber = supprBegin.lineNumber;
suppr.type = Suppressions::Type::block;
inlineSuppressionsBlockBegin.remove(supprBegin);
suppressions.addSuppression(std::move(suppr));
throwError = false;
break;
}
}
}
if (throwError) {
// NOLINTNEXTLINE(bugprone-use-after-move) - moved only when thrownError is false
bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress End: No matching begin");
}
} else if (Suppressions::Type::unique == suppr.type) {
// special handling when suppressing { warnings for backwards compatibility
const bool thisAndNextLine = tok->previous &&
tok->previous->previous &&
tok->next &&
!sameline(tok->previous->previous, tok->previous) &&
tok->location.line + 1 == tok->next->location.line &&
tok->location.fileIndex == tok->next->location.fileIndex &&
tok->previous->str() == "{";
suppr.thisAndNextLine = thisAndNextLine;
suppr.lineNumber = tok->location.line;
suppressions.addSuppression(std::move(suppr));
} else if (Suppressions::Type::file == suppr.type) {
if (onlyComments)
suppressions.addSuppression(std::move(suppr));
else
bad.emplace_back(suppr.fileName, suppr.lineNumber, "File suppression should be at the top of the file");
}
} }
} }
std::for_each(inlineSuppressionsBlockBegin.begin(), inlineSuppressionsBlockBegin.end(), [&](const Suppressions::Suppression & suppr) {
bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress Begin: No matching end");
});
} }
void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppressions &suppressions) void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppressions &suppressions)
@ -190,13 +284,13 @@ void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppre
if (!mSettings.inlineSuppressions) if (!mSettings.inlineSuppressions)
return; return;
std::list<BadInlineSuppression> err; std::list<BadInlineSuppression> err;
::addinlineSuppressions(tokens, mSettings, suppressions, err); ::addInlineSuppressions(tokens, mSettings, suppressions, err);
for (std::map<std::string,simplecpp::TokenList*>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) { for (std::map<std::string,simplecpp::TokenList*>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
if (it->second) if (it->second)
::addinlineSuppressions(*it->second, mSettings, suppressions, err); ::addInlineSuppressions(*it->second, mSettings, suppressions, err);
} }
for (const BadInlineSuppression &bad : err) { for (const BadInlineSuppression &bad : err) {
error(bad.location.file(), bad.location.line, bad.errmsg); error(bad.file, bad.line, bad.errmsg);
} }
} }

View File

@ -165,6 +165,8 @@ std::vector<Suppressions::Suppression> Suppressions::parseMultiSuppressComment(c
return suppressions; return suppressions;
} }
const std::string SymbolNameString = "symbolName=";
while (iss) { while (iss) {
std::string word; std::string word;
iss >> word; iss >> word;
@ -172,8 +174,8 @@ std::vector<Suppressions::Suppression> Suppressions::parseMultiSuppressComment(c
break; break;
if (word.find_first_not_of("+-*/%#;") == std::string::npos) if (word.find_first_not_of("+-*/%#;") == std::string::npos)
break; break;
if (startsWith(word, "symbolName=")) { if (startsWith(word, SymbolNameString)) {
s.symbolName = word.substr(11); s.symbolName = word.substr(SymbolNameString.size());
} else { } else {
if (errorMessage && errorMessage->empty()) if (errorMessage && errorMessage->empty())
*errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]";
@ -302,22 +304,28 @@ bool Suppressions::Suppression::parseComment(std::string comment, std::string *e
if (comment.compare(comment.size() - 2, 2, "*/") == 0) if (comment.compare(comment.size() - 2, 2, "*/") == 0)
comment.erase(comment.size() - 2, 2); comment.erase(comment.size() - 2, 2);
const std::string cppchecksuppress = "cppcheck-suppress";
std::istringstream iss(comment.substr(2)); std::istringstream iss(comment.substr(2));
std::string word; std::string word;
iss >> word; iss >> word;
if (word != "cppcheck-suppress") if (word.substr(0, cppchecksuppress.size()) != cppchecksuppress)
return false; return false;
iss >> errorId; iss >> errorId;
if (!iss) if (!iss)
return false; return false;
const std::string SymbolNameString = "symbolName=";
while (iss) { while (iss) {
iss >> word; iss >> word;
if (!iss) if (!iss)
break; break;
if (word.find_first_not_of("+-*/%#;") == std::string::npos) if (word.find_first_not_of("+-*/%#;") == std::string::npos)
break; break;
if (startsWith(word,"symbolName=")) if (startsWith(word, SymbolNameString))
symbolName = word.substr(11); symbolName = word.substr(SymbolNameString.size());
else if (errorMessage && errorMessage->empty()) else if (errorMessage && errorMessage->empty())
*errorMessage = "Bad suppression attribute '" + word + "'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym"; *errorMessage = "Bad suppression attribute '" + word + "'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym";
} }
@ -332,10 +340,12 @@ bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &e
return false; return false;
if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName()))
return false; return false;
if (lineNumber != NO_LINE && lineNumber != errmsg.lineNumber) { if ((Suppressions::Type::unique == type) && (lineNumber != NO_LINE) && (lineNumber != errmsg.lineNumber)) {
if (!thisAndNextLine || lineNumber + 1 != errmsg.lineNumber) if (!thisAndNextLine || lineNumber + 1 != errmsg.lineNumber)
return false; return false;
} }
if ((Suppressions::Type::block == type) && ((errmsg.lineNumber < lineBegin) || (errmsg.lineNumber > lineEnd)))
return false;
if (!symbolName.empty()) { if (!symbolName.empty()) {
for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) { for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) {
const std::string::size_type pos2 = errmsg.symbolNames.find('\n',pos); const std::string::size_type pos2 = errmsg.symbolNames.find('\n',pos);
@ -385,15 +395,16 @@ std::string Suppressions::Suppression::getText() const
bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg, bool global) bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg, bool global)
{ {
const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression"); const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression");
bool return_value = false;
for (Suppression &s : mSuppressions) { for (Suppression &s : mSuppressions) {
if (!global && !s.isLocal()) if (!global && !s.isLocal())
continue; continue;
if (unmatchedSuppression && s.errorId != errmsg.errorId) if (unmatchedSuppression && s.errorId != errmsg.errorId)
continue; continue;
if (s.isMatch(errmsg)) if (s.isMatch(errmsg))
return true; return_value = true;
} }
return false; return return_value;
} }
bool Suppressions::isSuppressed(const ::ErrorMessage &errmsg) bool Suppressions::isSuppressed(const ::ErrorMessage &errmsg)
@ -470,7 +481,15 @@ void Suppressions::markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tok
currLineNr = tok->linenr(); currLineNr = tok->linenr();
currFileIdx = tok->fileIndex(); currFileIdx = tok->fileIndex();
for (auto &suppression : mSuppressions) { for (auto &suppression : mSuppressions) {
if (!suppression.checked && (suppression.lineNumber == currLineNr) && (suppression.fileName == tokenizer.list.file(tok))) { if (suppression.type == Suppressions::Type::unique) {
if (!suppression.checked && (suppression.lineNumber == currLineNr) && (suppression.fileName == tokenizer.list.file(tok))) {
suppression.checked = true;
}
} else if (suppression.type == Suppressions::Type::block) {
if ((!suppression.checked && (suppression.lineBegin <= currLineNr) && (suppression.lineEnd >= currLineNr) && (suppression.fileName == tokenizer.list.file(tok)))) {
suppression.checked = true;
}
} else if (!suppression.checked && suppression.fileName == tokenizer.list.file(tok)) {
suppression.checked = true; suppression.checked = true;
} }
} }

View File

@ -41,6 +41,10 @@ enum class Certainty;
class CPPCHECKLIB Suppressions { class CPPCHECKLIB Suppressions {
public: public:
enum class Type {
unique, file, block, blockBegin, blockEnd
};
struct CPPCHECKLIB ErrorMessage { struct CPPCHECKLIB ErrorMessage {
std::size_t hash; std::size_t hash;
std::string errorId; std::string errorId;
@ -77,6 +81,26 @@ public:
return false; return false;
} }
bool operator==(const Suppression &other) const {
if (errorId != other.errorId)
return false;
if (lineNumber < other.lineNumber)
return false;
if (fileName != other.fileName)
return false;
if (symbolName != other.symbolName)
return false;
if (hash != other.hash)
return false;
if (type != other.type)
return false;
if (lineBegin != other.lineBegin)
return false;
if (lineEnd != other.lineEnd)
return false;
return true;
}
/** /**
* Parse inline suppression in comment * Parse inline suppression in comment
* @param comment the full comment text * @param comment the full comment text
@ -107,6 +131,9 @@ public:
std::string errorId; std::string errorId;
std::string fileName; std::string fileName;
int lineNumber = NO_LINE; int lineNumber = NO_LINE;
int lineBegin = NO_LINE;
int lineEnd = NO_LINE;
Type type = Type::unique;
std::string symbolName; std::string symbolName;
std::size_t hash{}; std::size_t hash{};
bool thisAndNextLine{}; // Special case for backwards compatibility: { // cppcheck-suppress something bool thisAndNextLine{}; // Special case for backwards compatibility: { // cppcheck-suppress something

View File

@ -91,6 +91,11 @@ bool startsWith(const std::string& str, const char (&start)[N])
return startsWith(str, start, N - 1); return startsWith(str, start, N - 1);
} }
inline bool startsWith(const std::string& str, const std::string& start)
{
return startsWith(str, start.c_str(), start.length());
}
inline bool endsWith(const std::string &str, char c) inline bool endsWith(const std::string &str, char c)
{ {
return !str.empty() && str.back() == c; return !str.empty() && str.back() == c;

View File

@ -531,6 +531,26 @@ Suppressing multiple ids in one comment by using []:
// cppcheck-suppress [aaaa, bbbb] // cppcheck-suppress [aaaa, bbbb]
Suppressing warnings `aaaa` on a block of code:
// cppcheck-suppress-begin aaaa
...
// cppcheck-suppress-end aaaa
Suppressing multiple ids on a block of code:
// cppcheck-suppress-begin [aaaa, bbbb]
...
// cppcheck-suppress-end [aaaa, bbbb]
Suppressing warnings `aaaa` for a whole file:
// cppcheck-suppress-file aaaa
Suppressing multiple ids for a whole file:
// cppcheck-suppress-file [aaaa, bbbb]
### Comment before code or on same line ### Comment before code or on same line
The comment can be put before the code or at the same line as the code. The comment can be put before the code or at the same line as the code.

View File

@ -277,7 +277,7 @@ private:
#endif #endif
void runChecks(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { void runChecks(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) {
// check to make sure the appropriate error is present // check to make sure the appropriate errors are present
ASSERT_EQUALS(1, (this->*check)("void f() {\n" ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" int a;\n" " int a;\n"
" a++;\n" " a++;\n"
@ -285,6 +285,16 @@ private:
"")); ""));
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str());
ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" int a;\n"
" a++;\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n"
"[test.cpp:5]: (error) Uninitialized variable: b\n", errout.str());
// suppress uninitvar globally // suppress uninitvar globally
ASSERT_EQUALS(0, (this->*check)("void f() {\n" ASSERT_EQUALS(0, (this->*check)("void f() {\n"
" int a;\n" " int a;\n"
@ -293,6 +303,59 @@ private:
"uninitvar")); "uninitvar"));
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
(this->*check)("void f() {\n"
" // cppcheck-suppress-file uninitvar\n"
" int a;\n"
" a++;\n"
"}\n",
"");
ASSERT_EQUALS("[test.cpp:2]: (error) File suppression should be at the top of the file\n"
"[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str());
(this->*check)("void f() {\n"
" int a;\n"
" a++;\n"
"}\n"
"// cppcheck-suppress-file uninitvar\n",
"");
ASSERT_EQUALS("[test.cpp:5]: (error) File suppression should be at the top of the file\n"
"[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str());
ASSERT_EQUALS(0, (this->*check)("// cppcheck-suppress-file uninitvar\n"
"void f() {\n"
" int a;\n"
" a++;\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("", errout.str());
ASSERT_EQUALS(0, (this->*check)("/* Fake file description\n"
" * End\n"
" */\n"
"\n"
"// cppcheck-suppress-file uninitvar\n"
"\n"
"void f() {\n"
" int a;\n"
" a++;\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("", errout.str());
(this->*check)("// cppcheck-suppress-file uninitvar\n"
"void f() {\n"
" int a;\n"
" a++;\n"
" int b;\n"
" b++;\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
// suppress uninitvar globally, without error present // suppress uninitvar globally, without error present
ASSERT_EQUALS(0, (this->*check)("void f() {\n" ASSERT_EQUALS(0, (this->*check)("void f() {\n"
" int a;\n" " int a;\n"
@ -301,6 +364,14 @@ private:
"uninitvar")); "uninitvar"));
ASSERT_EQUALS("(information) Unmatched suppression: uninitvar\n", errout.str()); ASSERT_EQUALS("(information) Unmatched suppression: uninitvar\n", errout.str());
(this->*check)("// cppcheck-suppress-file uninitvar\n"
"void f() {\n"
" int a;\n"
" b++;\n"
"}\n",
"");
ASSERT_EQUALS("[test.cpp:1]: (information) Unmatched suppression: uninitvar\n", errout.str());
// suppress uninitvar for this file only // suppress uninitvar for this file only
ASSERT_EQUALS(0, (this->*check)("void f() {\n" ASSERT_EQUALS(0, (this->*check)("void f() {\n"
" int a;\n" " int a;\n"
@ -479,6 +550,222 @@ private:
""); "");
ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str()); ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str());
// suppress block inline checks
ASSERT_EQUALS(0, (this->*check)("void f() {\n"
" // cppcheck-suppress-begin uninitvar\n"
" int a;\n"
" a++;\n"
" int b;\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n",
""));
ASSERT_EQUALS("", errout.str());
ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" // cppcheck-suppress-begin uninitvar\n"
" int a;\n"
" a++;\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("[test.cpp:2]: (error) Suppress Begin: No matching end\n"
"[test.cpp:4]: (error) Uninitialized variable: a\n"
"[test.cpp:6]: (error) Uninitialized variable: b\n", errout.str());
ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" int a;\n"
" a++;\n"
" int b;\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n",
""));
ASSERT_EQUALS("[test.cpp:6]: (error) Suppress End: No matching begin\n"
"[test.cpp:3]: (error) Uninitialized variable: a\n"
"[test.cpp:5]: (error) Uninitialized variable: b\n", errout.str());
ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str());
ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str());
ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin[uninitvar]\n"
" a++;\n"
" // cppcheck-suppress-end[uninitvar]\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str());
ASSERT_EQUALS(1, (this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin [uninitvar]\n"
" a++;\n"
" // cppcheck-suppress-end [uninitvar]\n"
" int b;\n"
" b++;\n"
"}\n",
""));
ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: b\n", errout.str());
(this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" int b;\n"
" // cppcheck-suppress-begin uninitvar\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
(this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" // cppcheck-suppress-begin uninitvar\n"
" int b;\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
(this->*check)("void f() {\n"
" // cppcheck-suppress-begin [uninitvar]\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" int b;\n"
" // cppcheck-suppress-begin uninitvar\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
" // cppcheck-suppress-end [uninitvar]\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
(this->*check)("void f() {\n"
" // cppcheck-suppress-begin [uninitvar, syntaxError]\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" int b;\n"
" // cppcheck-suppress-begin uninitvar\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
" // cppcheck-suppress-end [uninitvar, syntaxError]\n"
"}\n",
"");
ASSERT_EQUALS("[test.cpp:2]: (information) Unmatched suppression: syntaxError\n", errout.str());
(this->*check)("// cppcheck-suppress-begin [uninitvar, syntaxError]\n"
"void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" int b;\n"
" // cppcheck-suppress-begin uninitvar\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n"
"// cppcheck-suppress-end [uninitvar, syntaxError]\n",
"");
ASSERT_EQUALS("[test.cpp:1]: (information) Unmatched suppression: syntaxError\n", errout.str());
(this->*check)("// cppcheck-suppress-begin [uninitvar, syntaxError]\n"
"void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
" int b;\n"
" // cppcheck-suppress-begin uninitvar\n"
" b++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n"
"// cppcheck-suppress-end [uninitvar, syntaxError]",
"");
ASSERT_EQUALS("[test.cpp:1]: (information) Unmatched suppression: syntaxError\n", errout.str());
// test of multiple suppression types
(this->*check)("// cppcheck-suppress-file uninitvar\n"
"void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" // cppcheck-suppress uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
(this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" // cppcheck-suppress uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
(this->*check)("// cppcheck-suppress-file uninitvar\n"
"void f() {\n"
" int a;\n"
" // cppcheck-suppress uninitvar\n"
" a++;\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
(this->*check)("// cppcheck-suppress-file uninitvar\n"
"void f() {\n"
" int a;\n"
" // cppcheck-suppress-begin uninitvar\n"
" a++;\n"
" // cppcheck-suppress-end uninitvar\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
(this->*check)("// cppcheck-suppress-file uninitvar\n"
"void f() {\n"
" // cppcheck-suppress uninitvar\n"
" int a;\n"
" a++;\n"
"}\n",
"");
ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str());
// #5746 - exitcode // #5746 - exitcode
ASSERT_EQUALS(1U, ASSERT_EQUALS(1U,
(this->*check)("int f() {\n" (this->*check)("int f() {\n"
@ -571,15 +858,42 @@ private:
void inlinesuppress() const { void inlinesuppress() const {
Suppressions::Suppression s; Suppressions::Suppression s;
std::string msg; std::string msg;
// Suppress without attribute
ASSERT_EQUALS(false, s.parseComment("/* some text */", &msg)); ASSERT_EQUALS(false, s.parseComment("/* some text */", &msg));
ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress */", &msg)); ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress */", &msg));
ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress-file */", &msg));
ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress-begin */", &msg));
ASSERT_EQUALS(false, s.parseComment("/* cppcheck-suppress-end */", &msg));
// Correct suppress
msg.clear(); msg.clear();
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id */", &msg)); ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id */", &msg));
ASSERT_EQUALS("", msg); ASSERT_EQUALS("", msg);
msg.clear();
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-file id */", &msg));
ASSERT_EQUALS("", msg);
msg.clear();
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-begin id */", &msg));
ASSERT_EQUALS("", msg);
msg.clear();
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-end id */", &msg));
ASSERT_EQUALS("", msg);
// Bad attribute construction
const std::string badSuppressionAttribute = "Bad suppression attribute 'some'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym";
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id some text */", &msg)); ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id some text */", &msg));
ASSERT_EQUALS("Bad suppression attribute 'some'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym", msg); ASSERT_EQUALS(badSuppressionAttribute, msg);
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-file id some text */", &msg));
ASSERT_EQUALS(badSuppressionAttribute, msg);
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-begin id some text */", &msg));
ASSERT_EQUALS(badSuppressionAttribute, msg);
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-end id some text */", &msg));
ASSERT_EQUALS(badSuppressionAttribute, msg);
} }
void inlinesuppress_symbolname() { void inlinesuppress_symbolname() {
@ -622,6 +936,48 @@ private:
ASSERT_EQUALS("", suppressions[0].symbolName); ASSERT_EQUALS("", suppressions[0].symbolName);
ASSERT_EQUALS("", errMsg); ASSERT_EQUALS("", errMsg);
errMsg = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-begin[errorId]", &errMsg);
ASSERT_EQUALS(1, suppressions.size());
ASSERT_EQUALS("errorId", suppressions[0].errorId);
ASSERT_EQUALS("", suppressions[0].symbolName);
ASSERT_EQUALS("", errMsg);
errMsg = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-begin [errorId]", &errMsg);
ASSERT_EQUALS(1, suppressions.size());
ASSERT_EQUALS("errorId", suppressions[0].errorId);
ASSERT_EQUALS("", suppressions[0].symbolName);
ASSERT_EQUALS("", errMsg);
errMsg = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-end[errorId]", &errMsg);
ASSERT_EQUALS(1, suppressions.size());
ASSERT_EQUALS("errorId", suppressions[0].errorId);
ASSERT_EQUALS("", suppressions[0].symbolName);
ASSERT_EQUALS("", errMsg);
errMsg = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-end [errorId]", &errMsg);
ASSERT_EQUALS(1, suppressions.size());
ASSERT_EQUALS("errorId", suppressions[0].errorId);
ASSERT_EQUALS("", suppressions[0].symbolName);
ASSERT_EQUALS("", errMsg);
errMsg = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-file[errorId]", &errMsg);
ASSERT_EQUALS(1, suppressions.size());
ASSERT_EQUALS("errorId", suppressions[0].errorId);
ASSERT_EQUALS("", suppressions[0].symbolName);
ASSERT_EQUALS("", errMsg);
errMsg = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress-file [errorId]", &errMsg);
ASSERT_EQUALS(1, suppressions.size());
ASSERT_EQUALS("errorId", suppressions[0].errorId);
ASSERT_EQUALS("", suppressions[0].symbolName);
ASSERT_EQUALS("", errMsg);
errMsg = ""; errMsg = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress[errorId symbolName=arr]", &errMsg); suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress[errorId symbolName=arr]", &errMsg);
ASSERT_EQUALS(1, suppressions.size()); ASSERT_EQUALS(1, suppressions.size());