Skip to content
Open
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ $(libcppdir)/standards.o: lib/standards.cpp externals/simplecpp/simplecpp.h lib/
$(libcppdir)/summaries.o: lib/summaries.cpp lib/addoninfo.h lib/analyzerinfo.h lib/checkers.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/summaries.cpp

$(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/smallvector.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h
$(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/checkers.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/smallvector.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/suppressions.cpp

$(libcppdir)/templatesimplifier.o: lib/templatesimplifier.cpp lib/addoninfo.h lib/checkers.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h
Expand Down
6 changes: 5 additions & 1 deletion cli/cppcheckexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,11 @@ static bool reportUnmatchedSuppressions(const std::list<SuppressionList::Suppres
if (!s.fileName.empty()) {
callStack.emplace_back(s.fileName, s.lineNumber, 0);
}
errorLogger.reportErr(::ErrorMessage(std::move(callStack), "", Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal));
if (s.isPolyspace) {
errorLogger.reportErr(::ErrorMessage(std::move(callStack), "", Severity::information, "Unmatched polyspace suppression: " + s.errorId, "unmatchedPolyspaceSuppression", Certainty::normal));
} else {
errorLogger.reportErr(::ErrorMessage(std::move(callStack), "", Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal));
}
err = true;
}
return err;
Expand Down
9 changes: 9 additions & 0 deletions lib/preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,19 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett

bool onlyComments = true;

polyspace::Parser polyspaceParser(settings);

for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
if (!tok->comment) {
onlyComments = false;
continue;
}

if (polyspace::isPolyspaceComment(tok->str())) {
polyspaceParser.parse(tok->str(), tok->location.line, tokens.file(tok->location));
continue;
}

std::list<SuppressionList::Suppression> inlineSuppressions;
if (!parseInlineSuppressionCommentToken(tokens, tok, inlineSuppressions, bad))
continue;
Expand Down Expand Up @@ -310,6 +317,8 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett
for (const SuppressionList::Suppression & suppr: inlineSuppressionsBlockBegin)
// cppcheck-suppress useStlAlgorithm
bad.emplace_back(suppr.fileName, suppr.lineNumber, 0, "Suppress Begin: No matching end"); // TODO: set column

polyspaceParser.collect(suppressions);
}

void Preprocessor::inlineSuppressions(SuppressionList &suppressions)
Expand Down
262 changes: 262 additions & 0 deletions lib/suppressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "token.h"
#include "tokenize.h"
#include "tokenlist.h"
#include "settings.h"

#include <algorithm>
#include <cctype> // std::isdigit, std::isalnum, etc
Expand Down Expand Up @@ -647,3 +648,264 @@ std::string SuppressionList::Suppression::toString() const
}
return s;
}

std::string polyspace::Parser::peekToken()
{
if (!mHasPeeked) {
mPeeked = nextToken();
mHasPeeked = true;
}
return mPeeked;
}

std::string polyspace::Parser::nextToken()
{
if (mHasPeeked) {
mHasPeeked = false;
return mPeeked;
}

if (mComment.compare(0, 2, "/*") == 0 || mComment.compare(0, 2, "//") == 0)
mComment = mComment.substr(2);

std::string::size_type pos = 0;
while (mComment[pos] == ' ') {
pos++;
if (pos == mComment.size()) {
mComment = "";
return "";
}
}

if (mComment.compare(0, 2, "*/") == 0) {
mComment = "";
return "";
}

if (mComment[pos] == ':') {
mComment = mComment.substr(pos + 1);
return ":";
}

if (mComment[pos] == ',') {
mComment = mComment.substr(pos + 1);
return ",";
}

const char *stopChars;
std::string::size_type skip;
switch (mComment[pos]) {
case '\"':
stopChars = "\"";
skip = 1;
break;
case '[':
stopChars = "]";
skip = 1;
break;
default:
stopChars = " :,";
skip = 0;
break;
}

const std::string::size_type start = pos;
pos += skip;

if (pos == mComment.size()) {
mComment = "";
return "";
}

while (std::strchr(stopChars, mComment[pos]) == nullptr) {
pos++;
if (pos == mComment.size())
break;
}

if (pos == mComment.size())
skip = 0;

const std::string token = mComment.substr(start, pos - start + skip);
mComment = mComment.substr(pos + skip);

return token;
}

void polyspace::Parser::finishSuppression()
{
Suppression suppr = { mFamily, mResultName, mFilename, 0, 0 };

switch (mKind) {
case CommentKind::Regular:
{
suppr.lineBegin = mLine;
suppr.lineEnd = mLine + mRange;
mDone.push_back(suppr);
return;
}
case CommentKind::Begin:
{
suppr.lineBegin = mLine;
mStarted.push_back(suppr);
return;
}
case CommentKind::End:
{
auto it = std::find_if(
mStarted.begin(),
mStarted.end(),
[&] (const Suppression &other) {
return suppr.matches(other);
}
);

if (it == mStarted.end())
return;

suppr.lineBegin = it->lineBegin;
suppr.lineEnd = mLine;
mStarted.erase(it);
mDone.push_back(suppr);
return;
}
}
}

