-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix #14367: Support polyspace inline suppressions for misra c:20xx rules #8105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
aa31b8b
65c5275
3eb2c11
7682da1
af3fb82
be6d9dc
e33033a
0d70722
b45c618
cd884fc
2850299
56de4d2
e7afafd
9826d2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -647,3 +648,333 @@ std::string SuppressionList::Suppression::toString() const | |
| } | ||
| return s; | ||
| } | ||
|
|
||
| polyspace::Parser::Parser(const Settings &settings) | ||
| { | ||
| const auto haveMisraAddon = std::any_of(settings.addonInfos.cbegin(), | ||
| settings.addonInfos.cend(), | ||
| [] (const AddonInfo &info) { | ||
| return info.name == "misra"; | ||
| }); | ||
|
|
||
| if (haveMisraAddon) { | ||
| mFamilyMap["MISRA-C3"] = "misra-c2012-"; | ||
| mFamilyMap["MISRA2012"] = "misra-c2012-"; | ||
| } | ||
|
|
||
| const auto matchArg = [&](const std::string &arg) { | ||
| const std::string args = settings.premiumArgs; | ||
| const std::string::size_type pos = args.find(arg); | ||
|
|
||
| if (pos == std::string::npos) | ||
| return false; | ||
|
|
||
| if (pos > 0 && args[pos - 1] != ' ') | ||
| return false; | ||
|
|
||
| return pos == args.size() - arg.size() || args[pos + arg.size()] == ' '; | ||
| }; | ||
|
|
||
| if (matchArg("--misra-c-2012")) { | ||
| mFamilyMap["MISRA-C3"] = "premium-misra-c-2012-"; | ||
| mFamilyMap["MISRA2012"] = "premium-misra-c-2012-"; | ||
| } | ||
|
|
||
| if (matchArg("--misra-c-2023")) | ||
danmar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| mFamilyMap["MISRA-C-2023"] = "premium-misra-c-2023-"; | ||
|
|
||
| if (matchArg("--misra-cpp-2008") || matchArg("--misra-c++-2008")) | ||
| mFamilyMap["MISRA-CPP"] = "premium-misra-cpp-2008-"; | ||
|
|
||
| if (matchArg("--misra-cpp-2023") || matchArg("--misra-c++-2023")) | ||
| mFamilyMap["MISRA-CPP-2023"] = "premium-misra-cpp-2023-"; | ||
|
|
||
| if (matchArg("--cert-c") || matchArg("--cert-c-2016")) | ||
| mFamilyMap["CERT-C"] = "premium-cert-c-"; | ||
|
|
||
| if (matchArg("--cert-cpp") || matchArg("--cert-c++") || | ||
| matchArg("--cert-cpp-2016") || matchArg("--cert-c++-2016")) | ||
| mFamilyMap["CERT-CPP"] = "premium-cert-cpp-"; | ||
|
|
||
| if (matchArg("--autosar")) | ||
| mFamilyMap["AUTOSAR-CPP14"] = "premium-autosar-"; | ||
| } | ||
|
|
||
| std::string polyspace::Parser::peekToken() | ||
| { | ||
| if (!mHasPeeked) { | ||
| mPeeked = nextToken(); | ||
| mHasPeeked = true; | ||
| } | ||
| return mPeeked; | ||
| } | ||
|
|
||
| std::string polyspace::Parser::nextToken() | ||
| { | ||
| if (mHasPeeked) { | ||
| mHasPeeked = false; | ||
| return mPeeked; | ||
| } | ||
|
|
||
| std::string::size_type pos = 0; | ||
ludviggunne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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; | ||
| } | ||
|
|
||
| bool polyspace::Parser::parseAnnotation(polyspace::Annotation &annotation) | ||
| { | ||
| annotation.family = nextToken(); | ||
| annotation.resultNames.clear(); | ||
| annotation.extraComment = ""; | ||
|
|
||
| if (annotation.family.empty()) | ||
| return false; | ||
|
|
||
| if (nextToken() != ":") | ||
| return false; | ||
|
|
||
| for (;;) { | ||
| const std::string resultName = nextToken(); | ||
|
|
||
| if (resultName.empty()) | ||
| return false; | ||
|
|
||
| annotation.resultNames.push_back(resultName); | ||
|
|
||
| if (peekToken().substr(0, 1) == ",") { | ||
| (void) nextToken(); | ||
| continue; | ||
| } | ||
|
|
||
| break; | ||
| } | ||
|
|
||
| if (peekToken().substr(0, 1) == "[") | ||
| (void) nextToken(); | ||
|
|
||
| if (peekToken().substr(0, 1) == "\"") { | ||
| std::string extraComment = nextToken().substr(1); | ||
|
|
||
| if (extraComment.size() > 1) | ||
| extraComment.pop_back(); | ||
|
|
||
| annotation.extraComment = extraComment; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| polyspace::CommentKind polyspace::Parser::parseKind() | ||
| { | ||
| const std::string token = nextToken(); | ||
|
|
||
| if (token == "polyspace") | ||
| return CommentKind::Regular; | ||
|
|
||
| if (token == "polyspace-begin") | ||
| return CommentKind::Begin; | ||
|
|
||
| if (token == "polyspace-end") | ||
| return CommentKind::End; | ||
|
|
||
| return CommentKind::Invalid; | ||
| } | ||
|
|
||
| int polyspace::Parser::parseRange() | ||
| { | ||
| if (peekToken()[0] == '+') { | ||
| try { | ||
| const int range = std::stoi(peekToken().substr(1)); | ||
| (void) nextToken(); | ||
| return range; | ||
| } catch (...) { | ||
| return -1; | ||
| } | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| void polyspace::Parser::handleAnnotation(const polyspace::Annotation &annotation) | ||
| { | ||
| for (const auto &resultName : annotation.resultNames) { | ||
| Suppression suppr = { | ||
| annotation.family, | ||
| resultName, | ||
| annotation.filename, | ||
| annotation.extraComment, | ||
| 0, | ||
| 0, | ||
| }; | ||
|
|
||
| switch (annotation.kind) { | ||
| case CommentKind::Regular: | ||
| { | ||
| suppr.lineBegin = annotation.line; | ||
| suppr.lineEnd = annotation.line + annotation.range; | ||
| mDone.push_back(suppr); | ||
| break; | ||
| } | ||
| case CommentKind::Begin: | ||
| { | ||
| suppr.lineBegin = annotation.line; | ||
| mStarted.push_back(suppr); | ||
| break; | ||
| } | ||
| case CommentKind::End: | ||
| { | ||
| auto it = std::find_if( | ||
| mStarted.begin(), | ||
| mStarted.end(), | ||
| [&] (const Suppression &other) { | ||
| return suppr.matches(other); | ||
| } | ||
| ); | ||
|
|
||
| if (it == mStarted.end()) | ||
| break; | ||
|
|
||
| suppr.lineBegin = it->lineBegin; | ||
| suppr.lineEnd = annotation.line; | ||
| mStarted.erase(it); | ||
| mDone.push_back(suppr); | ||
| break; | ||
| } | ||
| case CommentKind::Invalid: | ||
| { | ||
| assert(false); // Invalid comments are not handled | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void polyspace::Parser::collect(SuppressionList &suppressions) const | ||
| { | ||
| for (const auto &polyspaceSuppr : mDone) { | ||
| const auto it = mFamilyMap.find(polyspaceSuppr.family); | ||
| if (it == mFamilyMap.cend()) | ||
| continue; | ||
|
|
||
| SuppressionList::Suppression suppr; | ||
| suppr.errorId = it->second + polyspaceSuppr.resultName; | ||
| suppr.isInline = true; | ||
| suppr.isPolyspace = true; | ||
| suppr.fileName = polyspaceSuppr.filename; | ||
| suppr.extraComment = polyspaceSuppr.extraComment; | ||
|
|
||
| suppr.lineNumber = polyspaceSuppr.lineBegin; | ||
| if (polyspaceSuppr.lineBegin == polyspaceSuppr.lineEnd) { | ||
| suppr.type = SuppressionList::Type::unique; | ||
| } else { | ||
| suppr.type = SuppressionList::Type::block; | ||
| suppr.lineBegin = polyspaceSuppr.lineBegin; | ||
| suppr.lineEnd = polyspaceSuppr.lineEnd; | ||
| } | ||
|
|
||
| suppressions.addSuppression(std::move(suppr)); | ||
| } | ||
| } | ||
|
|
||
| void polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename) | ||
| { | ||
| if (mFamilyMap.empty()) | ||
| return; | ||
|
|
||
| mComment = comment.substr(2); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My spontanous feeling about this parsing is that looks cludgy to me. mComment is assigned here in parse and tokens are grabbed from it in the nextToken(). I have the feeling I would prefer that we manipulate comment locally on the stack instead.. which makes the interface reentrant however reentrancy is not really my ultimate reason why I want this.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I don't see the benefit of this. |
||
| mHasPeeked = false; | ||
|
|
||
| while (true) { | ||
| const CommentKind kind = parseKind(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this handle multiple polyspace suppressions in a comment? can't there only be one suppression in a comment?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There can be multiple: e7afafd
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if (kind == CommentKind::Invalid) | ||
| return; | ||
|
|
||
| const int range = parseRange(); | ||
| if (range < 0) | ||
| return; | ||
|
|
||
| Annotation annotation; | ||
| annotation.filename = filename; | ||
| annotation.kind = kind; | ||
| annotation.line = line; | ||
| annotation.range = range; | ||
|
|
||
| while (parseAnnotation(annotation)) { | ||
| handleAnnotation(annotation); | ||
| if (!annotation.extraComment.empty()) | ||
| 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; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what exact handling we have here.. but I have the feeling we might want to reuse the same handling for cppcheck/polyspace inline suppressions so isn't it a good idea to add polyspace suppressions in
inlineSuppressionsalso? For instance the filename used for cppcheck inline suppressions why don't we want to use the same for polyspace suppressions. There is handling for "file/begin"/"end" which could be useful for polyspace also I think..There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Polyspace suppressions comments work quite differently from cppcheck suppression comments. So there isn't really any shared logic. But good point about the filename, I'll fix that.