Skip to content

Commit d4dbe7a

Browse files
committed
Support polyspace suppressions
1 parent 2905857 commit d4dbe7a

3 files changed

Lines changed: 302 additions & 0 deletions

File tree

lib/preprocessor.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,19 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett
200200

201201
bool onlyComments = true;
202202

203+
polyspace::Parser polyspaceParser;
204+
203205
for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
204206
if (!tok->comment) {
205207
onlyComments = false;
206208
continue;
207209
}
208210

211+
if (polyspace::isPolyspaceComment(tok->str())) {
212+
polyspaceParser.parse(tok->str(), tok->location.line, tokens.file(tok->location));
213+
continue;
214+
}
215+
209216
std::list<SuppressionList::Suppression> inlineSuppressions;
210217
if (!parseInlineSuppressionCommentToken(tokens, tok, inlineSuppressions, bad))
211218
continue;
@@ -310,6 +317,8 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett
310317
for (const SuppressionList::Suppression & suppr: inlineSuppressionsBlockBegin)
311318
// cppcheck-suppress useStlAlgorithm
312319
bad.emplace_back(suppr.fileName, suppr.lineNumber, 0, "Suppress Begin: No matching end"); // TODO: set column
320+
321+
polyspaceParser.collect(suppressions);
313322
}
314323

315324
void Preprocessor::inlineSuppressions(SuppressionList &suppressions)

lib/suppressions.cpp

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,3 +647,251 @@ std::string SuppressionList::Suppression::toString() const
647647
}
648648
return s;
649649
}
650+
651+
std::string polyspace::Parser::peekToken()
652+
{
653+
if (!mHasPeeked) {
654+
mPeeked = nextToken();
655+
mHasPeeked = true;
656+
}
657+
return mPeeked;
658+
}
659+
660+
std::string polyspace::Parser::nextToken()
661+
{
662+
if (mHasPeeked) {
663+
mHasPeeked = false;
664+
return mPeeked;
665+
}
666+
667+
if (mComment.compare(0, 2, "/*") == 0 || mComment.compare(0, 2, "//") == 0)
668+
mComment = mComment.substr(2);
669+
670+
std::string::size_type pos = 0;
671+
while (mComment[pos] == ' ') {
672+
pos++;
673+
if (pos == mComment.size()) {
674+
mComment = "";
675+
return "";
676+
}
677+
}
678+
679+
if (mComment.compare(0, 2, "*/") == 0) {
680+
mComment = "";
681+
return "";
682+
}
683+
684+
if (mComment[pos] == ':') {
685+
mComment = mComment.substr(pos + 1);
686+
return ":";
687+
}
688+
689+
if (mComment[pos] == ',') {
690+
mComment = mComment.substr(pos + 1);
691+
return ",";
692+
}
693+
694+
const char *stopChars;
695+
std::string::size_type skip;
696+
switch (mComment[pos]) {
697+
case '\"':
698+
stopChars = "\"";
699+
skip = 1;
700+
break;
701+
case '[':
702+
stopChars = "]";
703+
skip = 1;
704+
break;
705+
default:
706+
stopChars = " :,";
707+
skip = 0;
708+
break;
709+
}
710+
711+
const std::string::size_type start = pos;
712+
pos += skip;
713+
714+
if (pos == mComment.size()) {
715+
mComment = "";
716+
return "";
717+
}
718+
719+
while (std::strchr(stopChars, mComment[pos]) == nullptr) {
720+
pos++;
721+
if (pos == mComment.size())
722+
break;
723+
}
724+
725+
if (pos == mComment.size())
726+
skip = 0;
727+
728+
const std::string token = mComment.substr(start, pos - start + skip);
729+
mComment = mComment.substr(pos + skip);
730+
731+
return token;
732+
}
733+
734+
void polyspace::Parser::finishSuppression()
735+
{
736+
Suppression suppr = { mFamily, mResultName, mFilename, 0, 0 };
737+
738+
switch (mKind) {
739+
case CommentKind::Regular:
740+
{
741+
suppr.lineBegin = mLine;
742+
suppr.lineEnd = mLine + mRange;
743+
mDone.push_back(suppr);
744+
return;
745+
}
746+
case CommentKind::Begin:
747+
{
748+
suppr.lineBegin = mLine;
749+
mStarted.push_back(suppr);
750+
return;
751+
}
752+
case CommentKind::End:
753+
{
754+
auto it = std::find_if(
755+
mStarted.begin(),
756+
mStarted.end(),
757+
[&] (const Suppression &other) {
758+
return suppr.matches(other);
759+
}
760+
);
761+
762+
if (it == mStarted.end())
763+
return;
764+
765+
suppr.lineBegin = it->lineBegin;
766+
suppr.lineEnd = mLine;
767+
mStarted.erase(it);
768+
mDone.push_back(suppr);
769+
return;
770+
}
771+
}
772+
}
773+
774+
bool polyspace::Parser::parseEntry()
775+
{
776+
mFamily = nextToken();
777+
if (mFamily.empty())
778+
return false;
779+
780+
if (nextToken() != ":")
781+
return false;
782+
783+
// Parse result name, multiple names may be separated by commas
784+
while (!mComment.empty()) {
785+
mResultName = nextToken();
786+
if (mResultName.empty())
787+
return false;
788+
789+
finishSuppression();
790+
791+
if (peekToken() == ",") {
792+
(void) nextToken();
793+
continue;
794+
}
795+
796+
break;
797+
}
798+
799+
// Skip status and severity
800+
if (!peekToken().empty() && mPeeked[0] == '[')
801+
(void) nextToken();
802+
803+
return true;
804+
}
805+
806+
void polyspace::Parser::collect(SuppressionList &suppressions) const
807+
{
808+
for (const auto &polyspaceSuppr : mDone) {
809+
SuppressionList::Suppression suppr;
810+
if (polyspaceSuppr.convert(suppr))
811+
suppressions.addSuppression(std::move(suppr));
812+
}
813+
}
814+
815+
void polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename)
816+
{
817+
mComment = comment;
818+
mLine = line;
819+
mFilename = filename;
820+
mHasPeeked = false;
821+
822+
while (true) {
823+
const std::string kindStr = nextToken();
824+
if (kindStr.empty())
825+
return;
826+
827+
if (kindStr == "polyspace") mKind = CommentKind::Regular;
828+
else if (kindStr == "polyspace-begin") mKind = CommentKind::Begin;
829+
else if (kindStr == "polyspace-end") mKind = CommentKind::End;
830+
else return;
831+
832+
mRange = 0;
833+
if (peekToken()[0] == '+') {
834+
try { mRange = std::stoi(mPeeked.substr(1)); } catch (...) { return; }
835+
(void) nextToken();
836+
}
837+
838+
while (parseEntry()) {
839+
if (peekToken().empty() || mPeeked[0] == '\"')
840+
break;
841+
}
842+
843+
if (!peekToken().empty() && mPeeked[0] == '\"') {
844+
(void) nextToken();
845+
if (peekToken().empty())
846+
return;
847+
continue;
848+
}
849+
850+
break;
851+
}
852+
}
853+
854+
bool polyspace::isPolyspaceComment(const std::string &comment)
855+
{
856+
const std::string polyspace = "polyspace";
857+
const std::string::size_type pos = comment.find_first_not_of("/* ");
858+
return comment.compare(pos, polyspace.size(), polyspace, 0, polyspace.size()) == 0;
859+
}
860+
861+
bool polyspace::Suppression::matches(const polyspace::Suppression &other) const
862+
{
863+
return family == other.family && resultName == other.resultName;
864+
}
865+
866+
bool polyspace::Suppression::convert(SuppressionList::Suppression &suppr) const
867+
{
868+
static const std::map<std::string, std::string> map = {
869+
{ "MISRA-C3", "premium-misra-c-2012-" },
870+
{ "MISRA2012", "premium-misra-c-2012-" },
871+
{ "MISRA-C-2023", "premium-misra-c-2023-" },
872+
{ "MISRA-CPP", "premium-misra-cpp-2008-" },
873+
{ "MISRA-CPP-2023", "premium-misra-cpp-2023-" },
874+
{ "CERT-C", "premium-cert-c-" },
875+
{ "CERT-CPP", "premium-cert-cpp-" },
876+
{ "AUTOSAR-CPP14", "premium-autosar-" },
877+
};
878+
879+
const auto it = map.find(family);
880+
if (it == map.cend())
881+
return false;
882+
883+
suppr.errorId = it->second + resultName;
884+
suppr.isInline = true;
885+
suppr.fileName = filename;
886+
887+
suppr.lineNumber = lineBegin;
888+
if (lineBegin == lineEnd) {
889+
suppr.type = SuppressionList::Type::unique;
890+
} else {
891+
suppr.type = SuppressionList::Type::block;
892+
suppr.lineBegin = lineBegin;
893+
suppr.lineEnd = lineEnd;
894+
}
895+
896+
return true;
897+
}

