Skip to content

Commit 380cf9a

Browse files
authored
Fix #14367: Support polyspace inline suppressions for misra c:20xx rules (#8169)
1 parent a4bd260 commit 380cf9a

File tree

8 files changed

+662
-23
lines changed

8 files changed

+662
-23
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ $(libcppdir)/standards.o: lib/standards.cpp externals/simplecpp/simplecpp.h lib/
652652
$(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
653653
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/summaries.cpp
654654

655-
$(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
655+
$(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
656656
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/suppressions.cpp
657657

658658
$(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

cli/cppcheckexecutor.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ static bool reportUnmatchedSuppressions(const std::list<SuppressionList::Suppres
329329
if (!s.fileName.empty()) {
330330
callStack.emplace_back(s.fileName, s.lineNumber, 0);
331331
}
332-
errorLogger.reportErr(::ErrorMessage(std::move(callStack), "", Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal));
332+
const std::string unmatchedSuppressionId = s.isPolyspace ? "unmatchedPolyspaceSuppression" : "unmatchedSuppression";
333+
errorLogger.reportErr(::ErrorMessage(std::move(callStack), "", Severity::information, "Unmatched suppression: " + s.errorId, unmatchedSuppressionId, Certainty::normal));
333334
err = true;
334335
}
335336
return err;

lib/preprocessor.cpp

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,27 +200,33 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett
200200

201201
bool onlyComments = true;
202202

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

209211
std::list<SuppressionList::Suppression> inlineSuppressions;
210-
if (!parseInlineSuppressionCommentToken(tokens, tok, inlineSuppressions, bad))
211-
continue;
212+
if (polyspace::isPolyspaceComment(tok->str())) {
213+
inlineSuppressions = polyspaceParser.parse(tok->str(), tok->location.line, getRelativeFilename(tokens, tok, settings));
214+
} else {
215+
if (!parseInlineSuppressionCommentToken(tokens, tok, inlineSuppressions, bad))
216+
continue;
212217

213-
if (!sameline(tok->previous, tok)) {
214-
// find code after comment..
215-
if (tok->next) {
216-
tok = tok->next;
218+
if (!sameline(tok->previous, tok)) {
219+
// find code after comment..
220+
if (tok->next) {
221+
tok = tok->next;
217222

218-
while (tok->comment) {
219-
parseInlineSuppressionCommentToken(tokens, tok, inlineSuppressions, bad);
220-
if (tok->next) {
221-
tok = tok->next;
222-
} else {
223-
break;
223+
while (tok->comment) {
224+
parseInlineSuppressionCommentToken(tokens, tok, inlineSuppressions, bad);
225+
if (tok->next) {
226+
tok = tok->next;
227+
} else {
228+
break;
229+
}
224230
}
225231
}
226232
}
@@ -249,8 +255,9 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett
249255
for (SuppressionList::Suppression &suppr : inlineSuppressions) {
250256
suppr.fileName = relativeFilename;
251257

252-
if (SuppressionList::Type::blockBegin == suppr.type)
253-
{
258+
if (SuppressionList::Type::block == suppr.type) {
259+
suppressions.addSuppression(std::move(suppr));
260+
} else if (SuppressionList::Type::blockBegin == suppr.type) {
254261
inlineSuppressionsBlockBegin.push_back(std::move(suppr));
255262
} else if (SuppressionList::Type::blockEnd == suppr.type) {
256263
bool throwError = true;

lib/suppressions.cpp

Lines changed: 256 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "token.h"
2727
#include "tokenize.h"
2828
#include "tokenlist.h"
29+
#include "settings.h"
2930

3031
#include <algorithm>
3132
#include <cctype> // std::isdigit, std::isalnum, etc
@@ -244,6 +245,15 @@ SuppressionList::Suppression SuppressionList::parseLine(const std::string &line)
244245
suppression.fileName.erase(pos);
245246
}
246247
}
248+
249+
// when parsing string generated internally by toString() there can be newline
250+
std::string extra;
251+
while (std::getline(lineStream, extra)) {
252+
if (startsWith(extra, "symbol="))
253+
suppression.symbolName = extra.substr(7);
254+
else if (extra == "polyspace=1")
255+
suppression.isPolyspace = true;
256+
}
247257
}
248258
}
249259

@@ -632,7 +642,7 @@ void SuppressionList::markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &
632642
std::string SuppressionList::Suppression::toString() const
633643
{
634644
std::string s;
635-
s+= errorId;
645+
s += errorId;
636646
if (!fileName.empty()) {
637647
s += ':';
638648
s += fileName;
@@ -641,9 +651,250 @@ std::string SuppressionList::Suppression::toString() const
641651
s += std::to_string(lineNumber);
642652
}
643653
}
644-
if (!symbolName.empty()) {
645-
s += ':';
646-
s += symbolName;
647-
}
654+
if (!symbolName.empty())
655+
s += "\nsymbol=" + symbolName;
656+
if (isPolyspace)
657+
s += "\npolyspace=1";
648658
return s;
649659
}
660+
661+
polyspace::Parser::Parser(const Settings &settings)
662+
{
663+
const bool haveMisraAddon = std::any_of(settings.addonInfos.cbegin(),
664+
settings.addonInfos.cend(),
665+
[] (const AddonInfo &info) {
666+
return info.name == "misra";
667+
});
668+
669+
if (haveMisraAddon) {
670+
mFamilyMap["MISRA-C3"] = "misra-c2012-";
671+
mFamilyMap["MISRA2012"] = "misra-c2012-";
672+
}
673+
674+
const auto matchArg = [&](const std::string &arg) {
675+
const std::string args = settings.premiumArgs;
676+
const std::string::size_type pos = args.find(arg);
677+
678+
if (pos == std::string::npos)
679+
return false;
680+
681+
const char prevChar = (pos > 0) ? args[pos - 1] : ' ';
682+
const char nextChar = (pos + arg.size() < args.size()) ? args[pos + arg.size()] : ' ';
683+
684+
return prevChar == ' ' && (nextChar == ' ' || nextChar == ':');
685+
};
686+
687+
if (matchArg("--misra-c-2012")) {
688+
mFamilyMap["MISRA-C3"] = "premium-misra-c-2012-";
689+
mFamilyMap["MISRA2012"] = "premium-misra-c-2012-";
690+
}
691+
692+
if (matchArg("--misra-c-2023"))
693+
mFamilyMap["MISRA-C-2023"] = "premium-misra-c-2023-";
694+
695+
if (matchArg("--misra-cpp-2008") || matchArg("--misra-c++-2008"))
696+
mFamilyMap["MISRA-CPP"] = "premium-misra-cpp-2008-";
697+
698+
if (matchArg("--misra-cpp-2023") || matchArg("--misra-c++-2023"))
699+
mFamilyMap["MISRA-CPP-2023"] = "premium-misra-cpp-2023-";
700+
701+
if (matchArg("--cert-c") || matchArg("--cert-c-2016"))
702+
mFamilyMap["CERT-C"] = "premium-cert-c-";
703+
704+
if (matchArg("--cert-cpp") || matchArg("--cert-c++") ||
705+
matchArg("--cert-cpp-2016") || matchArg("--cert-c++-2016"))
706+
mFamilyMap["CERT-CPP"] = "premium-cert-cpp-";
707+
708+
if (matchArg("--autosar"))
709+
mFamilyMap["AUTOSAR-CPP14"] = "premium-autosar-";
710+
}
711+
712+
polyspace::CommentKind polyspace::Parser::parseKind(const std::string& comment, std::string::size_type& pos)
713+
{
714+
const std::string::size_type pos1 = pos;
715+
pos = comment.find_first_of(" \t", pos);
716+
if (pos >= comment.size())
717+
return CommentKind::Invalid;
718+
719+
const std::string token = comment.substr(pos1, pos-pos1);
720+
721+
if (token == "polyspace")
722+
return CommentKind::Regular;
723+
724+
if (token == "polyspace-begin")
725+
return CommentKind::Begin;
726+
727+
if (token == "polyspace-end")
728+
return CommentKind::End;
729+
730+
return CommentKind::Invalid;
731+
}
732+
733+
734+
std::list<SuppressionList::Suppression> polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename) const
735+
{
736+
// Syntax for a polyspace suppression:
737+
// https://se.mathworks.com/help/bugfinder/ug/annotate-hide-known-acceptable-polyspace-results-web-browser.html
738+
739+
std::list<SuppressionList::Suppression> ret;
740+
741+
if (mFamilyMap.empty())
742+
return ret;
743+
744+
for (std::string::size_type pos = comment.find_first_not_of("/* "); pos < comment.size();) {
745+
// polyspace
746+
const auto polyspaceKind = parseKind(comment, pos);
747+
if (polyspaceKind == CommentKind::Invalid)
748+
break;
749+
750+
// optional range
751+
const int rangeValue = parseRange(comment, pos);
752+
753+
// ids..
754+
const std::set<std::string> ids = parseIds(comment, pos);
755+
756+
// skip justification
757+
if (pos < comment.size() && comment[pos] == '[') {
758+
pos = comment.find(']',pos+1);
759+
if (pos >= comment.size())
760+
break;
761+
pos = comment.find_first_not_of(" \t", pos+1);
762+
if (pos >= comment.size())
763+
break;
764+
}
765+
766+
// extra comment
767+
std::string extraComment;
768+
if (pos < comment.size() && comment[pos] == '\"') {
769+
const std::string::size_type p1 = pos + 1;
770+
pos = comment.find('\"',p1);
771+
if (pos >= comment.size())
772+
break;
773+
extraComment = comment.substr(p1, pos-p1);
774+
}
775+
776+
for (const std::string& errorId: ids) {
777+
SuppressionList::Suppression suppr;
778+
suppr.errorId = errorId;
779+
suppr.isInline = true;
780+
suppr.isPolyspace = true;
781+
suppr.fileName = filename;
782+
suppr.lineNumber = line;
783+
suppr.extraComment = extraComment;
784+
785+
if (rangeValue > 0) {
786+
suppr.type = SuppressionList::Type::block;
787+
suppr.lineBegin = line;
788+
suppr.lineEnd = line + rangeValue;
789+
}
790+
else if (polyspaceKind == polyspace::CommentKind::Regular)
791+
suppr.type = SuppressionList::Type::unique;
792+
else if (polyspaceKind == polyspace::CommentKind::Begin)
793+
suppr.type = SuppressionList::Type::blockBegin;
794+
else
795+
suppr.type = SuppressionList::Type::blockEnd;
796+
797+
ret.emplace_back(suppr);
798+
}
799+
800+
// proceed to next "polyspace" if it exists
801+
if (pos < comment.size())
802+
pos = comment.find("polyspace", pos);
803+
}
804+
805+
return ret;
806+
}
807+
808+
809+
int polyspace::Parser::parseRange(const std::string& comment, std::string::size_type& pos) {
810+
pos = comment.find_first_not_of(" \t", pos);
811+
if (pos >= comment.size())
812+
return 0;
813+
if (comment[pos] != '+')
814+
return 0;
815+
const std::string::size_type startpos = pos + 1;
816+
std::string::size_type endpos = comment.find_first_of(" \t", startpos);
817+
if (endpos > comment.size())
818+
return 0;
819+
const std::string range = comment.substr(startpos, endpos-startpos);
820+
try {
821+
int ret = std::stoi(range);
822+
pos = endpos;
823+
return ret;
824+
} catch (const std::invalid_argument &) {}
825+
return 0;
826+
}
827+
828+
std::vector<std::pair<std::string, std::string>> polyspace::Parser::parseFamilyRules(const std::string& comment, std::string::size_type& pos) {
829+
std::vector<std::pair<std::string, std::string>> fr;
830+
std::string family;
831+
std::string rule;
832+
enum class State: uint8_t { family, colon, rule, rule_or_family } state = State::family;
833+
const std::string::size_type endpos = startsWith(comment, "/*") ? comment.size() - 2 : comment.size();
834+
for (; pos <= endpos; ++pos) {
835+
const char c = comment[pos];
836+
if (std::strchr("[\"", c))
837+
break;
838+
switch (state) {
839+
case State::family:
840+
if (std::isalnum(c) || std::strchr("-_.",c))
841+
family += c;
842+
else if (!family.empty() && std::strchr(" \t:",c))
843+
state = State::colon;
844+
break;
845+
case State::colon:
846+
if (!std::strchr(" \t:", c)) {
847+
rule.clear();
848+
--pos;
849+
state = State::rule;
850+
}
851+
break;
852+
case State::rule:
853+
if (std::strchr(", \t",c)) {
854+
if (!rule.empty()) {
855+
fr.emplace_back(family,rule);
856+
rule.clear();
857+
if (c != ',')
858+
state = State::rule_or_family;
859+
}
860+
}
861+
else
862+
rule += c;
863+
break;
864+
case State::rule_or_family:
865+
rule.clear();
866+
if (std::isalnum(c)) {
867+
--pos;
868+
family.clear();
869+
state = State::family;
870+
} else if (c == ',') {
871+
--pos;
872+
state = State::rule;
873+
}
874+
break;
875+
}
876+
}
877+
if (!family.empty() && !rule.empty())
878+
fr.emplace_back(family,rule);
879+
return fr;
880+
}
881+
882+
std::set<std::string> polyspace::Parser::parseIds(const std::string& comment, std::string::size_type& pos) const {
883+
std::set<std::string> ids;
884+
for (const auto& fr: parseFamilyRules(comment,pos)) {
885+
const auto it = mFamilyMap.find(fr.first);
886+
if (it != mFamilyMap.cend())
887+
ids.emplace(it->second + fr.second);
888+
}
889+
return ids;
890+
}
891+
892+
893+
bool polyspace::isPolyspaceComment(const std::string &comment)
894+
{
895+
const std::string polyspace = "polyspace";
896+
const std::string::size_type pos = comment.find_first_not_of("/* ");
897+
if (pos == std::string::npos)
898+
return false;
899+
return comment.compare(pos, polyspace.size(), polyspace, 0, polyspace.size()) == 0;
900+
}

0 commit comments

Comments
 (0)