|
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,333 @@ 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 auto 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 | + if (pos > 0 && args[pos - 1] != ' ') |
| 673 | + return false; |
| 674 | + |
| 675 | + return pos == args.size() - arg.size() || args[pos + arg.size()] == ' '; |
| 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 | +std::string polyspace::Parser::peekToken() |
| 704 | +{ |
| 705 | + if (!mHasPeeked) { |
| 706 | + mPeeked = nextToken(); |
| 707 | + mHasPeeked = true; |
| 708 | + } |
| 709 | + return mPeeked; |
| 710 | +} |
| 711 | + |
| 712 | +std::string polyspace::Parser::nextToken() |
| 713 | +{ |
| 714 | + if (mHasPeeked) { |
| 715 | + mHasPeeked = false; |
| 716 | + return mPeeked; |
| 717 | + } |
| 718 | + |
| 719 | + std::string::size_type pos = 0; |
| 720 | + while (mComment[pos] == ' ') { |
| 721 | + pos++; |
| 722 | + if (pos == mComment.size()) { |
| 723 | + mComment = ""; |
| 724 | + return ""; |
| 725 | + } |
| 726 | + } |
| 727 | + |
| 728 | + if (mComment.compare(0, 2, "*/") == 0) { |
| 729 | + mComment = ""; |
| 730 | + return ""; |
| 731 | + } |
| 732 | + |
| 733 | + if (mComment[pos] == ':') { |
| 734 | + mComment = mComment.substr(pos + 1); |
| 735 | + return ":"; |
| 736 | + } |
| 737 | + |
| 738 | + if (mComment[pos] == ',') { |
| 739 | + mComment = mComment.substr(pos + 1); |
| 740 | + return ","; |
| 741 | + } |
| 742 | + |
| 743 | + const char *stopChars; |
| 744 | + std::string::size_type skip; |
| 745 | + switch (mComment[pos]) { |
| 746 | + case '\"': |
| 747 | + stopChars = "\""; |
| 748 | + skip = 1; |
| 749 | + break; |
| 750 | + case '[': |
| 751 | + stopChars = "]"; |
| 752 | + skip = 1; |
| 753 | + break; |
| 754 | + default: |
| 755 | + stopChars = " :,"; |
| 756 | + skip = 0; |
| 757 | + break; |
| 758 | + } |
| 759 | + |
| 760 | + const std::string::size_type start = pos; |
| 761 | + pos += skip; |
| 762 | + |
| 763 | + if (pos == mComment.size()) { |
| 764 | + mComment = ""; |
| 765 | + return ""; |
| 766 | + } |
| 767 | + |
| 768 | + while (std::strchr(stopChars, mComment[pos]) == nullptr) { |
| 769 | + pos++; |
| 770 | + if (pos == mComment.size()) |
| 771 | + break; |
| 772 | + } |
| 773 | + |
| 774 | + if (pos == mComment.size()) |
| 775 | + skip = 0; |
| 776 | + |
| 777 | + const std::string token = mComment.substr(start, pos - start + skip); |
| 778 | + mComment = mComment.substr(pos + skip); |
| 779 | + |
| 780 | + return token; |
| 781 | +} |
| 782 | + |
| 783 | +bool polyspace::Parser::parseAnnotation(polyspace::Annotation &annotation) |
| 784 | +{ |
| 785 | + annotation.family = nextToken(); |
| 786 | + annotation.resultNames.clear(); |
| 787 | + annotation.extraComment = ""; |
| 788 | + |
| 789 | + if (annotation.family.empty()) |
| 790 | + return false; |
| 791 | + |
| 792 | + if (nextToken() != ":") |
| 793 | + return false; |
| 794 | + |
| 795 | + for (;;) { |
| 796 | + const std::string resultName = nextToken(); |
| 797 | + |
| 798 | + if (resultName.empty()) |
| 799 | + return false; |
| 800 | + |
| 801 | + annotation.resultNames.push_back(resultName); |
| 802 | + |
| 803 | + if (peekToken().substr(0, 1) == ",") { |
| 804 | + (void) nextToken(); |
| 805 | + continue; |
| 806 | + } |
| 807 | + |
| 808 | + break; |
| 809 | + } |
| 810 | + |
| 811 | + if (peekToken().substr(0, 1) == "[") |
| 812 | + (void) nextToken(); |
| 813 | + |
| 814 | + if (peekToken().substr(0, 1) == "\"") { |
| 815 | + std::string extraComment = nextToken().substr(1); |
| 816 | + |
| 817 | + if (extraComment.size() > 1) |
| 818 | + extraComment.pop_back(); |
| 819 | + |
| 820 | + annotation.extraComment = extraComment; |
| 821 | + } |
| 822 | + |
| 823 | + return true; |
| 824 | +} |
| 825 | + |
| 826 | +polyspace::CommentKind polyspace::Parser::parseKind() |
| 827 | +{ |
| 828 | + const std::string token = nextToken(); |
| 829 | + |
| 830 | + if (token == "polyspace") |
| 831 | + return CommentKind::Regular; |
| 832 | + |
| 833 | + if (token == "polyspace-begin") |
| 834 | + return CommentKind::Begin; |
| 835 | + |
| 836 | + if (token == "polyspace-end") |
| 837 | + return CommentKind::End; |
| 838 | + |
| 839 | + return CommentKind::Invalid; |
| 840 | +} |
| 841 | + |
| 842 | +int polyspace::Parser::parseRange() |
| 843 | +{ |
| 844 | + if (peekToken()[0] == '+') { |
| 845 | + try { |
| 846 | + const int range = std::stoi(peekToken().substr(1)); |
| 847 | + (void) nextToken(); |
| 848 | + return range; |
| 849 | + } catch (...) { |
| 850 | + return -1; |
| 851 | + } |
| 852 | + } |
| 853 | + |
| 854 | + return 0; |
| 855 | +} |
| 856 | + |
| 857 | +void polyspace::Parser::handleAnnotation(const polyspace::Annotation &annotation) |
| 858 | +{ |
| 859 | + for (const auto &resultName : annotation.resultNames) { |
| 860 | + Suppression suppr = { |
| 861 | + annotation.family, |
| 862 | + resultName, |
| 863 | + annotation.filename, |
| 864 | + annotation.extraComment, |
| 865 | + 0, |
| 866 | + 0, |
| 867 | + }; |
| 868 | + |
| 869 | + switch (annotation.kind) { |
| 870 | + case CommentKind::Regular: |
| 871 | + { |
| 872 | + suppr.lineBegin = annotation.line; |
| 873 | + suppr.lineEnd = annotation.line + annotation.range; |
| 874 | + mDone.push_back(suppr); |
| 875 | + break; |
| 876 | + } |
| 877 | + case CommentKind::Begin: |
| 878 | + { |
| 879 | + suppr.lineBegin = annotation.line; |
| 880 | + mStarted.push_back(suppr); |
| 881 | + break; |
| 882 | + } |
| 883 | + case CommentKind::End: |
| 884 | + { |
| 885 | + auto it = std::find_if( |
| 886 | + mStarted.begin(), |
| 887 | + mStarted.end(), |
| 888 | + [&] (const Suppression &other) { |
| 889 | + return suppr.matches(other); |
| 890 | + } |
| 891 | + ); |
| 892 | + |
| 893 | + if (it == mStarted.end()) |
| 894 | + break; |
| 895 | + |
| 896 | + suppr.lineBegin = it->lineBegin; |
| 897 | + suppr.lineEnd = annotation.line; |
| 898 | + mStarted.erase(it); |
| 899 | + mDone.push_back(suppr); |
| 900 | + break; |
| 901 | + } |
| 902 | + case CommentKind::Invalid: |
| 903 | + { |
| 904 | + assert(false); // Invalid comments are not handled |
| 905 | + } |
| 906 | + } |
| 907 | + } |
| 908 | +} |
| 909 | + |
| 910 | +void polyspace::Parser::collect(SuppressionList &suppressions) const |
| 911 | +{ |
| 912 | + for (const auto &polyspaceSuppr : mDone) { |
| 913 | + const auto it = mFamilyMap.find(polyspaceSuppr.family); |
| 914 | + if (it == mFamilyMap.cend()) |
| 915 | + continue; |
| 916 | + |
| 917 | + SuppressionList::Suppression suppr; |
| 918 | + suppr.errorId = it->second + polyspaceSuppr.resultName; |
| 919 | + suppr.isInline = true; |
| 920 | + suppr.isPolyspace = true; |
| 921 | + suppr.fileName = polyspaceSuppr.filename; |
| 922 | + suppr.extraComment = polyspaceSuppr.extraComment; |
| 923 | + |
| 924 | + suppr.lineNumber = polyspaceSuppr.lineBegin; |
| 925 | + if (polyspaceSuppr.lineBegin == polyspaceSuppr.lineEnd) { |
| 926 | + suppr.type = SuppressionList::Type::unique; |
| 927 | + } else { |
| 928 | + suppr.type = SuppressionList::Type::block; |
| 929 | + suppr.lineBegin = polyspaceSuppr.lineBegin; |
| 930 | + suppr.lineEnd = polyspaceSuppr.lineEnd; |
| 931 | + } |
| 932 | + |
| 933 | + suppressions.addSuppression(std::move(suppr)); |
| 934 | + } |
| 935 | +} |
| 936 | + |
| 937 | +void polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename) |
| 938 | +{ |
| 939 | + if (mFamilyMap.empty()) |
| 940 | + return; |
| 941 | + |
| 942 | + mComment = comment.substr(2); |
| 943 | + mHasPeeked = false; |
| 944 | + |
| 945 | + while (true) { |
| 946 | + const CommentKind kind = parseKind(); |
| 947 | + if (kind == CommentKind::Invalid) |
| 948 | + return; |
| 949 | + |
| 950 | + const int range = parseRange(); |
| 951 | + if (range < 0) |
| 952 | + return; |
| 953 | + |
| 954 | + Annotation annotation; |
| 955 | + annotation.filename = filename; |
| 956 | + annotation.kind = kind; |
| 957 | + annotation.line = line; |
| 958 | + annotation.range = range; |
| 959 | + |
| 960 | + while (parseAnnotation(annotation)) { |
| 961 | + handleAnnotation(annotation); |
| 962 | + if (!annotation.extraComment.empty()) |
| 963 | + break; |
| 964 | + } |
| 965 | + } |
| 966 | +} |
| 967 | + |
| 968 | +bool polyspace::isPolyspaceComment(const std::string &comment) |
| 969 | +{ |
| 970 | + const std::string polyspace = "polyspace"; |
| 971 | + const std::string::size_type pos = comment.find_first_not_of("/* "); |
| 972 | + if (pos == std::string::npos) |
| 973 | + return false; |
| 974 | + return comment.compare(pos, polyspace.size(), polyspace, 0, polyspace.size()) == 0; |
| 975 | +} |
| 976 | + |
| 977 | +bool polyspace::Suppression::matches(const polyspace::Suppression &other) const |
| 978 | +{ |
| 979 | + return family == other.family && resultName == other.resultName; |
| 980 | +} |
0 commit comments