|
26 | 26 | #include "token.h" |
27 | 27 | #include "tokenize.h" |
28 | 28 | #include "tokenlist.h" |
| 29 | +#include "settings.h" |
29 | 30 |
|
30 | 31 | #include <algorithm> |
31 | 32 | #include <cctype> // std::isdigit, std::isalnum, etc |
@@ -647,3 +648,230 @@ std::string SuppressionList::Suppression::toString() const |
647 | 648 | } |
648 | 649 | return s; |
649 | 650 | } |
| 651 | + |
| 652 | +polyspace::Parser::Parser(const Settings &settings) |
| 653 | +{ |
| 654 | + const bool haveMisraAddon = std::any_of(settings.addonInfos.cbegin(), |
| 655 | + settings.addonInfos.cend(), |
| 656 | + [] (const AddonInfo &info) { |
| 657 | + return info.name == "misra"; |
| 658 | + }); |
| 659 | + |
| 660 | + if (haveMisraAddon) { |
| 661 | + mFamilyMap["MISRA-C3"] = "misra-c2012-"; |
| 662 | + mFamilyMap["MISRA2012"] = "misra-c2012-"; |
| 663 | + } |
| 664 | + |
| 665 | + const auto matchArg = [&](const std::string &arg) { |
| 666 | + const std::string args = settings.premiumArgs; |
| 667 | + const std::string::size_type pos = args.find(arg); |
| 668 | + |
| 669 | + if (pos == std::string::npos) |
| 670 | + return false; |
| 671 | + |
| 672 | + const char prevChar = (pos > 0) ? args[pos - 1] : ' '; |
| 673 | + const char nextChar = (pos + arg.size() < args.size()) ? args[pos + arg.size()] : ' '; |
| 674 | + |
| 675 | + return prevChar == ' ' && (nextChar == ' ' || nextChar == ':'); |
| 676 | + }; |
| 677 | + |
| 678 | + if (matchArg("--misra-c-2012")) { |
| 679 | + mFamilyMap["MISRA-C3"] = "premium-misra-c-2012-"; |
| 680 | + mFamilyMap["MISRA2012"] = "premium-misra-c-2012-"; |
| 681 | + } |
| 682 | + |
| 683 | + if (matchArg("--misra-c-2023")) |
| 684 | + mFamilyMap["MISRA-C-2023"] = "premium-misra-c-2023-"; |
| 685 | + |
| 686 | + if (matchArg("--misra-cpp-2008") || matchArg("--misra-c++-2008")) |
| 687 | + mFamilyMap["MISRA-CPP"] = "premium-misra-cpp-2008-"; |
| 688 | + |
| 689 | + if (matchArg("--misra-cpp-2023") || matchArg("--misra-c++-2023")) |
| 690 | + mFamilyMap["MISRA-CPP-2023"] = "premium-misra-cpp-2023-"; |
| 691 | + |
| 692 | + if (matchArg("--cert-c") || matchArg("--cert-c-2016")) |
| 693 | + mFamilyMap["CERT-C"] = "premium-cert-c-"; |
| 694 | + |
| 695 | + if (matchArg("--cert-cpp") || matchArg("--cert-c++") || |
| 696 | + matchArg("--cert-cpp-2016") || matchArg("--cert-c++-2016")) |
| 697 | + mFamilyMap["CERT-CPP"] = "premium-cert-cpp-"; |
| 698 | + |
| 699 | + if (matchArg("--autosar")) |
| 700 | + mFamilyMap["AUTOSAR-CPP14"] = "premium-autosar-"; |
| 701 | +} |
| 702 | + |
| 703 | +polyspace::CommentKind polyspace::Parser::parseKind(const std::string& comment, std::string::size_type& pos) |
| 704 | +{ |
| 705 | + const std::string::size_type pos1 = pos; |
| 706 | + pos = comment.find_first_of(" \t", pos); |
| 707 | + if (pos >= comment.size()) |
| 708 | + return CommentKind::Invalid; |
| 709 | + |
| 710 | + const std::string token = comment.substr(pos1, pos-pos1); |
| 711 | + |
| 712 | + if (token == "polyspace") |
| 713 | + return CommentKind::Regular; |
| 714 | + |
| 715 | + if (token == "polyspace-begin") |
| 716 | + return CommentKind::Begin; |
| 717 | + |
| 718 | + if (token == "polyspace-end") |
| 719 | + return CommentKind::End; |
| 720 | + |
| 721 | + return CommentKind::Invalid; |
| 722 | +} |
| 723 | + |
| 724 | + |
| 725 | +std::list<SuppressionList::Suppression> polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename) |
| 726 | +{ |
| 727 | + std::list<SuppressionList::Suppression> ret; |
| 728 | + |
| 729 | + if (mFamilyMap.empty()) |
| 730 | + return ret; |
| 731 | + |
| 732 | + for (std::string::size_type pos = comment.find_first_not_of("/* "); pos < comment.size();) { |
| 733 | + // polyspace |
| 734 | + const auto polyspaceKind = parseKind(comment, pos); |
| 735 | + if (polyspaceKind == CommentKind::Invalid) |
| 736 | + break; |
| 737 | + |
| 738 | + // optional range |
| 739 | + pos = comment.find_first_not_of(" \t", pos); |
| 740 | + if (pos >= comment.size()) |
| 741 | + break; |
| 742 | + int rangeValue = 0; |
| 743 | + if (comment[pos] == '+') { |
| 744 | + const std::string::size_type posRangeStart = pos + 1; |
| 745 | + pos = comment.find_first_of(" \t", posRangeStart); |
| 746 | + if (pos > comment.size()) |
| 747 | + break; |
| 748 | + const std::string range = comment.substr(posRangeStart, pos-posRangeStart); |
| 749 | + // TODO check that range is an integer |
| 750 | + rangeValue = std::stoi(range); |
| 751 | + } |
| 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 | + pos = comment.find("polyspace", pos + 1); |
| 775 | + } |
| 776 | + |
| 777 | + for (const std::string& errorId: ids) { |
| 778 | + SuppressionList::Suppression suppr; |
| 779 | + suppr.errorId = errorId; |
| 780 | + suppr.isInline = true; |
| 781 | + suppr.isPolyspace = true; |
| 782 | + suppr.fileName = filename; |
| 783 | + suppr.lineNumber = line; |
| 784 | + suppr.extraComment = extraComment; |
| 785 | + |
| 786 | + if (rangeValue > 0) { |
| 787 | + suppr.type = SuppressionList::Type::block; |
| 788 | + suppr.lineBegin = line; |
| 789 | + suppr.lineEnd = line + rangeValue; |
| 790 | + } |
| 791 | + else if (polyspaceKind == polyspace::CommentKind::Regular) |
| 792 | + suppr.type = SuppressionList::Type::unique; |
| 793 | + else if (polyspaceKind == polyspace::CommentKind::Begin) |
| 794 | + suppr.type = SuppressionList::Type::blockBegin; |
| 795 | + else |
| 796 | + suppr.type = SuppressionList::Type::blockEnd; |
| 797 | + |
| 798 | + ret.emplace_back(suppr); |
| 799 | + } |
| 800 | + } |
| 801 | + |
| 802 | + return ret; |
| 803 | +} |
| 804 | + |
| 805 | +std::vector<std::pair<std::string, std::string>> polyspace::Parser::parseFamilyRules(const std::string& comment, std::string::size_type& pos) { |
| 806 | + std::vector<std::pair<std::string, std::string>> fr; |
| 807 | + std::string family; |
| 808 | + std::string rule; |
| 809 | + enum class State: uint8_t { family, colon, rule, rule_or_family } state = State::family; |
| 810 | + const std::string::size_type endpos = startsWith(comment, "/*") ? comment.size() - 2 : comment.size(); |
| 811 | + for (; pos <= endpos; ++pos) { |
| 812 | + const char c = comment[pos]; |
| 813 | + if (std::strchr("[\"", c)) |
| 814 | + break; |
| 815 | + switch (state) { |
| 816 | + case State::family: |
| 817 | + if (std::isalnum(c) || std::strchr("-_.",c)) |
| 818 | + family += c; |
| 819 | + else if (!family.empty() && std::strchr(" \t:",c)) |
| 820 | + state = State::colon; |
| 821 | + break; |
| 822 | + case State::colon: |
| 823 | + if (!std::strchr(" \t:", c)) { |
| 824 | + rule.clear(); |
| 825 | + --pos; |
| 826 | + state = State::rule; |
| 827 | + } |
| 828 | + break; |
| 829 | + case State::rule: |
| 830 | + if (std::strchr(", \t",c)) { |
| 831 | + if (!rule.empty()) { |
| 832 | + fr.emplace_back(family,rule); |
| 833 | + rule.clear(); |
| 834 | + if (c != ',') |
| 835 | + state = State::rule_or_family; |
| 836 | + } |
| 837 | + } |
| 838 | + else |
| 839 | + rule += c; |
| 840 | + break; |
| 841 | + case State::rule_or_family: |
| 842 | + rule.clear(); |
| 843 | + if (std::isalnum(c)) { |
| 844 | + --pos; |
| 845 | + family.clear(); |
| 846 | + state = State::family; |
| 847 | + } else if (c == ',') { |
| 848 | + --pos; |
| 849 | + state = State::rule; |
| 850 | + } |
| 851 | + break; |
| 852 | + } |
| 853 | + } |
| 854 | + if (!family.empty() && !rule.empty()) |
| 855 | + fr.emplace_back(family,rule); |
| 856 | + return fr; |
| 857 | +} |
| 858 | + |
| 859 | +std::set<std::string> polyspace::Parser::parseIds(const std::string& comment, std::string::size_type& pos) const { |
| 860 | + std::set<std::string> ids; |
| 861 | + for (const auto& fr: parseFamilyRules(comment,pos)) { |
| 862 | + const auto it = mFamilyMap.find(fr.first); |
| 863 | + if (it != mFamilyMap.cend()) |
| 864 | + ids.emplace(it->second + fr.second); |
| 865 | + } |
| 866 | + return ids; |
| 867 | +} |
| 868 | + |
| 869 | + |
| 870 | +bool polyspace::isPolyspaceComment(const std::string &comment) |
| 871 | +{ |
| 872 | + const std::string polyspace = "polyspace"; |
| 873 | + const std::string::size_type pos = comment.find_first_not_of("/* "); |
| 874 | + if (pos == std::string::npos) |
| 875 | + return false; |
| 876 | + return comment.compare(pos, polyspace.size(), polyspace, 0, polyspace.size()) == 0; |
| 877 | +} |
0 commit comments