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:
parent
8ef4da475a
commit
44ab976451
|
@ -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..
|
||||
tok = tok->next;
|
||||
while (tok && tok->comment) {
|
||||
parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad);
|
||||
if (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())
|
||||
continue;
|
||||
|
||||
// It should never happen
|
||||
if (!tok)
|
||||
continue;
|
||||
|
||||
// Relative filename
|
||||
std::string relativeFilename(tok->location.file());
|
||||
if (settings.relativePaths) {
|
||||
|
@ -166,23 +217,66 @@ static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Sett
|
|||
}
|
||||
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.
|
||||
for (Suppressions::Suppression &suppr : inlineSuppressions) {
|
||||
suppr.fileName = relativeFilename;
|
||||
suppr.lineNumber = tok->location.line;
|
||||
suppr.thisAndNextLine = thisAndNextLine;
|
||||
suppressions.addSuppression(std::move(suppr));
|
||||
|
||||
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 &&
|
||||
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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,7 +481,15 @@ void Suppressions::markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tok
|
|||
currLineNr = tok->linenr();
|
||||
currFileIdx = tok->fileIndex();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue