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 &
632642std::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 += " \n symbol= " + symbolName ;
656+ if (isPolyspace)
657+ s += " \n polyspace=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