lib/suppressions.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,51 @@ struct Suppressions
294294
SuppressionList nofail;
295295
};
296296

297+
namespace polyspace {
298+
299+
struct Suppression {
300+
std::string family;
301+
std::string resultName;
302+
std::string filename;
303+
int lineBegin;
304+
int lineEnd;
305+
306+
bool matches(const Suppression &other) const;
307+
bool convert(SuppressionList::Suppression &suppr) const;
308+
};
309+
310+
class Parser {
311+
public:
312+
void collect(SuppressionList &suppressions) const;
313+
void parse(const std::string &comment, int line, const std::string &filename);
314+
315+
private:
316+
std::string peekToken();
317+
std::string nextToken();
318+
void finishSuppression();
319+
bool parseEntry();
320+
321+
enum class CommentKind {
322+
Regular, Begin, End,
323+
};
324+
325+
std::list<Suppression> mStarted;
326+
std::list<Suppression> mDone;
327+
std::string mComment;
328+
std::string mFilename;
329+
int mLine{};
330+
int mRange{};
331+
CommentKind mKind{};
332+
std::string mFamily;
333+
std::string mResultName;
334+
std::string mPeeked;
335+
bool mHasPeeked{};
336+
};
337+
338+
bool isPolyspaceComment(const std::string &comment);
339+
340+
}
341+
297342
/// @}
298343
//---------------------------------------------------------------------------
299344
#endif // suppressionsH

0 commit comments

Comments
 (0)