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 {
struct BadInlineSuppression {
BadInlineSuppression(const simplecpp::Location &l, std::string msg) : location(l), errmsg(std::move(msg)) {}
simplecpp::Location location;
BadInlineSuppression(std::string file, const int line, std::string msg) : file(std::move(file)), line(line), errmsg(std::move(msg)) {}
std::string file;
int line;
std::string errmsg;
};
}
@ -97,18 +98,50 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std:
if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress)
return false;
// skip spaces after "cppcheck-suppress"
const std::string::size_type pos2 = comment.find_first_not_of(' ', pos1+cppchecksuppress.size());
// check if it has a prefix
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)
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] == '[') {
// multi suppress format
std::string 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())
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) {
return !s.errorId.empty();
@ -120,21 +153,30 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std:
if (!s.parseComment(comment, &errmsg))
return false;
s.type = errorType;
s.lineNumber = tok->location.line;
if (!s.errorId.empty())
inlineSuppressions.push_back(std::move(s));
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;
}
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) {
if (!tok->comment)
if (!tok->comment) {
onlyComments = false;
continue;
}
std::list<Suppressions::Suppression> inlineSuppressions;
if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad))
@ -142,18 +184,27 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett
if (!sameline(tok->previous, tok)) {
// find code after comment..
if (tok->next) {
tok = tok->next;
while (tok && tok->comment) {
while (tok->comment) {
parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad);
if (tok->next) {
tok = tok->next;
}
if (!tok)
} else {
break;
}
}
}
}
if (inlineSuppressions.empty())
continue;
// It should never happen
if (!tok)
continue;
// Relative filename
std::string relativeFilename(tok->location.file());
if (settings.relativePaths) {
@ -166,6 +217,42 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett
}
relativeFilename = Path::simplifyPath(relativeFilename);
// Add the suppressions.
for (Suppressions::Suppression &suppr : inlineSuppressions) {
suppr.fileName = relativeFilename;
if (Suppressions::Type::blockBegin == suppr.type)
{
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 &&
@ -175,14 +262,21 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett
tok->location.fileIndex == tok->next->location.fileIndex &&
tok->previous->str() == "{";
// Add the suppressions.
for (Suppressions::Suppression &suppr : inlineSuppressions) {
suppr.fileName = relativeFilename;
suppr.lineNumber = tok->location.line;
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)
@ -190,13 +284,13 @@ void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppre
if (!mSettings.inlineSuppressions)
return;
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) {
if (it->second)
::addinlineSuppressions(*it->second, mSettings, suppressions, err);
::addInlineSuppressions(*it->second, mSettings, suppressions, 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;
}
const std::string SymbolNameString = "symbolName=";
while (iss) {
std::string word;
iss >> word;
@ -172,8 +174,8 @@ std::vector<Suppressions::Suppression> Suppressions::parseMultiSuppressComment(c
break;
if (word.find_first_not_of("+-*/%#;") == std::string::npos)
break;
if (startsWith(word, "symbolName=")) {
s.symbolName = word.substr(11);
if (startsWith(word, SymbolNameString)) {
s.symbolName = word.substr(SymbolNameString.size());
} else {
if (errorMessage && errorMessage->empty())
*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)
comment.erase(comment.size() - 2, 2);
const std::string cppchecksuppress = "cppcheck-suppress";
std::istringstream iss(comment.substr(2));
std::string word;
iss >> word;
if (word != "cppcheck-suppress")
if (word.substr(0, cppchecksuppress.size()) != cppchecksuppress)
return false;
iss >> errorId;
if (!iss)
return false;
const std::string SymbolNameString = "symbolName=";
while (iss) {
iss >> word;
if (!iss)
break;
if (word.find_first_not_of("+-*/%#;") == std::string::npos)
break;
if (startsWith(word,"symbolName="))
symbolName = word.substr(11);
if (startsWith(word, SymbolNameString))
symbolName = word.substr(SymbolNameString.size());
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";
}
@ -332,10 +340,12 @@ bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &e
return false;
if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName()))
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)
return false;
}
if ((Suppressions::Type::block == type) && ((errmsg.lineNumber < lineBegin) || (errmsg.lineNumber > lineEnd)))
return false;
if (!symbolName.empty()) {
for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) {
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)
{
const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression");
bool return_value = false;
for (Suppression &s : mSuppressions) {
if (!global && !s.isLocal())
continue;
if (unmatchedSuppression && s.errorId != errmsg.errorId)
continue;
if (s.isMatch(errmsg))
return true;
return_value = true;
}
return false;
return return_value;
}
bool Suppressions::isSuppressed(const ::ErrorMessage &errmsg)
@ -470,9 +481,17 @@ void Suppressions::markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tok
currLineNr = tok->linenr();
currFileIdx = tok->fileIndex();
for (auto &suppression : mSuppressions) {
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;
}
}
}
}

View File

@ -41,6 +41,10 @@ enum class Certainty;
class CPPCHECKLIB Suppressions {
public:
enum class Type {
unique, file, block, blockBegin, blockEnd
};
struct CPPCHECKLIB ErrorMessage {
std::size_t hash;
std::string errorId;
@ -77,6 +81,26 @@ public:
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
* @param comment the full comment text
@ -107,6 +131,9 @@ public:
std::string errorId;
std::string fileName;
int lineNumber = NO_LINE;
int lineBegin = NO_LINE;
int lineEnd = NO_LINE;
Type type = Type::unique;
std::string symbolName;
std::size_t hash{};
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);
}
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)
{
return !str.empty() && str.back() == c;

View File

@ -531,6 +531,26 @@ Suppressing multiple ids in one comment by using []:
// 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
The comment can be put before the code or at the same line as the code.

View File

@ -277,7 +277,7 @@ private:
#endif
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"
" int a;\n"
" a++;\n"
@ -285,6 +285,16 @@ private:
""));
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
ASSERT_EQUALS(0, (this->*check)("void f() {\n"
" int a;\n"
@ -293,6 +303,59 @@ private:
"uninitvar"));
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
ASSERT_EQUALS(0, (this->*check)("void f() {\n"
" int a;\n"
@ -301,6 +364,14 @@ private:
"uninitvar"));
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
ASSERT_EQUALS(0, (this->*check)("void f() {\n"
" int a;\n"
@ -479,6 +550,222 @@ private:
"");
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
ASSERT_EQUALS(1U,
(this->*check)("int f() {\n"
@ -571,15 +858,42 @@ private:
void inlinesuppress() const {
Suppressions::Suppression s;
std::string msg;
// Suppress without attribute
ASSERT_EQUALS(false, s.parseComment("/* some text */", &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();
ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id */", &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("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() {
@ -622,6 +936,48 @@ private:
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-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 = "";
suppressions=Suppressions::parseMultiSuppressComment("// cppcheck-suppress[errorId symbolName=arr]", &errMsg);
ASSERT_EQUALS(1, suppressions.size());