bool polyspace::Parser::parseEntry()
{
mFamily = nextToken();
if (mFamily.empty())
return false;

if (nextToken() != ":")
return false;

// Parse result name, multiple names may be separated by commas
while (!mComment.empty()) {
mResultName = nextToken();
if (mResultName.empty())
return false;

finishSuppression();

if (peekToken() == ",") {
(void) nextToken();
continue;
}

break;
}

// Skip status and severity
if (!peekToken().empty() && mPeeked[0] == '[')
(void) nextToken();

return true;
}

void polyspace::Parser::collect(SuppressionList &suppressions) const
{
for (const auto &polyspaceSuppr : mDone) {
SuppressionList::Suppression suppr;
if (polyspaceSuppr.convert(mSettings, suppr))
suppressions.addSuppression(std::move(suppr));
}
}

void polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename)
{
mComment = comment;
mLine = line;
mFilename = filename;
mHasPeeked = false;

while (true) {
const std::string kindStr = nextToken();
if (kindStr.empty())
return;

if (kindStr == "polyspace") mKind = CommentKind::Regular;
else if (kindStr == "polyspace-begin") mKind = CommentKind::Begin;
else if (kindStr == "polyspace-end") mKind = CommentKind::End;
else return;

mRange = 0;
if (peekToken()[0] == '+') {
try { mRange = std::stoi(mPeeked.substr(1)); } catch (...) { return; }
(void) nextToken();
}

while (parseEntry()) {
if (peekToken().empty() || mPeeked[0] == '\"')
break;
}

if (!peekToken().empty() && mPeeked[0] == '\"') {
(void) nextToken();
if (peekToken().empty())
return;
continue;
}

break;
}
}

bool polyspace::isPolyspaceComment(const std::string &comment)
{
const std::string polyspace = "polyspace";
const std::string::size_type pos = comment.find_first_not_of("/* ");
if (pos == std::string::npos)
return false;
return comment.compare(pos, polyspace.size(), polyspace, 0, polyspace.size()) == 0;
}

bool polyspace::Suppression::matches(const polyspace::Suppression &other) const
{
return family == other.family && resultName == other.resultName;
}

bool polyspace::Suppression::convert(const Settings &settings, SuppressionList::Suppression &suppr) const
{
static const std::map<std::string, std::string> map = {
{ "MISRA-C-2023", "premium-misra-c-2023-" },
{ "MISRA-CPP", "premium-misra-cpp-2008-" },
{ "MISRA-CPP-2023", "premium-misra-cpp-2023-" },
{ "CERT-C", "premium-cert-c-" },
{ "CERT-CPP", "premium-cert-cpp-" },
{ "AUTOSAR-CPP14", "premium-autosar-" },
};

const auto it = map.find(family);
std::string prefix;
if (it == map.cend()) {
if (family == "MISRA-C3" || family == "MISRA2012") {
if (settings.premiumArgs.empty()) {
prefix = "misra-c2012-";
} else {
prefix = "premium-misra-c-2012-";
}
} else {
return false;
}
} else {
prefix = it->second;
}

suppr.errorId = prefix + resultName;
suppr.isInline = true;
suppr.isPolyspace = true;
suppr.fileName = filename;

suppr.lineNumber = lineBegin;
if (lineBegin == lineEnd) {
suppr.type = SuppressionList::Type::unique;
} else {
suppr.type = SuppressionList::Type::block;
suppr.lineBegin = lineBegin;
suppr.lineEnd = lineEnd;
}

return true;
}
50 changes: 50 additions & 0 deletions lib/suppressions.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Tokenizer;
class ErrorMessage;
enum class Certainty : std::uint8_t;
class FileWithDetails;
class Settings;

/// @addtogroup Core
/// @{
Expand Down Expand Up @@ -160,6 +161,7 @@ class CPPCHECKLIB SuppressionList {
bool matched{}; /** This suppression was fully matched in an isSuppressed() call */
bool checked{}; /** This suppression applied to code which was being analyzed but did not match the error in an isSuppressed() call */
bool isInline{};
bool isPolyspace{};

enum : std::int8_t { NO_LINE = -1 };
};
Expand Down Expand Up @@ -294,6 +296,54 @@ struct Suppressions
SuppressionList nofail;
};

namespace polyspace {

struct CPPCHECKLIB Suppression {
std::string family;
std::string resultName;
std::string filename;
int lineBegin;
int lineEnd;

bool matches(const Suppression &other) const;
bool convert(const Settings &settings, SuppressionList::Suppression &suppr) const;
};

class CPPCHECKLIB Parser {
public:
Parser() = delete;
explicit Parser(const Settings &settings) : mSettings(settings) {}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the constructor could look at the settings and check if any misra rules are enabled. If no misra rules are enabled it would make sense if collect and parse will not do anything.

void collect(SuppressionList &suppressions) const;
void parse(const std::string &comment, int line, const std::string &filename);

private:
std::string peekToken();
std::string nextToken();
void finishSuppression();
bool parseEntry();

enum class CommentKind : std::uint8_t {
Regular, Begin, End,
};

std::list<Suppression> mStarted;
std::list<Suppression> mDone;
std::string mComment;
std::string mFilename;
int mLine{};
int mRange{};
CommentKind mKind{};
std::string mFamily;
std::string mResultName;
std::string mPeeked;
bool mHasPeeked{};
const Settings &mSettings;
};

bool CPPCHECKLIB isPolyspaceComment(const std::string &comment);

}

/// @}
//---------------------------------------------------------------------------
#endif // suppressionsH
Loading
Loading